Merge "Add new flag for capturing power button feature." into main
diff --git a/core/api/current.txt b/core/api/current.txt
index aa6615b..94481b6 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -20277,6 +20277,7 @@
method public long getDynamicRangeProfile();
method public int getMaxSharedSurfaceCount();
method public int getMirrorMode();
+ method @FlaggedApi("com.android.internal.camera.flags.mirror_mode_shared_surfaces") public int getMirrorMode(@NonNull android.view.Surface);
method public long getStreamUseCase();
method @Nullable public android.view.Surface getSurface();
method public int getSurfaceGroupId();
@@ -20287,6 +20288,7 @@
method public void removeSurface(@NonNull android.view.Surface);
method public void setDynamicRangeProfile(long);
method public void setMirrorMode(int);
+ method @FlaggedApi("com.android.internal.camera.flags.mirror_mode_shared_surfaces") public void setMirrorMode(@NonNull android.view.Surface, int);
method public void setPhysicalCameraId(@Nullable String);
method public void setReadoutTimestampEnabled(boolean);
method public void setStreamUseCase(long);
@@ -32852,6 +32854,7 @@
public static class Build.VERSION_CODES {
ctor public Build.VERSION_CODES();
+ field @FlaggedApi("android.sdk.major_minor_versioning_scheme") public static final int BAKLAVA = 10000; // 0x2710
field public static final int BASE = 1; // 0x1
field public static final int BASE_1_1 = 2; // 0x2
field public static final int CUPCAKE = 3; // 0x3
@@ -32891,6 +32894,7 @@
}
@FlaggedApi("android.sdk.major_minor_versioning_scheme") public static class Build.VERSION_CODES_FULL {
+ field public static final int BAKLAVA = 1000000000; // 0x3b9aca00
field public static final int BASE = 100000; // 0x186a0
field public static final int BASE_1_1 = 200000; // 0x30d40
field public static final int CUPCAKE = 300000; // 0x493e0
@@ -55037,6 +55041,7 @@
field public static final int CONTENT_CHANGE_TYPE_DRAG_STARTED = 128; // 0x80
field public static final int CONTENT_CHANGE_TYPE_ENABLED = 4096; // 0x1000
field public static final int CONTENT_CHANGE_TYPE_ERROR = 2048; // 0x800
+ field @FlaggedApi("android.view.accessibility.a11y_expansion_state_api") public static final int CONTENT_CHANGE_TYPE_EXPANDED = 16384; // 0x4000
field public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 16; // 0x10
field public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 32; // 0x20
field public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 8; // 0x8
@@ -55186,6 +55191,7 @@
method public CharSequence getContentDescription();
method public int getDrawingOrder();
method public CharSequence getError();
+ method @FlaggedApi("android.view.accessibility.a11y_expansion_state_api") public int getExpandedState();
method @Nullable public android.view.accessibility.AccessibilityNodeInfo.ExtraRenderingInfo getExtraRenderingInfo();
method public android.os.Bundle getExtras();
method public CharSequence getHintText();
@@ -55278,6 +55284,7 @@
method public void setEditable(boolean);
method public void setEnabled(boolean);
method public void setError(CharSequence);
+ method @FlaggedApi("android.view.accessibility.a11y_expansion_state_api") public void setExpandedState(int);
method public void setFocusable(boolean);
method public void setFocused(boolean);
method @FlaggedApi("android.view.accessibility.granular_scrolling") public void setGranularScrollingSupported(boolean);
@@ -55364,6 +55371,10 @@
field @FlaggedApi("android.view.accessibility.tri_state_checked") public static final int CHECKED_STATE_PARTIAL = 2; // 0x2
field @FlaggedApi("android.view.accessibility.tri_state_checked") public static final int CHECKED_STATE_TRUE = 1; // 0x1
field @NonNull public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityNodeInfo> CREATOR;
+ field @FlaggedApi("android.view.accessibility.a11y_expansion_state_api") public static final int EXPANDED_STATE_COLLAPSED = 1; // 0x1
+ field @FlaggedApi("android.view.accessibility.a11y_expansion_state_api") public static final int EXPANDED_STATE_FULL = 3; // 0x3
+ field @FlaggedApi("android.view.accessibility.a11y_expansion_state_api") public static final int EXPANDED_STATE_PARTIAL = 2; // 0x2
+ field @FlaggedApi("android.view.accessibility.a11y_expansion_state_api") public static final int EXPANDED_STATE_UNDEFINED = 0; // 0x0
field public static final String EXTRA_DATA_RENDERING_INFO_KEY = "android.view.accessibility.extra.DATA_RENDERING_INFO_KEY";
field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
field public static final int EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH = 20000; // 0x4e20
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 1fdb698..d1a9e67 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -209,6 +209,8 @@
field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
field public static final String MANAGE_GAME_ACTIVITY = "android.permission.MANAGE_GAME_ACTIVITY";
field public static final String MANAGE_GAME_MODE = "android.permission.MANAGE_GAME_MODE";
+ field @FlaggedApi("android.media.tv.flags.media_quality_fw") public static final String MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE = "android.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE";
+ field @FlaggedApi("android.media.tv.flags.media_quality_fw") public static final String MANAGE_GLOBAL_SOUND_QUALITY_SERVICE = "android.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE";
field public static final String MANAGE_HOTWORD_DETECTION = "android.permission.MANAGE_HOTWORD_DETECTION";
field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
field public static final String MANAGE_LOW_POWER_STANDBY = "android.permission.MANAGE_LOW_POWER_STANDBY";
@@ -4637,7 +4639,7 @@
@FlaggedApi("android.content.pm.verification_service") public final class VerificationSession implements android.os.Parcelable {
method public int describeContents();
- method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public long extendTimeRemaining(long);
+ method public long extendTimeRemaining(long);
method @NonNull public java.util.List<android.content.pm.SharedLibraryInfo> getDeclaredLibraries();
method @NonNull public android.os.PersistableBundle getExtensionParams();
method public int getId();
@@ -4645,12 +4647,12 @@
method @NonNull public String getPackageName();
method @NonNull public android.content.pm.SigningInfo getSigningInfo();
method @NonNull public android.net.Uri getStagedPackageUri();
- method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public long getTimeoutTime();
+ method public long getTimeoutTime();
method public int getVerificationPolicy();
- method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus);
- method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus, @NonNull android.os.PersistableBundle);
- method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationIncomplete(int);
- method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public boolean setVerificationPolicy(int);
+ method public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus);
+ method public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus, @NonNull android.os.PersistableBundle);
+ method public void reportVerificationIncomplete(int);
+ method public boolean setVerificationPolicy(int);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.pkg.VerificationSession> CREATOR;
field public static final int VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE = 1; // 0x1
@@ -7338,6 +7340,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]);
method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public void setVolumeGroupVolumeIndex(int, int, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setVolumeIndexForAttributes(@NonNull android.media.AudioAttributes, int, int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setWiredDeviceConnectionState(@NonNull android.media.AudioDeviceAttributes, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean supportsBluetoothVariableLatency();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicyAsync(@NonNull android.media.audiopolicy.AudioPolicy);
@@ -7347,6 +7350,8 @@
field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1
field public static final int AUDIOFOCUS_FLAG_LOCK = 4; // 0x4
field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2
+ field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int DEVICE_CONNECTION_STATE_CONNECTED = 1; // 0x1
+ field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int DEVICE_CONNECTION_STATE_DISCONNECTED = 0; // 0x0
field public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3; // 0x3
field public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY = 5; // 0x5
field public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE = 4; // 0x4
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 2e3d226..56538d9 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -8755,21 +8755,9 @@
* Do a quick check for whether an application might be able to perform an operation.
* This is <em>not</em> a security check; you must use {@link #noteOp(String, int, String,
* String, String)} or {@link #startOp(String, int, String, String, String)} for your actual
- * security checks, which also ensure that the given uid and package name are consistent. This
- * function can just be used for a quick check to see if an operation has been disabled for the
- * application, as an early reject of some work. This does not modify the time stamp or other
- * data about the operation.
- *
- * <p>Important things this will not do (which you need to ultimate use
- * {@link #noteOp(String, int, String, String, String)} or
- * {@link #startOp(String, int, String, String, String)} to cover):</p>
- * <ul>
- * <li>Verifying the uid and package are consistent, so callers can't spoof
- * their identity.</li>
- * <li>Taking into account the current foreground/background state of the
- * app; apps whose mode varies by this state will always be reported
- * as {@link #MODE_ALLOWED}.</li>
- * </ul>
+ * security checks. This function can just be used for a quick check to see if an operation has
+ * been disabled for the application, as an early reject of some work. This does not modify the
+ * time stamp or other data about the operation.
*
* @param op The operation to check. One of the OPSTR_* constants.
* @param uid The user id of the application attempting to perform the operation.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 64aa705..ca1662e6 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -11823,28 +11823,42 @@
sanitizeProgressColor(indeterminateColor,
backgroundColor, defaultProgressColor));
} else {
-
// Ensure segment color contrasts.
final List<Segment> segments = new ArrayList<>();
+ int totalLength = 0;
for (Segment segment : mProgressSegments) {
- segments.add(sanitizeSegment(segment, backgroundColor,
- defaultProgressColor));
+ final int length = segment.getLength();
+ if (length <= 0) continue;
+
+ try {
+ totalLength += Math.addExact(totalLength, length);
+ segments.add(sanitizeSegment(segment, backgroundColor,
+ defaultProgressColor));
+ } catch (ArithmeticException e) {
+ totalLength = DEFAULT_PROGRESS_MAX;
+ segments.clear();
+ break;
+ }
}
// Create default segment when no segments are provided.
if (segments.isEmpty()) {
- segments.add(sanitizeSegment(new Segment(100), backgroundColor,
+ totalLength = DEFAULT_PROGRESS_MAX;
+ segments.add(sanitizeSegment(new Segment(totalLength), backgroundColor,
defaultProgressColor));
}
// Ensure point color contrasts.
final List<Point> points = new ArrayList<>();
for (Point point : mProgressPoints) {
+ final int position = point.getPosition();
+ if (position < 0 || position > totalLength) continue;
+
points.add(sanitizePoint(point, backgroundColor, defaultProgressColor));
}
model = new NotificationProgressModel(segments, points,
- mProgress, mIsStyledByProgress);
+ Math.clamp(mProgress, 0, totalLength), mIsStyledByProgress);
}
return model;
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 06bf67c..f2a36e9 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -16,6 +16,8 @@
package android.app;
+import static android.service.notification.Flags.notificationClassification;
+
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
@@ -752,6 +754,11 @@
INotificationManager service = getService();
String pkg = mContext.getPackageName();
+ if (notificationClassification()
+ && NotificationChannel.SYSTEM_RESERVED_IDS.contains(notification.getChannelId())) {
+ return;
+ }
+
try {
if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
@@ -1131,6 +1138,10 @@
* had before it was deleted.
*/
public void deleteNotificationChannel(String channelId) {
+ if (notificationClassification()
+ && NotificationChannel.SYSTEM_RESERVED_IDS.contains(channelId)) {
+ return;
+ }
INotificationManager service = getService();
try {
service.deleteNotificationChannel(mContext.getPackageName(), channelId);
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index c93a6dd..bc9e709 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -30,16 +30,23 @@
import android.os.Process;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.util.ArrayMap;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.ApplicationSharedMemory;
import com.android.internal.os.BackgroundThread;
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
@@ -203,19 +210,14 @@
};
/**
- * Verify that the property name conforms to the standard. Log a warning if this is not true.
- * Note that this is done once in the cache constructor; it does not have to be very fast.
+ * Verify that the property name conforms to the standard and throw if this is not true. Note
+ * that this is done only once for a given property name; it does not have to be very fast.
*/
- private void validateCacheKey(String name) {
- if (Build.IS_USER) {
- // Do not bother checking keys in user builds. The keys will have been tested in
- // eng/userdebug builds already.
- return;
- }
+ private static void throwIfInvalidCacheKey(String name) {
for (int i = 0; i < sValidKeyPrefix.length; i++) {
if (name.startsWith(sValidKeyPrefix[i])) return;
}
- Log.w(TAG, "invalid cache name: " + name);
+ throw new IllegalArgumentException("invalid cache name: " + name);
}
/**
@@ -234,7 +236,8 @@
* reserved values cause the cache to be skipped.
*/
// This is the initial value of all cache keys. It is changed when a cache is invalidated.
- private static final int NONCE_UNSET = 0;
+ @VisibleForTesting
+ static final int NONCE_UNSET = 0;
// This value is used in two ways. First, it is used internally to indicate that the cache is
// disabled for the current query. Secondly, it is used to globally disable the cache across
// the entire system. Once a cache is disabled, there is no way to enable it again. The
@@ -685,6 +688,77 @@
}
/**
+ * Manage nonces that are stored in shared memory.
+ */
+ private static final class NonceSharedMem extends NonceHandler {
+ // The shared memory.
+ private volatile NonceStore mStore;
+
+ // The index of the nonce in shared memory.
+ private volatile int mHandle = NonceStore.INVALID_NONCE_INDEX;
+
+ // True if the string has been stored, ever.
+ private volatile boolean mRecorded = false;
+
+ // A short name that is saved in shared memory. This is the portion of the property name
+ // that follows the prefix.
+ private final String mShortName;
+
+ NonceSharedMem(@NonNull String name, @Nullable String prefix) {
+ super(name);
+ if ((prefix != null) && name.startsWith(prefix)) {
+ mShortName = name.substring(prefix.length());
+ } else {
+ mShortName = name;
+ }
+ }
+
+ // Fetch the nonce from shared memory. If the shared memory is not available, return
+ // UNSET. If the shared memory is available but the nonce name is not known (it may not
+ // have been invalidated by the server yet), return UNSET.
+ @Override
+ long getNonceInternal() {
+ if (mHandle == NonceStore.INVALID_NONCE_INDEX) {
+ if (mStore == null) {
+ mStore = NonceStore.getInstance();
+ if (mStore == null) {
+ return NONCE_UNSET;
+ }
+ }
+ mHandle = mStore.getHandleForName(mShortName);
+ if (mHandle == NonceStore.INVALID_NONCE_INDEX) {
+ return NONCE_UNSET;
+ }
+ }
+ return mStore.getNonce(mHandle);
+ }
+
+ // Set the nonce in shared mmory. If the shared memory is not available, throw an
+ // exception. Otherwise, if the nonce name has never been recorded, record it now and
+ // fetch the handle for the name. If the handle cannot be created, throw an exception.
+ @Override
+ void setNonceInternal(long value) {
+ if (mHandle == NonceStore.INVALID_NONCE_INDEX || !mRecorded) {
+ if (mStore == null) {
+ mStore = NonceStore.getInstance();
+ if (mStore == null) {
+ throw new IllegalStateException("setNonce: shared memory not ready");
+ }
+ }
+ // Always store the name before fetching the handle. storeName() is idempotent
+ // but does take a little time, so this code calls it just once.
+ mStore.storeName(mShortName);
+ mRecorded = true;
+ mHandle = mStore.getHandleForName(mShortName);
+ if (mHandle == NonceStore.INVALID_NONCE_INDEX) {
+ throw new IllegalStateException("setNonce: shared memory store failed");
+ }
+ }
+ mStore.setNonce(mHandle, value);
+ }
+ }
+
+ /**
* SystemProperties and shared storage are protected and cannot be written by random
* processes. So, for testing purposes, the NonceLocal handler stores the nonce locally. The
* NonceLocal uses the mTestNonce in the superclass, regardless of test mode.
@@ -712,6 +786,7 @@
* Complete key prefixes.
*/
private static final String PREFIX_TEST = CACHE_KEY_PREFIX + "." + MODULE_TEST + ".";
+ private static final String PREFIX_SYSTEM = CACHE_KEY_PREFIX + "." + MODULE_SYSTEM + ".";
/**
* A static list of nonce handlers, indexed by name. NonceHandlers can be safely shared by
@@ -722,16 +797,32 @@
private static final ConcurrentHashMap<String, NonceHandler> sHandlers
= new ConcurrentHashMap<>();
+ // True if shared memory is flag-enabled, false otherwise. Read the flags exactly once.
+ private static final boolean sSharedMemoryAvailable =
+ com.android.internal.os.Flags.applicationSharedMemoryEnabled()
+ && android.app.Flags.picUsesSharedMemory();
+
+ // Return true if this cache can use shared memory for its nonce. Shared memory may be used
+ // if the module is the system.
+ private static boolean sharedMemoryOkay(@NonNull String name) {
+ return sSharedMemoryAvailable && name.startsWith(PREFIX_SYSTEM);
+ }
+
/**
- * Return the proper nonce handler, based on the property name.
+ * Return the proper nonce handler, based on the property name. A handler is created if
+ * necessary. Before a handler is created, the name is checked, and an exception is thrown if
+ * the name is not valid.
*/
private static NonceHandler getNonceHandler(@NonNull String name) {
NonceHandler h = sHandlers.get(name);
if (h == null) {
synchronized (sGlobalLock) {
+ throwIfInvalidCacheKey(name);
h = sHandlers.get(name);
if (h == null) {
- if (name.startsWith(PREFIX_TEST)) {
+ if (sharedMemoryOkay(name)) {
+ h = new NonceSharedMem(name, PREFIX_SYSTEM);
+ } else if (name.startsWith(PREFIX_TEST)) {
h = new NonceLocal(name);
} else {
h = new NonceSysprop(name);
@@ -774,7 +865,6 @@
public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName,
@NonNull String cacheName) {
mPropertyName = propertyName;
- validateCacheKey(mPropertyName);
mCacheName = cacheName;
mNonce = getNonceHandler(mPropertyName);
mMaxEntries = maxEntries;
@@ -799,7 +889,6 @@
public PropertyInvalidatedCache(int maxEntries, @NonNull String module, @NonNull String api,
@NonNull String cacheName, @NonNull QueryHandler<Query, Result> computer) {
mPropertyName = createPropertyName(module, api);
- validateCacheKey(mPropertyName);
mCacheName = cacheName;
mNonce = getNonceHandler(mPropertyName);
mMaxEntries = maxEntries;
@@ -1620,6 +1709,14 @@
// then only that cache is reported.
boolean detail = anyDetailed(args);
+ if (sSharedMemoryAvailable) {
+ pw.println(" SharedMemory: enabled");
+ NonceStore.getInstance().dump(pw, " ", detail);
+ } else {
+ pw.println(" SharedMemory: disabled");
+ }
+ pw.println();
+
ArrayList<PropertyInvalidatedCache> activeCaches = getActiveCaches();
for (int i = 0; i < activeCaches.size(); i++) {
PropertyInvalidatedCache currentCache = activeCaches.get(i);
@@ -1654,4 +1751,363 @@
Log.e(TAG, "Failed to dump PropertyInvalidatedCache instances");
}
}
+
+ /**
+ * Nonces in shared memory are supported by a string block that acts as a table of contents
+ * for nonce names, and an array of nonce values. There are two key design principles with
+ * respect to nonce maps:
+ *
+ * 1. It is always okay if a nonce value cannot be determined. If the nonce is UNSET, the
+ * cache is bypassed, which is always functionally correct. Clients do not take extraordinary
+ * measures to be current with the nonce map. Clients must be current with the nonce itself;
+ * this is achieved through the shared memory.
+ *
+ * 2. Once a name is mapped to a nonce index, the mapping is fixed for the lifetime of the
+ * system. It is only necessary to distinguish between the unmapped and mapped states. Once
+ * a client has mapped a nonce, that mapping is known to be good for the lifetime of the
+ * system.
+ * @hide
+ */
+ @VisibleForTesting
+ public static class NonceStore {
+
+ // A lock for the store.
+ private final Object mLock = new Object();
+
+ // The native pointer. This is not owned by this class. It is owned by
+ // ApplicationSharedMemory, and it disappears when the owning instance is closed.
+ private final long mPtr;
+
+ // True if the memory is immutable.
+ private final boolean mMutable;
+
+ // The maximum length of a string in the string block. The maximum length must fit in a
+ // byte, but a smaller value has been chosen to limit memory use. Because strings are
+ // run-length encoded, a string consumes at most MAX_STRING_LENGTH+1 bytes in the string
+ // block.
+ private static final int MAX_STRING_LENGTH = 63;
+
+ // The raw byte block. Strings are stored as run-length encoded byte arrays. The first
+ // byte is the length of the following string. It is an axiom of the system that the
+ // string block is initially all zeros and that it is write-once memory: new strings are
+ // appended to existing strings, so there is never a need to revisit strings that have
+ // already been pulled from the string block.
+ @GuardedBy("mLock")
+ private final byte[] mStringBlock;
+
+ // The expected hash code of the string block. If the hash over the string block equals
+ // this value, then the string block is valid. Otherwise, the block is not valid and
+ // should be re-read. An invalid block generally means that a client has read the shared
+ // memory while the server was still writing it.
+ @GuardedBy("mLock")
+ private int mBlockHash = 0;
+
+ // The number of nonces that the native layer can hold. This is maintained for debug and
+ // logging.
+ private final int mMaxNonce;
+
+ /** @hide */
+ @VisibleForTesting
+ public NonceStore(long ptr, boolean mutable) {
+ mPtr = ptr;
+ mMutable = mutable;
+ mStringBlock = new byte[nativeGetMaxByte(ptr)];
+ mMaxNonce = nativeGetMaxNonce(ptr);
+ refreshStringBlockLocked();
+ }
+
+ // The static lock for singleton acquisition.
+ private static Object sLock = new Object();
+
+ // NonceStore is supposed to be a singleton.
+ private static NonceStore sInstance;
+
+ // Return the singleton instance.
+ static NonceStore getInstance() {
+ synchronized (sLock) {
+ if (sInstance == null) {
+ try {
+ ApplicationSharedMemory shmem = ApplicationSharedMemory.getInstance();
+ sInstance = (shmem == null)
+ ? null
+ : new NonceStore(shmem.getSystemNonceBlock(),
+ shmem.isMutable());
+ } catch (IllegalStateException e) {
+ // ApplicationSharedMemory.getInstance() throws if the shared memory is
+ // not yet mapped. Swallow the exception and leave sInstance null.
+ }
+ }
+ return sInstance;
+ }
+ }
+
+ // The index value of an unmapped name.
+ public static final int INVALID_NONCE_INDEX = -1;
+
+ // The highest string index extracted from the string block. -1 means no strings have
+ // been seen. This is used to skip strings that have already been processed, when the
+ // string block is updated.
+ @GuardedBy("mLock")
+ private int mHighestIndex = -1;
+
+ // The number bytes of the string block that has been used. This is a statistics.
+ @GuardedBy("mLock")
+ private int mStringBytes = 0;
+
+ // The number of partial reads on the string block. This is a statistic.
+ @GuardedBy("mLock")
+ private int mPartialReads = 0;
+
+ // The number of times the string block was updated. This is a statistic.
+ @GuardedBy("mLock")
+ private int mStringUpdated = 0;
+
+ // Map a string to a native index.
+ @GuardedBy("mLock")
+ private final ArrayMap<String, Integer> mStringHandle = new ArrayMap<>();
+
+ // Update the string map from the current string block. The string block is not modified
+ // and the block hash is not checked. The function skips past strings that have already
+ // been read, and then processes any new strings.
+ @GuardedBy("mLock")
+ private void updateStringMapLocked() {
+ int index = 0;
+ int offset = 0;
+ while (offset < mStringBlock.length && mStringBlock[offset] != 0) {
+ if (index > mHighestIndex) {
+ // Only record the string if it has not been seen yet.
+ final String s = new String(mStringBlock, offset+1, mStringBlock[offset]);
+ mStringHandle.put(s, index);
+ mHighestIndex = index;
+ }
+ offset += mStringBlock[offset] + 1;
+ index++;
+ }
+ mStringBytes = offset;
+ }
+
+ // Append a string to the string block and update the hash. This does not write the block
+ // to shared memory.
+ @GuardedBy("mLock")
+ private void appendStringToMapLocked(@NonNull String str) {
+ int offset = 0;
+ while (offset < mStringBlock.length && mStringBlock[offset] != 0) {
+ offset += mStringBlock[offset] + 1;
+ }
+ final byte[] strBytes = str.getBytes();
+
+ if (offset + strBytes.length >= mStringBlock.length) {
+ // Overflow. Do not add the string to the block; the string will remain undefined.
+ return;
+ }
+
+ mStringBlock[offset] = (byte) strBytes.length;
+ offset++;
+ for (int i = 0; i < strBytes.length; i++, offset++) {
+ mStringBlock[offset] = strBytes[i];
+ }
+ mBlockHash = Arrays.hashCode(mStringBlock);
+ }
+
+ // Possibly update the string block. If the native shared memory has a new block hash,
+ // then read the new string block values from shared memory, as well as the new hash.
+ @GuardedBy("mLock")
+ private void refreshStringBlockLocked() {
+ if (mBlockHash == nativeGetByteBlockHash(mPtr)) {
+ // The fastest way to know that the shared memory string block has not changed.
+ return;
+ }
+ final int hash = nativeGetByteBlock(mPtr, mBlockHash, mStringBlock);
+ if (hash != Arrays.hashCode(mStringBlock)) {
+ // This is a partial read: ignore it. The next time someone needs this string
+ // the memory will be read again and should succeed. Set the local hash to
+ // zero to ensure that the next read attempt will actually read from shared
+ // memory.
+ mBlockHash = 0;
+ mPartialReads++;
+ return;
+ }
+ // The hash has changed. Update the strings from the byte block.
+ mStringUpdated++;
+ mBlockHash = hash;
+ updateStringMapLocked();
+ }
+
+ // Throw an exception if the string cannot be stored in the string block.
+ private static void throwIfBadString(@NonNull String s) {
+ if (s.length() == 0) {
+ throw new IllegalArgumentException("cannot store an empty string");
+ }
+ if (s.length() > MAX_STRING_LENGTH) {
+ throw new IllegalArgumentException("cannot store a string longer than "
+ + MAX_STRING_LENGTH);
+ }
+ }
+
+ // Throw an exception if the nonce handle is invalid. The handle is bad if it is out of
+ // range of allocated handles. Note that NONCE_HANDLE_INVALID will throw: this is
+ // important for setNonce().
+ @GuardedBy("mLock")
+ private void throwIfBadHandle(int handle) {
+ if (handle < 0 || handle > mHighestIndex) {
+ throw new IllegalArgumentException("invalid nonce handle: " + handle);
+ }
+ }
+
+ // Throw if the memory is immutable (the process does not have write permission). The
+ // exception mimics the permission-denied exception thrown when a process writes to an
+ // unauthorized system property.
+ private void throwIfImmutable() {
+ if (!mMutable) {
+ throw new RuntimeException("write permission denied");
+ }
+ }
+
+ // Add a string to the local copy of the block and write the block to shared memory.
+ // Return the index of the new string. If the string has already been recorded, the
+ // shared memory is not updated but the index of the existing string is returned.
+ public int storeName(@NonNull String str) {
+ synchronized (mLock) {
+ Integer handle = mStringHandle.get(str);
+ if (handle == null) {
+ throwIfImmutable();
+ throwIfBadString(str);
+ appendStringToMapLocked(str);
+ nativeSetByteBlock(mPtr, mBlockHash, mStringBlock);
+ updateStringMapLocked();
+ handle = mStringHandle.get(str);
+ }
+ return handle;
+ }
+ }
+
+ // Retrieve the handle for a string. -1 is returned if the string is not found.
+ public int getHandleForName(@NonNull String str) {
+ synchronized (mLock) {
+ Integer handle = mStringHandle.get(str);
+ if (handle == null) {
+ refreshStringBlockLocked();
+ handle = mStringHandle.get(str);
+ }
+ return (handle != null) ? handle : INVALID_NONCE_INDEX;
+ }
+ }
+
+ // Thin wrapper around the native method.
+ public boolean setNonce(int handle, long value) {
+ synchronized (mLock) {
+ throwIfBadHandle(handle);
+ throwIfImmutable();
+ return nativeSetNonce(mPtr, handle, value);
+ }
+ }
+
+ public long getNonce(int handle) {
+ synchronized (mLock) {
+ throwIfBadHandle(handle);
+ return nativeGetNonce(mPtr, handle);
+ }
+ }
+
+ /**
+ * Dump the nonce statistics
+ */
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix, boolean detailed) {
+ synchronized (mLock) {
+ pw.println(formatSimple(
+ "%sStringsMapped: %d, BytesUsed: %d",
+ prefix, mHighestIndex, mStringBytes));
+ pw.println(formatSimple(
+ "%sPartialReads: %d, StringUpdates: %d",
+ prefix, mPartialReads, mStringUpdated));
+
+ if (detailed) {
+ for (String s: mStringHandle.keySet()) {
+ int h = mStringHandle.get(s);
+ pw.println(formatSimple(
+ "%sHandle:%d Name:%s", prefix, h, s));
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Return the maximum number of nonces supported in the native layer.
+ *
+ * @param mPtr the pointer to the native shared memory.
+ * @return the number of nonces supported by the shared memory.
+ */
+ private static native int nativeGetMaxNonce(long mPtr);
+
+ /**
+ * Return the maximum number of string bytes supported in the native layer.
+ *
+ * @param mPtr the pointer to the native shared memory.
+ * @return the number of string bytes supported by the shared memory.
+ */
+ private static native int nativeGetMaxByte(long mPtr);
+
+ /**
+ * Write the byte block and set the hash into shared memory. The method is relatively
+ * forgiving, in that any non-null byte array will be stored without error. The number of
+ * bytes will the lesser of the length of the block parameter and the size of the native
+ * array. The native layer performs no checks on either byte block or the hash.
+ *
+ * @param mPtr the pointer to the native shared memory.
+ * @param hash a value to be stored in the native block hash.
+ * @param block the byte array to be store.
+ */
+ @FastNative
+ private static native void nativeSetByteBlock(long mPtr, int hash, @NonNull byte[] block);
+
+ /**
+ * Retrieve the string block into the array and return the hash value. If the incoming hash
+ * value is the same as the hash in shared memory, the native function returns immediately
+ * without touching the block parameter. Note that a zero hash value will always cause shared
+ * memory to be read. The number of bytes read is the lesser of the length of the block
+ * parameter and the size of the native array.
+ *
+ * @param mPtr the pointer to the native shared memory.
+ * @param hash a value to be compared against the hash in the native layer.
+ * @param block an array to receive the bytes from the native layer.
+ * @return the hash from the native layer.
+ */
+ @FastNative
+ private static native int nativeGetByteBlock(long mPtr, int hash, @NonNull byte[] block);
+
+ /**
+ * Retrieve just the byte block hash from the native layer. The function is CriticalNative
+ * and thus very fast.
+ *
+ * @param mPtr the pointer to the native shared memory.
+ * @return the current native hash value.
+ */
+ @CriticalNative
+ private static native int nativeGetByteBlockHash(long mPtr);
+
+ /**
+ * Set a nonce at the specified index. The index is checked against the size of the native
+ * nonce array and the function returns true if the index is valid, and false. The function
+ * is CriticalNative and thus very fast.
+ *
+ * @param mPtr the pointer to the native shared memory.
+ * @param index the index of the nonce to set.
+ * @param value the value to set for the nonce.
+ * @return true if the index is inside the nonce array and false otherwise.
+ */
+ @CriticalNative
+ private static native boolean nativeSetNonce(long mPtr, int index, long value);
+
+ /**
+ * Get the nonce from the specified index. The index is checked against the size of the
+ * native nonce array; the function returns the nonce value if the index is valid, and 0
+ * otherwise. The function is CriticalNative and thus very fast.
+ *
+ * @param mPtr the pointer to the native shared memory.
+ * @param index the index of the nonce to retrieve.
+ * @return the value of the specified nonce, of 0 if the index is out of bounds.
+ */
+ @CriticalNative
+ private static native long nativeGetNonce(long mPtr, int index);
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index bd26db5..c6b8f3b 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1750,10 +1750,13 @@
@Override
public AdvancedProtectionManager createService(ContextImpl ctx)
throws ServiceNotFoundException {
- IBinder iBinder = ServiceManager.getServiceOrThrow(
+ IBinder iBinder = ServiceManager.getService(
Context.ADVANCED_PROTECTION_SERVICE);
IAdvancedProtectionService service =
IAdvancedProtectionService.Stub.asInterface(iBinder);
+ if (service == null) {
+ return null;
+ }
return new AdvancedProtectionManager(service);
}
});
diff --git a/core/java/android/app/appfunctions/AppFunctionManager.java b/core/java/android/app/appfunctions/AppFunctionManager.java
index a82c6ba7..5b478d09 100644
--- a/core/java/android/app/appfunctions/AppFunctionManager.java
+++ b/core/java/android/app/appfunctions/AppFunctionManager.java
@@ -42,10 +42,37 @@
import java.util.function.Consumer;
/**
- * Provides app functions related functionalities.
+ * Provides access to app functions.
*
- * <p>App function is a specific piece of functionality that an app offers to the system. These
- * functionalities can be integrated into various system features.
+ * <p>An app function is a piece of functionality that apps expose to the system for cross-app
+ * orchestration.
+ *
+ * <p>**Developer Workflow:**
+ *
+ * <p>Most developers should interact with app functions through the AppFunctions SDK. This SDK
+ * library offers a more convenient and type-safe way to represent the inputs and outputs of an app
+ * function, using custom data classes called "AppFunction Schemas".
+ *
+ * <p>The suggested way to build an app function is to use the AppFunctions SDK. The SDK provides
+ * custom data classes (AppFunctions Schemas) and handles the conversion to the underlying {@link
+ * android.app.appsearch.GenericDocument}/{@link android.os.Bundle} format used in {@link
+ * ExecuteAppFunctionRequest} and {@link ExecuteAppFunctionResponse}.
+ *
+ * <p>**Discovering (Listing) App Functions:**
+ *
+ * <p>When there is a package change or the device starts up, the metadata of available functions is
+ * indexed on-device by {@link AppSearchManager}. AppSearch stores the indexed information as a
+ * {@code AppFunctionStaticMetadata} document. This allows other apps and the app itself to discover
+ * these functions using the AppSearch search APIs. Visibility to this metadata document is based on
+ * the packages that have visibility to the app providing the app functions.
+ *
+ * <p>**Executing App Functions:**
+ *
+ * <p>Requests to execute a function are built using the {@link ExecuteAppFunctionRequest} class.
+ * Callers need the {@code android.permission.EXECUTE_APP_FUNCTIONS} or {@code
+ * android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} permission to execute app functions from other
+ * apps. An app has automatic visibility to its own functions and doesn't need these permissions to
+ * call its own functions via {@code AppFunctionManager}.
*/
@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
@SystemService(Context.APP_FUNCTION_SERVICE)
diff --git a/core/java/android/app/performance.aconfig b/core/java/android/app/performance.aconfig
new file mode 100644
index 0000000..7c6989e
--- /dev/null
+++ b/core/java/android/app/performance.aconfig
@@ -0,0 +1,11 @@
+package: "android.app"
+container: "system"
+
+flag {
+ namespace: "system_performance"
+ name: "pic_uses_shared_memory"
+ is_exported: true
+ is_fixed_read_only: true
+ description: "PropertyInvalidatedCache uses shared memory for nonces."
+ bug: "366552454"
+}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e8cec70..66ef004 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -11672,6 +11672,7 @@
Log.w(TAG, "Failure filling in extras", e);
}
}
+ mCreatorTokenInfo = other.mCreatorTokenInfo;
if (mayHaveCopiedUris && mContentUserHint == UserHandle.USER_CURRENT
&& other.mContentUserHint != UserHandle.USER_CURRENT) {
mContentUserHint = other.mContentUserHint;
@@ -12225,6 +12226,13 @@
}
/** @hide */
+ public void removeCreatorToken() {
+ if (mCreatorTokenInfo != null) {
+ mCreatorTokenInfo.mCreatorToken = null;
+ }
+ }
+
+ /** @hide */
public @Nullable IBinder getCreatorToken() {
return mCreatorTokenInfo == null ? null : mCreatorTokenInfo.mCreatorToken;
}
@@ -12251,7 +12259,7 @@
public void collectExtraIntentKeys() {
if (!isPreventIntentRedirectEnabled()) return;
- if (mExtras != null && !mExtras.isParcelled() && !mExtras.isEmpty()) {
+ if (mExtras != null && !mExtras.isEmpty()) {
for (String key : mExtras.keySet()) {
if (mExtras.get(key) instanceof Intent) {
if (mCreatorTokenInfo == null) {
@@ -12833,6 +12841,8 @@
private boolean isImageCaptureIntent() {
return (MediaStore.ACTION_IMAGE_CAPTURE.equals(mAction)
|| MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(mAction)
+ || MediaStore.ACTION_MOTION_PHOTO_CAPTURE.equals(mAction)
+ || MediaStore.ACTION_MOTION_PHOTO_CAPTURE_SECURE.equals(mAction)
|| MediaStore.ACTION_VIDEO_CAPTURE.equals(mAction));
}
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index c911326..ecea479 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -94,9 +94,9 @@
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES,android.Manifest.permission.REQUEST_INSTALL_PACKAGES})")
void reportUnarchivalStatus(int unarchiveId, int status, long requiredStorageBytes, in PendingIntent userActionIntent, in UserHandle userHandle);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
+ @EnforcePermission("VERIFICATION_AGENT")
int getVerificationPolicy();
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
+ @EnforcePermission("VERIFICATION_AGENT")
boolean setVerificationPolicy(int policy);
}
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 5f439b1..6f70586 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -297,14 +297,6 @@
}
flag {
- name: "get_packages_from_launcher_apps"
- namespace: "package_manager_service"
- description: "Feature flag to provide the new methods within launcher apps class to get packages."
- bug: "363324203"
- is_fixed_read_only: true
-}
-
-flag {
name: "remove_cross_user_permission_hack"
namespace: "package_manager_service"
description: "Feature flag to remove hack code of using PackageManager.MATCH_ANY_USER flag without cross user permission."
@@ -328,6 +320,13 @@
}
flag {
+ name: "sdk_dependency_installer"
+ namespace: "package_manager_service"
+ description: "Feature flag to enable installation of missing sdk dependency of app"
+ bug: "370822870"
+}
+
+flag {
name: "include_feature_flags_in_package_cacher"
namespace: "package_manager_service"
description: "Include feature flag status when determining hits or misses in PackageCacher."
diff --git a/core/java/android/content/pm/parsing/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java
index 19a13db..4220590 100644
--- a/core/java/android/content/pm/parsing/ApkLite.java
+++ b/core/java/android/content/pm/parsing/ApkLite.java
@@ -28,6 +28,7 @@
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DataClass;
+import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -141,6 +142,21 @@
private final boolean mIsSdkLibrary;
/**
+ * List of SDK names used by this apk.
+ */
+ private final @NonNull List<String> mUsesSdkLibraries;
+
+ /**
+ * List of SDK major versions used by this apk.
+ */
+ private final @Nullable long[] mUsesSdkLibrariesVersionsMajor;
+
+ /**
+ * List of SDK certificates used by this apk.
+ */
+ private final @Nullable String[][] mUsesSdkLibrariesCertDigests;
+
+ /**
* Indicates if this system app can be updated.
*/
private final boolean mUpdatableSystem;
@@ -167,7 +183,9 @@
String requiredSystemPropertyName, String requiredSystemPropertyValue,
int minSdkVersion, int targetSdkVersion, int rollbackDataPolicy,
Set<String> requiredSplitTypes, Set<String> splitTypes,
- boolean hasDeviceAdminReceiver, boolean isSdkLibrary, boolean updatableSystem,
+ boolean hasDeviceAdminReceiver, boolean isSdkLibrary,
+ List<String> usesSdkLibraries, long[] usesSdkLibrariesVersionsMajor,
+ String[][] usesSdkLibrariesCertDigests, boolean updatableSystem,
String emergencyInstaller, List<SharedLibraryInfo> declaredLibraries) {
mPath = path;
mPackageName = packageName;
@@ -202,6 +220,9 @@
mRollbackDataPolicy = rollbackDataPolicy;
mHasDeviceAdminReceiver = hasDeviceAdminReceiver;
mIsSdkLibrary = isSdkLibrary;
+ mUsesSdkLibraries = usesSdkLibraries;
+ mUsesSdkLibrariesVersionsMajor = usesSdkLibrariesVersionsMajor;
+ mUsesSdkLibrariesCertDigests = usesSdkLibrariesCertDigests;
mUpdatableSystem = updatableSystem;
mEmergencyInstaller = emergencyInstaller;
mArchivedPackage = null;
@@ -242,6 +263,9 @@
mRollbackDataPolicy = 0;
mHasDeviceAdminReceiver = false;
mIsSdkLibrary = false;
+ mUsesSdkLibraries = Collections.emptyList();
+ mUsesSdkLibrariesVersionsMajor = null;
+ mUsesSdkLibrariesCertDigests = null;
mUpdatableSystem = true;
mEmergencyInstaller = null;
mArchivedPackage = archivedPackage;
@@ -555,6 +579,30 @@
}
/**
+ * List of SDK names used by this apk.
+ */
+ @DataClass.Generated.Member
+ public @NonNull List<String> getUsesSdkLibraries() {
+ return mUsesSdkLibraries;
+ }
+
+ /**
+ * List of SDK major versions used by this apk.
+ */
+ @DataClass.Generated.Member
+ public @Nullable long[] getUsesSdkLibrariesVersionsMajor() {
+ return mUsesSdkLibrariesVersionsMajor;
+ }
+
+ /**
+ * List of SDK certificates used by this apk.
+ */
+ @DataClass.Generated.Member
+ public @Nullable String[][] getUsesSdkLibrariesCertDigests() {
+ return mUsesSdkLibrariesCertDigests;
+ }
+
+ /**
* Indicates if this system app can be updated.
*/
@DataClass.Generated.Member
@@ -584,10 +632,10 @@
}
@DataClass.Generated(
- time = 1728333566322L,
+ time = 1729247366948L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ApkLite.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final int mRollbackDataPolicy\nprivate final boolean mHasDeviceAdminReceiver\nprivate final boolean mIsSdkLibrary\nprivate final boolean mUpdatableSystem\nprivate final @android.annotation.Nullable java.lang.String mEmergencyInstaller\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final int mRollbackDataPolicy\nprivate final boolean mHasDeviceAdminReceiver\nprivate final boolean mIsSdkLibrary\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesSdkLibraries\nprivate final @android.annotation.Nullable long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.Nullable java.lang.String[][] mUsesSdkLibrariesCertDigests\nprivate final boolean mUpdatableSystem\nprivate final @android.annotation.Nullable java.lang.String mEmergencyInstaller\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 1a7f628..50d8758 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -32,6 +32,7 @@
import android.content.res.ApkAssets;
import android.content.res.XmlResourceParser;
import android.os.Build;
+import android.os.SystemProperties;
import android.os.Trace;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -44,6 +45,7 @@
import com.android.internal.util.ArrayUtils;
import libcore.io.IoUtils;
+import libcore.util.HexEncoding;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -88,6 +90,7 @@
private static final String TAG_USES_SDK = "uses-sdk";
private static final String TAG_USES_SPLIT = "uses-split";
private static final String TAG_MANIFEST = "manifest";
+ private static final String TAG_USES_SDK_LIBRARY = "uses-sdk-library";
private static final String TAG_SDK_LIBRARY = "sdk-library";
private static final int SDK_VERSION = Build.VERSION.SDK_INT;
private static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES;
@@ -460,6 +463,9 @@
boolean hasDeviceAdminReceiver = false;
boolean isSdkLibrary = false;
+ List<String> usesSdkLibraries = new ArrayList<>();
+ long[] usesSdkLibrariesVersionsMajor = new long[0];
+ String[][] usesSdkLibrariesCertDigests = new String[0][0];
List<SharedLibraryInfo> declaredLibraries = new ArrayList<>();
// Only search the tree when the tag is the direct child of <manifest> tag
@@ -523,6 +529,57 @@
hasDeviceAdminReceiver |= isDeviceAdminReceiver(parser,
hasBindDeviceAdminPermission);
break;
+ case TAG_USES_SDK_LIBRARY:
+ String usesSdkLibName = parser.getAttributeValue(
+ ANDROID_RES_NAMESPACE, "name");
+ long usesSdkLibVersionMajor = parser.getAttributeIntValue(
+ ANDROID_RES_NAMESPACE, "versionMajor", -1);
+ String usesSdkCertDigest = parser.getAttributeValue(
+ ANDROID_RES_NAMESPACE, "certDigest");
+
+ if (usesSdkLibName == null || usesSdkLibName.isBlank()
+ || usesSdkLibVersionMajor < 0) {
+ return input.error(
+ PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ "Bad uses-sdk-library declaration name: "
+ + usesSdkLibName
+ + " version: " + usesSdkLibVersionMajor);
+ }
+
+ if (usesSdkLibraries.contains(usesSdkLibName)) {
+ return input.error(
+ PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ "Bad uses-sdk-library declaration. Depending on"
+ + " multiple versions of SDK library: "
+ + usesSdkLibName);
+ }
+
+ usesSdkLibraries.add(usesSdkLibName);
+ usesSdkLibrariesVersionsMajor = ArrayUtils.appendLong(
+ usesSdkLibrariesVersionsMajor, usesSdkLibVersionMajor,
+ /*allowDuplicates=*/ true);
+
+ // We allow ":" delimiters in the SHA declaration as this is the format
+ // emitted by the certtool making it easy for developers to copy/paste.
+ // TODO(372862145): Add test for this replacement
+ usesSdkCertDigest = usesSdkCertDigest.replace(":", "").toLowerCase();
+
+ if ("".equals(usesSdkCertDigest)) {
+ // Test-only uses-sdk-library empty certificate digest override.
+ usesSdkCertDigest = SystemProperties.get(
+ "debug.pm.uses_sdk_library_default_cert_digest", "");
+ // Validate the overridden digest.
+ try {
+ HexEncoding.decode(usesSdkCertDigest, false);
+ } catch (IllegalArgumentException e) {
+ usesSdkCertDigest = "";
+ }
+ }
+ // TODO(372862145): Add support for multiple signer
+ usesSdkLibrariesCertDigests = ArrayUtils.appendElement(String[].class,
+ usesSdkLibrariesCertDigests, new String[]{usesSdkCertDigest},
+ /*allowDuplicates=*/ true);
+ break;
case TAG_SDK_LIBRARY:
isSdkLibrary = true;
// Mirrors ParsingPackageUtils#parseSdkLibrary until lite and full
@@ -534,7 +591,7 @@
if (sdkLibName == null || sdkLibVersionMajor < 0) {
return input.error(
PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Bad uses-sdk-library declaration name: " + sdkLibName
+ "Bad sdk-library declaration name: " + sdkLibName
+ " version: " + sdkLibVersionMajor);
}
declaredLibraries.add(new SharedLibraryInfo(
@@ -694,8 +751,9 @@
overlayIsStatic, overlayPriority, requiredSystemPropertyName,
requiredSystemPropertyValue, minSdkVersion, targetSdkVersion,
rollbackDataPolicy, requiredSplitTypes.first, requiredSplitTypes.second,
- hasDeviceAdminReceiver, isSdkLibrary, updatableSystem, emergencyInstaller,
- declaredLibraries));
+ hasDeviceAdminReceiver, isSdkLibrary, usesSdkLibraries,
+ usesSdkLibrariesVersionsMajor, usesSdkLibrariesCertDigests,
+ updatableSystem, emergencyInstaller, declaredLibraries));
}
private static boolean isDeviceAdminReceiver(
diff --git a/core/java/android/content/pm/parsing/PackageLite.java b/core/java/android/content/pm/parsing/PackageLite.java
index 9a2ee7f..79c5973 100644
--- a/core/java/android/content/pm/parsing/PackageLite.java
+++ b/core/java/android/content/pm/parsing/PackageLite.java
@@ -115,6 +115,12 @@
*/
private final boolean mIsSdkLibrary;
+ private final @NonNull List<String> mUsesSdkLibraries;
+
+ private final @Nullable long[] mUsesSdkLibrariesVersionsMajor;
+
+ private final @Nullable String[][] mUsesSdkLibrariesCertDigests;
+
private final @NonNull List<SharedLibraryInfo> mDeclaredLibraries;
/**
@@ -149,6 +155,9 @@
mSplitRequired = (baseApk.isSplitRequired() || hasAnyRequiredSplitTypes());
mProfileableByShell = baseApk.isProfileableByShell();
mIsSdkLibrary = baseApk.isIsSdkLibrary();
+ mUsesSdkLibraries = baseApk.getUsesSdkLibraries();
+ mUsesSdkLibrariesVersionsMajor = baseApk.getUsesSdkLibrariesVersionsMajor();
+ mUsesSdkLibrariesCertDigests = baseApk.getUsesSdkLibrariesCertDigests();
mSplitNames = splitNames;
mSplitTypes = splitTypes;
mIsFeatureSplits = isFeatureSplits;
@@ -438,6 +447,21 @@
}
@DataClass.Generated.Member
+ public @NonNull List<String> getUsesSdkLibraries() {
+ return mUsesSdkLibraries;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable long[] getUsesSdkLibrariesVersionsMajor() {
+ return mUsesSdkLibrariesVersionsMajor;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable String[][] getUsesSdkLibrariesCertDigests() {
+ return mUsesSdkLibrariesCertDigests;
+ }
+
+ @DataClass.Generated.Member
public @NonNull List<SharedLibraryInfo> getDeclaredLibraries() {
return mDeclaredLibraries;
}
@@ -451,10 +475,10 @@
}
@DataClass.Generated(
- time = 1728333569917L,
+ time = 1729248757933L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/PackageLite.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mTargetSdk\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\nprivate final boolean mIsSdkLibrary\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic java.util.List<java.lang.String> getAllApkPaths()\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mTargetSdk\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\nprivate final boolean mIsSdkLibrary\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesSdkLibraries\nprivate final @android.annotation.Nullable long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.Nullable java.lang.String[][] mUsesSdkLibrariesCertDigests\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic java.util.List<java.lang.String> getAllApkPaths()\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl b/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl
index 66caf2d..2ab7452 100644
--- a/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl
+++ b/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl
@@ -24,16 +24,9 @@
* @hide
*/
interface IVerificationSessionInterface {
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
long getTimeoutTime(int verificationId);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
long extendTimeRemaining(int verificationId, long additionalMs);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
boolean setVerificationPolicy(int verificationId, int policy);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
void reportVerificationIncomplete(int verificationId, int reason);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
- void reportVerificationComplete(int verificationId, in VerificationStatus status);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
- void reportVerificationCompleteWithExtensionResponse(int verificationId, in VerificationStatus status, in PersistableBundle response);
+ void reportVerificationComplete(int verificationId, in VerificationStatus status, in @nullable PersistableBundle extensionResponse);
}
\ No newline at end of file
diff --git a/core/java/android/content/pm/verify/pkg/VerificationSession.java b/core/java/android/content/pm/verify/pkg/VerificationSession.java
index 4ade211..97f78e0 100644
--- a/core/java/android/content/pm/verify/pkg/VerificationSession.java
+++ b/core/java/android/content/pm/verify/pkg/VerificationSession.java
@@ -19,7 +19,6 @@
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.pm.Flags;
import android.content.pm.PackageInstaller;
@@ -166,8 +165,8 @@
/**
* Get the value of Clock.elapsedRealtime() at which time this verification
* will timeout as incomplete if no other verification response is provided.
+ * @throws SecurityException if the caller is not the current verifier bound by the system.
*/
- @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
public long getTimeoutTime() {
try {
return mSession.getTimeoutTime(mId);
@@ -190,8 +189,8 @@
/**
* Override the verification policy for this session.
* @return True if the override was successful, False otherwise.
+ * @throws SecurityException if the caller is not the current verifier bound by the system.
*/
- @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
public boolean setVerificationPolicy(@PackageInstaller.VerificationPolicy int policy) {
if (mVerificationPolicy == policy) {
// No effective policy change
@@ -215,8 +214,8 @@
* This may be called multiple times. If the request would bypass any max
* duration by the system, the method will return a lower value than the
* requested amount that indicates how much the time was extended.
+ * @throws SecurityException if the caller is not the current verifier bound by the system.
*/
- @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
public long extendTimeRemaining(long additionalMs) {
try {
return mSession.extendTimeRemaining(mId, additionalMs);
@@ -227,9 +226,9 @@
/**
* Report to the system that verification could not be completed along
- * with an approximate reason to pass on to the installer.
+ * with an approximate reason to pass on to the installer.]
+ * @throws SecurityException if the caller is not the current verifier bound by the system.
*/
- @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
public void reportVerificationIncomplete(@VerificationIncompleteReason int reason) {
try {
mSession.reportVerificationIncomplete(mId, reason);
@@ -242,11 +241,11 @@
* Report to the system that the verification has completed and the
* install process may act on that status to either block in the case
* of failure or continue to process the install in the case of success.
+ * @throws SecurityException if the caller is not the current verifier bound by the system.
*/
- @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
public void reportVerificationComplete(@NonNull VerificationStatus status) {
try {
- mSession.reportVerificationComplete(mId, status);
+ mSession.reportVerificationComplete(mId, status, /* extensionResponse= */ null);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -256,12 +255,12 @@
* Same as {@link #reportVerificationComplete(VerificationStatus)}, but also provide
* a result to the extension params provided in the request, which will be passed to the
* installer in the installation result.
+ * @throws SecurityException if the caller is not the current verifier bound by the system.
*/
- @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
public void reportVerificationComplete(@NonNull VerificationStatus status,
- @NonNull PersistableBundle response) {
+ @NonNull PersistableBundle extensionResponse) {
try {
- mSession.reportVerificationCompleteWithExtensionResponse(mId, status, response);
+ mSession.reportVerificationComplete(mId, status, extensionResponse);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index ebcc371..22dbf5b 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -43,6 +43,7 @@
import android.media.ImageReader;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.IntArray;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
@@ -596,6 +597,10 @@
mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
mTimestampBase = TIMESTAMP_BASE_DEFAULT;
mMirrorMode = MIRROR_MODE_AUTO;
+ mMirrorModeForSurfaces = new IntArray();
+ if (Flags.mirrorModeSharedSurfaces()) {
+ mMirrorModeForSurfaces.add(mMirrorMode);
+ }
mReadoutTimestampEnabled = false;
mIsReadoutSensorTimestampBase = false;
mUsage = 0;
@@ -827,6 +832,7 @@
mSurfaceGroupId = SURFACE_GROUP_ID_NONE;
mSurfaces = new ArrayList<Surface>();
+ mMirrorModeForSurfaces = new IntArray();
mRotation = ROTATION_0;
mConfiguredSize = surfaceSize;
mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(ImageFormat.PRIVATE);
@@ -971,6 +977,9 @@
mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
mColorSpace = ColorSpaceProfiles.UNSPECIFIED;
mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
+ mTimestampBase = TIMESTAMP_BASE_DEFAULT;
+ mMirrorMode = MIRROR_MODE_AUTO;
+ mMirrorModeForSurfaces = new IntArray();
mReadoutTimestampEnabled = false;
mIsReadoutSensorTimestampBase = false;
mUsage = usage;
@@ -1239,6 +1248,9 @@
}
mSurfaces.add(surface);
+ if (Flags.mirrorModeSharedSurfaces()) {
+ mMirrorModeForSurfaces.add(mMirrorMode);
+ }
}
/**
@@ -1266,9 +1278,16 @@
throw new IllegalArgumentException(
"Cannot remove surface associated with this output configuration");
}
- if (!mSurfaces.remove(surface)) {
+
+ int surfaceIndex = mSurfaces.indexOf(surface);
+ if (surfaceIndex == -1) {
throw new IllegalArgumentException("Surface is not part of this output configuration");
}
+
+ mSurfaces.remove(surfaceIndex);
+ if (Flags.mirrorModeSharedSurfaces()) {
+ mMirrorModeForSurfaces.remove(surfaceIndex);
+ }
}
/**
@@ -1405,6 +1424,11 @@
* ImageReader, MediaRecorder, or MediaCodec, the mirror mode has no effect. If mirroring is
* needed for such outputs, the application needs to mirror the image buffers itself before
* passing them onward.</p>
+ *
+ * <p>Starting from Android 16, this function sets the mirror modes for all of the output
+ * surfaces contained within this OutputConfiguration. To set the mirror mode for a particular
+ * output surface, the application can call {@link #setMirrorMode(Surface, int)}. Prior to
+ * Android 16, this function is only applicable if surface sharing is not enabled.</p>
*/
public void setMirrorMode(@MirrorMode int mirrorMode) {
// Verify that the value is in range
@@ -1413,6 +1437,9 @@
throw new IllegalArgumentException("Not a valid mirror mode " + mirrorMode);
}
mMirrorMode = mirrorMode;
+ for (int j = 0; j < mMirrorModeForSurfaces.size(); j++) {
+ mMirrorModeForSurfaces.set(j, mirrorMode);
+ }
}
/**
@@ -1428,6 +1455,72 @@
}
/**
+ * Set the mirroring mode for a surface belonging to this OutputConfiguration
+ *
+ * <p>This function is identical to {@link #setMirroMode(int)} if {@code surface} is
+ * the only surface belonging to this OutputConfiguration.</p>
+ *
+ * <p>If this OutputConfiguration contains a deferred surface, the application can either
+ * call {@link #setMirrorMode(int)}, or call this function after calling {@link #addSurface}.
+ * </p>
+ *
+ * <p>If this OutputConfiguration contains shared surfaces, the application can set
+ * different mirroring modes for different surfaces.</p>
+ *
+ * <p>For efficiency, the mirror effect is applied as a transform flag, so it is only effective
+ * in some outputs. It works automatically for SurfaceView and TextureView outputs. For manual
+ * use of SurfaceTexture, it is reflected in the value of
+ * {@link android.graphics.SurfaceTexture#getTransformMatrix}. For other end points, such as
+ * ImageReader, MediaRecorder, or MediaCodec, the mirror mode has no effect. If mirroring is
+ * needed for such outputs, the application needs to mirror the image buffers itself before
+ * passing them onward.</p>
+ *
+ * @throws IllegalArgumentException If the {@code surface} doesn't belong to this
+ * OutputConfiguration, or the {@code mirrorMode} value is
+ * not valid.
+ */
+ @FlaggedApi(Flags.FLAG_MIRROR_MODE_SHARED_SURFACES)
+ public void setMirrorMode(@NonNull Surface surface, @MirrorMode int mirrorMode) {
+ checkNotNull(surface, "Surface must not be null");
+ // Verify that the value is in range
+ if (mirrorMode < MIRROR_MODE_AUTO || mirrorMode > MIRROR_MODE_V) {
+ throw new IllegalArgumentException("Not a valid mirror mode " + mirrorMode);
+ }
+ int surfaceIndex = mSurfaces.indexOf(surface);
+ if (surfaceIndex == -1) {
+ throw new IllegalArgumentException("Surface not part of the OutputConfiguration");
+ }
+
+ mMirrorModeForSurfaces.set(surfaceIndex, mirrorMode);
+ }
+
+ /**
+ * Get the current mirroring mode for an output surface
+ *
+ * <p>If no {@link #setMirrorMode} is called first, this function returns
+ * {@link #MIRROR_MODE_AUTO}.</p>
+ *
+ * <p>If only {@link #setMirrorMode(int)} is called, the mirroring mode set by that
+ * function will be returned here as long as the {@code surface} belongs to this
+ * output configuration.</p>
+ *
+ * @throws IllegalArgumentException If the {@code surface} doesn't belong to this
+ * OutputConfiguration.
+ *
+ * @return The mirroring mode for the specified output surface
+ */
+ @FlaggedApi(Flags.FLAG_MIRROR_MODE_SHARED_SURFACES)
+ public @MirrorMode int getMirrorMode(@NonNull Surface surface) {
+ checkNotNull(surface, "Surface must not be null");
+
+ int surfaceIndex = mSurfaces.indexOf(surface);
+ if (surfaceIndex == -1) {
+ throw new IllegalArgumentException("Surface not part of the OutputConfiguration");
+ }
+ return mMirrorModeForSurfaces.get(surfaceIndex);
+ }
+
+ /**
* Use the camera sensor's readout time for the image timestamp.
*
* <p>The start of the camera sensor readout after exposure. For a rolling shutter camera
@@ -1491,6 +1584,7 @@
this.mStreamUseCase = other.mStreamUseCase;
this.mTimestampBase = other.mTimestampBase;
this.mMirrorMode = other.mMirrorMode;
+ this.mMirrorModeForSurfaces = other.mMirrorModeForSurfaces.clone();
this.mReadoutTimestampEnabled = other.mReadoutTimestampEnabled;
this.mUsage = other.mUsage;
}
@@ -1520,6 +1614,7 @@
int timestampBase = source.readInt();
int mirrorMode = source.readInt();
+ int[] mirrorModeForSurfaces = source.createIntArray();
boolean readoutTimestampEnabled = source.readInt() == 1;
int format = source.readInt();
int dataSpace = source.readInt();
@@ -1531,7 +1626,6 @@
mConfiguredSize = new Size(width, height);
mIsDeferredConfig = isDeferred;
mIsShared = isShared;
- mSurfaces = surfaces;
mUsage = 0;
if (mSurfaces.size() > 0) {
mSurfaceType = SURFACE_TYPE_UNKNOWN;
@@ -1560,6 +1654,7 @@
mStreamUseCase = streamUseCase;
mTimestampBase = timestampBase;
mMirrorMode = mirrorMode;
+ mMirrorModeForSurfaces = IntArray.wrap(mirrorModeForSurfaces);
mReadoutTimestampEnabled = readoutTimestampEnabled;
}
@@ -1706,6 +1801,7 @@
dest.writeLong(mStreamUseCase);
dest.writeInt(mTimestampBase);
dest.writeInt(mMirrorMode);
+ dest.writeIntArray(mMirrorModeForSurfaces.toArray());
dest.writeInt(mReadoutTimestampEnabled ? 1 : 0);
dest.writeInt(mConfiguredFormat);
dest.writeInt(mConfiguredDataspace);
@@ -1756,6 +1852,16 @@
return false;
}
}
+ if (Flags.mirrorModeSharedSurfaces()) {
+ if (mMirrorModeForSurfaces.size() != other.mMirrorModeForSurfaces.size()) {
+ return false;
+ }
+ for (int j = 0; j < mMirrorModeForSurfaces.size(); j++) {
+ if (mMirrorModeForSurfaces.get(j) != other.mMirrorModeForSurfaces.get(j)) {
+ return false;
+ }
+ }
+ }
int minLen = Math.min(mSurfaces.size(), other.mSurfaces.size());
for (int i = 0; i < minLen; i++) {
if (mSurfaces.get(i) != other.mSurfaces.get(i))
@@ -1799,8 +1905,9 @@
mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
mDynamicRangeProfile, mColorSpace, mStreamUseCase,
- mTimestampBase, mMirrorMode, mReadoutTimestampEnabled ? 1 : 0,
- Long.hashCode(mUsage));
+ mTimestampBase, mMirrorMode,
+ HashCodeHelpers.hashCode(mMirrorModeForSurfaces.toArray()),
+ mReadoutTimestampEnabled ? 1 : 0, Long.hashCode(mUsage));
}
return HashCodeHelpers.hashCode(
@@ -1810,7 +1917,9 @@
mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
mDynamicRangeProfile, mColorSpace, mStreamUseCase, mTimestampBase,
- mMirrorMode, mReadoutTimestampEnabled ? 1 : 0, Long.hashCode(mUsage));
+ mMirrorMode, HashCodeHelpers.hashCode(mMirrorModeForSurfaces.toArray()),
+ mReadoutTimestampEnabled ? 1 : 0,
+ Long.hashCode(mUsage));
}
private static final String TAG = "OutputConfiguration";
@@ -1852,6 +1961,8 @@
private int mTimestampBase;
// Mirroring mode
private int mMirrorMode;
+ // Per-surface mirror modes
+ private IntArray mMirrorModeForSurfaces;
// readout timestamp
private boolean mReadoutTimestampEnabled;
// Whether the timestamp base is set to READOUT_SENSOR
diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
index 59a602ca..7361d4f 100644
--- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java
+++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
@@ -16,6 +16,8 @@
package android.hardware.display;
+import static android.provider.Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED;
+
import android.annotation.TestApi;
import android.content.Context;
import android.hardware.biometrics.Flags;
@@ -151,8 +153,8 @@
public boolean screenOffUdfpsEnabled(int user) {
return !TextUtils.isEmpty(udfpsLongPressSensorType())
&& ((mScreenOffUdfpsEnabledByDefault && Flags.screenOffUnlockUdfps())
- ? boolSettingDefaultOn("screen_off_udfps_enabled", user)
- : boolSettingDefaultOff("screen_off_udfps_enabled", user));
+ ? boolSettingDefaultOn(SCREEN_OFF_UNLOCK_UDFPS_ENABLED, user)
+ : boolSettingDefaultOff(SCREEN_OFF_UNLOCK_UDFPS_ENABLED, user));
}
/** @hide */
diff --git a/core/java/android/hardware/input/AidlKeyGestureEvent.aidl b/core/java/android/hardware/input/AidlKeyGestureEvent.aidl
index 7cf8795..fc71519 100644
--- a/core/java/android/hardware/input/AidlKeyGestureEvent.aidl
+++ b/core/java/android/hardware/input/AidlKeyGestureEvent.aidl
@@ -26,4 +26,10 @@
int action;
int displayId;
int flags;
+
+ // App launch parameters: only set when gestureType = KEY_GESTURE_TYPE_LAUNCH_APPLICATION
+ String appLaunchCategory;
+ String appLaunchRole;
+ String appLaunchPackageName;
+ String appLaunchClassName;
}
diff --git a/core/java/android/hardware/input/AppLaunchData.java b/core/java/android/hardware/input/AppLaunchData.java
new file mode 100644
index 0000000..43186f5
--- /dev/null
+++ b/core/java/android/hardware/input/AppLaunchData.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * Provides data for launching an application.
+ *
+ * @hide
+ */
+public interface AppLaunchData {
+
+ /** Creates AppLaunchData for the provided category */
+ @NonNull
+ static AppLaunchData createLaunchDataForCategory(@NonNull String category) {
+ return new CategoryData(category);
+ }
+
+ /** Creates AppLaunchData for the provided role */
+ @NonNull
+ static AppLaunchData createLaunchDataForRole(@NonNull String role) {
+ return new RoleData(role);
+ }
+
+ /** Creates AppLaunchData for the target package name and class name */
+ @NonNull
+ static AppLaunchData createLaunchDataForComponent(@NonNull String packageName,
+ @NonNull String className) {
+ return new ComponentData(packageName, className);
+ }
+
+ @Nullable
+ static AppLaunchData createLaunchData(@Nullable String category, @Nullable String role,
+ @Nullable String packageName, @Nullable String className) {
+ if (!TextUtils.isEmpty(category)) {
+ return new CategoryData(category);
+ }
+ if (!TextUtils.isEmpty(role)) {
+ return new RoleData(role);
+ }
+ if (!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(className)) {
+ return new ComponentData(packageName, className);
+ }
+ return null;
+ }
+
+ /** Intent category based app launch data */
+ class CategoryData implements AppLaunchData {
+ @NonNull
+ private final String mCategory;
+ public CategoryData(@NonNull String category) {
+ mCategory = category;
+ }
+
+ @NonNull
+ public String getCategory() {
+ return mCategory;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof CategoryData that)) return false;
+ return Objects.equals(mCategory, that.mCategory);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mCategory);
+ }
+
+ @Override
+ public String toString() {
+ return "CategoryData{" +
+ "mCategory='" + mCategory + '\'' +
+ '}';
+ }
+ }
+
+ /** Role based app launch data */
+ class RoleData implements AppLaunchData {
+ @NonNull
+ private final String mRole;
+ public RoleData(@NonNull String role) {
+ mRole = role;
+ }
+
+ @NonNull
+ public String getRole() {
+ return mRole;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof RoleData roleData)) return false;
+ return Objects.equals(mRole, roleData.mRole);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mRole);
+ }
+
+ @Override
+ public String toString() {
+ return "RoleData{" +
+ "mRole='" + mRole + '\'' +
+ '}';
+ }
+ }
+
+ /** Target application launch data */
+ class ComponentData implements AppLaunchData {
+ @NonNull
+ private final String mPackageName;
+
+ @NonNull
+ private final String mClassName;
+
+ public ComponentData(@NonNull String packageName, @NonNull String className) {
+ mPackageName = packageName;
+ mClassName = className;
+ }
+
+ @NonNull
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ @NonNull
+ public String getClassName() {
+ return mClassName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ComponentData that)) return false;
+ return Objects.equals(mPackageName, that.mPackageName) && Objects.equals(
+ mClassName, that.mClassName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPackageName, mClassName);
+ }
+
+ @Override
+ public String toString() {
+ return "ComponentData{" +
+ "mPackageName='" + mPackageName + '\'' +
+ ", mClassName='" + mClassName + '\'' +
+ '}';
+ }
+ }
+}
diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java
index 71d17eb..ee1a6ab 100644
--- a/core/java/android/hardware/input/KeyGestureEvent.java
+++ b/core/java/android/hardware/input/KeyGestureEvent.java
@@ -19,6 +19,8 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.role.RoleManager;
+import android.content.Intent;
import android.view.Display;
import android.view.KeyCharacterMap;
@@ -26,6 +28,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* Provides information about the keyboard gesture event being triggered by an external keyboard.
@@ -37,6 +40,9 @@
@NonNull
private AidlKeyGestureEvent mKeyGestureEvent;
+ private static final int LOG_EVENT_UNSPECIFIED =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__UNSPECIFIED;
+
public static final int KEY_GESTURE_TYPE_UNSPECIFIED = 0;
public static final int KEY_GESTURE_TYPE_HOME = 1;
public static final int KEY_GESTURE_TYPE_RECENT_APPS = 2;
@@ -76,6 +82,8 @@
public static final int KEY_GESTURE_TYPE_SLEEP = 36;
public static final int KEY_GESTURE_TYPE_WAKEUP = 37;
public static final int KEY_GESTURE_TYPE_MEDIA_KEY = 38;
+ // TODO(b/280423320): Remove "LAUNCH_DEFAULT_..." gestures and rely on launch intent to find
+ // the correct logging event.
public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER = 39;
public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL = 40;
public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS = 41;
@@ -88,7 +96,7 @@
public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES = 48;
public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER = 49;
public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS = 50;
- public static final int KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME = 51;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_APPLICATION = 51;
public static final int KEY_GESTURE_TYPE_DESKTOP_MODE = 52;
public static final int KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION = 53;
public static final int KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER = 54;
@@ -166,7 +174,7 @@
KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES,
KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER,
KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS,
- KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME,
+ KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
KEY_GESTURE_TYPE_DESKTOP_MODE,
KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER,
@@ -210,6 +218,8 @@
private int mAction = KeyGestureEvent.ACTION_GESTURE_COMPLETE;
private int mDisplayId = Display.DEFAULT_DISPLAY;
private int mFlags = 0;
+ @Nullable
+ private AppLaunchData mAppLaunchData = null;
/**
* @see KeyGestureEvent#getDeviceId()
@@ -268,6 +278,14 @@
}
/**
+ * @see KeyGestureEvent#getAppLaunchData()
+ */
+ public Builder setAppLaunchData(@NonNull AppLaunchData appLaunchData) {
+ mAppLaunchData = appLaunchData;
+ return this;
+ }
+
+ /**
* Build {@link KeyGestureEvent}
*/
public KeyGestureEvent build() {
@@ -279,6 +297,21 @@
event.action = mAction;
event.displayId = mDisplayId;
event.flags = mFlags;
+ if (mAppLaunchData != null) {
+ if (mAppLaunchData instanceof AppLaunchData.CategoryData) {
+ event.appLaunchCategory =
+ ((AppLaunchData.CategoryData) mAppLaunchData).getCategory();
+ } else if (mAppLaunchData instanceof AppLaunchData.RoleData) {
+ event.appLaunchRole = ((AppLaunchData.RoleData) mAppLaunchData).getRole();
+ } else if (mAppLaunchData instanceof AppLaunchData.ComponentData) {
+ event.appLaunchPackageName =
+ ((AppLaunchData.ComponentData) mAppLaunchData).getPackageName();
+ event.appLaunchClassName =
+ ((AppLaunchData.ComponentData) mAppLaunchData).getClassName();
+ } else {
+ throw new IllegalArgumentException("AppLaunchData type is invalid!");
+ }
+ }
return new KeyGestureEvent(event);
}
}
@@ -315,6 +348,27 @@
return (mKeyGestureEvent.flags & FLAG_CANCELLED) != 0;
}
+ public int getLogEvent() {
+ if (getKeyGestureType() == KEY_GESTURE_TYPE_LAUNCH_APPLICATION) {
+ return getLogEventFromLaunchAppData(getAppLaunchData());
+ }
+ return keyGestureTypeToLogEvent(getKeyGestureType());
+ }
+
+ /**
+ * @return Launch app data associated with the event, only if key gesture type is
+ * {@code KEY_GESTURE_TYPE_LAUNCH_APPLICATION}
+ */
+ @Nullable
+ public AppLaunchData getAppLaunchData() {
+ if (mKeyGestureEvent.gestureType != KEY_GESTURE_TYPE_LAUNCH_APPLICATION) {
+ return null;
+ }
+ return AppLaunchData.createLaunchData(mKeyGestureEvent.appLaunchCategory,
+ mKeyGestureEvent.appLaunchRole, mKeyGestureEvent.appLaunchPackageName,
+ mKeyGestureEvent.appLaunchClassName);
+ }
+
@Override
public String toString() {
return "KeyGestureEvent { "
@@ -324,7 +378,8 @@
+ "keyGestureType = " + keyGestureTypeToString(mKeyGestureEvent.gestureType) + ", "
+ "action = " + mKeyGestureEvent.action + ", "
+ "displayId = " + mKeyGestureEvent.displayId + ", "
- + "flags = " + mKeyGestureEvent.flags
+ + "flags = " + mKeyGestureEvent.flags + ", "
+ + "appLaunchData = " + getAppLaunchData()
+ " }";
}
@@ -339,7 +394,11 @@
&& mKeyGestureEvent.gestureType == that.mKeyGestureEvent.gestureType
&& mKeyGestureEvent.action == that.mKeyGestureEvent.action
&& mKeyGestureEvent.displayId == that.mKeyGestureEvent.displayId
- && mKeyGestureEvent.flags == that.mKeyGestureEvent.flags;
+ && mKeyGestureEvent.flags == that.mKeyGestureEvent.flags
+ && Objects.equals(mKeyGestureEvent.appLaunchCategory, that.mKeyGestureEvent.appLaunchCategory)
+ && Objects.equals(mKeyGestureEvent.appLaunchRole, that.mKeyGestureEvent.appLaunchRole)
+ && Objects.equals(mKeyGestureEvent.appLaunchPackageName, that.mKeyGestureEvent.appLaunchPackageName)
+ && Objects.equals(mKeyGestureEvent.appLaunchClassName, that.mKeyGestureEvent.appLaunchClassName);
}
@Override
@@ -352,13 +411,21 @@
_hash = 31 * _hash + mKeyGestureEvent.action;
_hash = 31 * _hash + mKeyGestureEvent.displayId;
_hash = 31 * _hash + mKeyGestureEvent.flags;
+ _hash = 31 * _hash + (mKeyGestureEvent.appLaunchCategory != null
+ ? mKeyGestureEvent.appLaunchCategory.hashCode() : 0);
+ _hash = 31 * _hash + (mKeyGestureEvent.appLaunchRole != null
+ ? mKeyGestureEvent.appLaunchRole.hashCode() : 0);
+ _hash = 31 * _hash + (mKeyGestureEvent.appLaunchPackageName != null
+ ? mKeyGestureEvent.appLaunchPackageName.hashCode() : 0);
+ _hash = 31 * _hash + (mKeyGestureEvent.appLaunchClassName != null
+ ? mKeyGestureEvent.appLaunchClassName.hashCode() : 0);
return _hash;
}
/**
* Convert KeyGestureEvent type to corresponding log event got KeyboardSystemsEvent
*/
- public static int keyGestureTypeToLogEvent(@KeyGestureType int value) {
+ private static int keyGestureTypeToLogEvent(@KeyGestureType int value) {
switch (value) {
case KEY_GESTURE_TYPE_HOME:
return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__HOME;
@@ -460,14 +527,79 @@
return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_WEATHER;
case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS:
return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FITNESS;
- case KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME:
+ case KEY_GESTURE_TYPE_LAUNCH_APPLICATION:
return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_APPLICATION_BY_PACKAGE_NAME;
case KEY_GESTURE_TYPE_DESKTOP_MODE:
return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__DESKTOP_MODE;
case KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MULTI_WINDOW_NAVIGATION;
default:
- return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__UNSPECIFIED;
+ return LOG_EVENT_UNSPECIFIED;
+ }
+ }
+
+ /**
+ * Find Log event type corresponding to app launch data.
+ * Returns {@code LOG_EVENT_UNSPECIFIED} if no matching event found
+ */
+ private static int getLogEventFromLaunchAppData(@Nullable AppLaunchData data) {
+ if (data == null) {
+ return LOG_EVENT_UNSPECIFIED;
+ }
+ if (data instanceof AppLaunchData.CategoryData) {
+ return getLogEventFromSelectorCategory(
+ ((AppLaunchData.CategoryData) data).getCategory());
+ } else if (data instanceof AppLaunchData.RoleData) {
+ return getLogEventFromRole(((AppLaunchData.RoleData) data).getRole());
+ } else if (data instanceof AppLaunchData.ComponentData) {
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_APPLICATION_BY_PACKAGE_NAME;
+ } else {
+ throw new IllegalArgumentException("AppLaunchData type is invalid!");
+ }
+ }
+
+ private static int getLogEventFromSelectorCategory(@NonNull String category) {
+ switch (category) {
+ case Intent.CATEGORY_APP_BROWSER:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_BROWSER;
+ case Intent.CATEGORY_APP_EMAIL:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_EMAIL;
+ case Intent.CATEGORY_APP_CONTACTS:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CONTACTS;
+ case Intent.CATEGORY_APP_CALENDAR:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALENDAR;
+ case Intent.CATEGORY_APP_CALCULATOR:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALCULATOR;
+ case Intent.CATEGORY_APP_MUSIC:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MUSIC;
+ case Intent.CATEGORY_APP_MAPS:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MAPS;
+ case Intent.CATEGORY_APP_MESSAGING:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MESSAGING;
+ case Intent.CATEGORY_APP_GALLERY:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_GALLERY;
+ case Intent.CATEGORY_APP_FILES:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FILES;
+ case Intent.CATEGORY_APP_WEATHER:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_WEATHER;
+ case Intent.CATEGORY_APP_FITNESS:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FITNESS;
+ default:
+ return LOG_EVENT_UNSPECIFIED;
+ }
+ }
+
+ /**
+ * Find Log event corresponding to the provide system role name.
+ * Returns {@code LOG_EVENT_UNSPECIFIED} if no matching event found.
+ */
+ private static int getLogEventFromRole(@NonNull String role) {
+ if (RoleManager.ROLE_BROWSER.equals(role)) {
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_BROWSER;
+ } else if (RoleManager.ROLE_SMS.equals(role)) {
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MESSAGING;
+ } else {
+ return LOG_EVENT_UNSPECIFIED;
}
}
@@ -577,8 +709,8 @@
return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER";
case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS:
return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS";
- case KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME:
- return "KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME";
+ case KEY_GESTURE_TYPE_LAUNCH_APPLICATION:
+ return "KEY_GESTURE_TYPE_LAUNCH_APPLICATION";
case KEY_GESTURE_TYPE_DESKTOP_MODE:
return "KEY_GESTURE_TYPE_DESKTOP_MODE";
case KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index a1e7567..c6fd0ee 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -1627,7 +1627,7 @@
boolean allowMultipleTriggers = in.readBoolean();
KeyphraseRecognitionExtra[] keyphrases =
in.createTypedArray(KeyphraseRecognitionExtra.CREATOR);
- byte[] data = in.readBlob();
+ byte[] data = in.createByteArray();
int audioCapabilities = in.readInt();
return new RecognitionConfig(captureRequested, allowMultipleTriggers, keyphrases, data,
audioCapabilities);
@@ -1638,7 +1638,7 @@
dest.writeBoolean(mCaptureRequested);
dest.writeBoolean(mAllowMultipleTriggers);
dest.writeTypedArray(mKeyphrases, flags);
- dest.writeBlob(mData);
+ dest.writeByteArray(mData);
dest.writeInt(mAudioCapabilities);
}
diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java
index e68c4ca..6325b00 100644
--- a/core/java/android/os/BatteryUsageStatsQuery.java
+++ b/core/java/android/os/BatteryUsageStatsQuery.java
@@ -370,7 +370,10 @@
* and most battery stats resets.
*/
public Builder accumulated() {
- mFlags |= FLAG_BATTERY_USAGE_STATS_ACCUMULATED;
+ mFlags |= FLAG_BATTERY_USAGE_STATS_ACCUMULATED
+ | FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_STATE
+ | FLAG_BATTERY_USAGE_STATS_INCLUDE_SCREEN_STATE
+ | FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA;
return this;
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index a894833..13d7e3c 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1274,6 +1274,12 @@
* Vanilla Ice Cream.
*/
public static final int VANILLA_ICE_CREAM = 35;
+
+ /**
+ * Baklava.
+ */
+ @FlaggedApi(Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME)
+ public static final int BAKLAVA = CUR_DEVELOPMENT;
}
/** @hide */
@@ -1313,6 +1319,7 @@
VERSION_CODES_FULL.TIRAMISU,
VERSION_CODES_FULL.UPSIDE_DOWN_CAKE,
VERSION_CODES_FULL.VANILLA_ICE_CREAM,
+ VERSION_CODES_FULL.BAKLAVA,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SdkIntFull {}
@@ -1514,6 +1521,11 @@
*/
public static final int VANILLA_ICE_CREAM =
VERSION_CODES.VANILLA_ICE_CREAM * SDK_INT_MULTIPLIER;
+
+ /**
+ * The upcoming, not yet finalized, version of Android.
+ */
+ public static final int BAKLAVA = VERSION_CODES.BAKLAVA * SDK_INT_MULTIPLIER;
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 83c599e..8efbc9c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11085,6 +11085,12 @@
public static final String SFPS_PERFORMANT_AUTH_ENABLED = "sfps_performant_auth_enabled_v2";
/**
+ * Whether or not the UDFPS device is enabling the screen off unlock settings.
+ * @hide
+ */
+ public static final String SCREEN_OFF_UNLOCK_UDFPS_ENABLED = "screen_off_udfps_enabled";
+
+ /**
* Whether or not debugging is enabled.
* @hide
*/
diff --git a/core/java/android/security/forensic/ForensicEvent.java b/core/java/android/security/forensic/ForensicEvent.java
index 9cbc5ec..90906ed 100644
--- a/core/java/android/security/forensic/ForensicEvent.java
+++ b/core/java/android/security/forensic/ForensicEvent.java
@@ -61,6 +61,14 @@
in.readMap(mKeyValuePairs, getClass().getClassLoader(), String.class, String.class);
}
+ public String getType() {
+ return mType;
+ }
+
+ public Map<String, String> getKeyValuePairs() {
+ return mKeyValuePairs;
+ }
+
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeString(mType);
diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig
index 9bb1039..dec28c3 100644
--- a/core/java/android/security/responsible_apis_flags.aconfig
+++ b/core/java/android/security/responsible_apis_flags.aconfig
@@ -84,4 +84,13 @@
namespace: "responsible_apis"
description: "Prevent intent redirect attacks by aborting or throwing security exception"
bug: "361143368"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "enable_intent_matching_flags"
+ is_exported: true
+ namespace: "permissions"
+ is_fixed_read_only: true
+ description: "Applies intentMatchingFlags while matching intents to application components"
+ bug: "364354494"
+}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index e6de478..94f415b 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -412,41 +412,28 @@
*/
public static class JankData {
- /** @hide */
- @IntDef(flag = true, value = {JANK_NONE,
- DISPLAY_HAL,
- JANK_SURFACEFLINGER_DEADLINE_MISSED,
- JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED,
- JANK_APP_DEADLINE_MISSED,
- PREDICTION_ERROR,
- SURFACE_FLINGER_SCHEDULING})
+ /**
+ * Needs to be kept in sync with android_view_SurfaceControl.cpp's
+ * JankDataListenerWrapper::onJankDataAvailable.
+ * @hide
+ */
+ @IntDef(flag = true, value = {
+ JANK_NONE,
+ JANK_COMPOSER,
+ JANK_APPLICATION,
+ JANK_OTHER,
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface JankType {}
- // Needs to be kept in sync with frameworks/native/libs/gui/include/gui/JankInfo.h
-
// No Jank
- public static final int JANK_NONE = 0x0;
-
- // Jank not related to SurfaceFlinger or the App
- public static final int DISPLAY_HAL = 0x1;
- // SF took too long on the CPU
- public static final int JANK_SURFACEFLINGER_DEADLINE_MISSED = 0x2;
- // SF took too long on the GPU
- public static final int JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED = 0x4;
- // Either App or GPU took too long on the frame
- public static final int JANK_APP_DEADLINE_MISSED = 0x8;
- // Vsync predictions have drifted beyond the threshold from the actual HWVsync
- public static final int PREDICTION_ERROR = 0x10;
- // Latching a buffer early might cause an early present of the frame
- public static final int SURFACE_FLINGER_SCHEDULING = 0x20;
- // A buffer is said to be stuffed if it was expected to be presented on a vsync but was
- // presented later because the previous buffer was presented in its expected vsync. This
- // usually happens if there is an unexpectedly long frame causing the rest of the buffers
- // to enter a stuffed state.
- public static final int BUFFER_STUFFING = 0x40;
- // Jank due to unknown reasons.
- public static final int UNKNOWN = 0x80;
+ public static final int JANK_NONE = 0;
+ // Jank caused by the composer missing a deadline
+ public static final int JANK_COMPOSER = 1 << 0;
+ // Jank caused by the application missing the composer's deadline
+ public static final int JANK_APPLICATION = 1 << 1;
+ // Jank due to other unknown reasons
+ public static final int JANK_OTHER = 1 << 2;
public JankData(long frameVsyncId, @JankType int jankType, long frameIntervalNs,
long scheduledAppFrameTimeNs, long actualAppFrameTimeNs) {
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index dfac244..4e9d054 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -798,6 +798,18 @@
@FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED)
public static final int CONTENT_CHANGE_TYPE_CHECKED = 1 << 13;
+ /**
+ * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event: The source node changed its
+ * expanded state which is returned by {@link AccessibilityNodeInfo#getExpandedState()}. The
+ * view changing the node's expanded state should call {@link
+ * AccessibilityNodeInfo#setExpandedState(int)} and then send this event.
+ *
+ * @see AccessibilityNodeInfo#getExpandedState()
+ * @see AccessibilityNodeInfo#setExpandedState(int)
+ */
+ @FlaggedApi(Flags.FLAG_A11Y_EXPANSION_STATE_API)
+ public static final int CONTENT_CHANGE_TYPE_EXPANDED = 1 << 14;
+
// Speech state change types.
/** Change type for {@link #TYPE_SPEECH_STATE_CHANGE} event: another service is speaking. */
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 60ccb77..d3e7faa 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -756,6 +756,56 @@
public static final String ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT =
"android.view.accessibility.action.ARGUMENT_SCROLL_AMOUNT_FLOAT";
+ // Expanded state types.
+
+ /**
+ * Expanded state for a non-expandable element
+ *
+ * @see #getExpandedState()
+ * @see #setExpandedState(int)
+ */
+ @FlaggedApi(Flags.FLAG_A11Y_EXPANSION_STATE_API)
+ public static final int EXPANDED_STATE_UNDEFINED = 0;
+
+ /**
+ * Expanded state for a collapsed expandable element.
+ *
+ * @see #getExpandedState()
+ * @see #setExpandedState(int)
+ */
+ @FlaggedApi(Flags.FLAG_A11Y_EXPANSION_STATE_API)
+ public static final int EXPANDED_STATE_COLLAPSED = 1;
+
+ /**
+ * Expanded state for an expanded expandable element that can still be expanded further.
+ *
+ * @see #getExpandedState()
+ * @see #setExpandedState(int)
+ */
+ @FlaggedApi(Flags.FLAG_A11Y_EXPANSION_STATE_API)
+ public static final int EXPANDED_STATE_PARTIAL = 2;
+
+ /**
+ * Expanded state for a expanded expandable element that cannot be expanded further.
+ *
+ * @see #getExpandedState()
+ * @see #setExpandedState(int)
+ */
+ @FlaggedApi(Flags.FLAG_A11Y_EXPANSION_STATE_API)
+ public static final int EXPANDED_STATE_FULL = 3;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = "EXPANDED_STATE_",
+ value = {
+ EXPANDED_STATE_UNDEFINED,
+ EXPANDED_STATE_COLLAPSED,
+ EXPANDED_STATE_PARTIAL,
+ EXPANDED_STATE_FULL,
+ })
+ public @interface ExpandedState {}
+
// Focus types.
/**
@@ -1048,6 +1098,10 @@
private int mMaxTextLength = -1;
private int mMovementGranularities;
+ // TODO(b/362782158) Initialize mExpandedState explicitly with
+ // the EXPANDED_STATE_UNDEFINED state when flagging is removed.
+ private int mExpandedState;
+
private int mTextSelectionStart = UNDEFINED_SELECTION_INDEX;
private int mTextSelectionEnd = UNDEFINED_SELECTION_INDEX;
private int mInputType = InputType.TYPE_NULL;
@@ -1931,6 +1985,47 @@
}
/**
+ * Sets the expanded state of the node.
+ *
+ * <p><strong>Note:</strong> Cannot be called from an {@link
+ * android.accessibilityservice.AccessibilityService}. This class is made immutable before being
+ * delivered to an {@link android.accessibilityservice.AccessibilityService}.
+ *
+ * @param state new expanded state of this node.
+ * @throws IllegalArgumentException If state is not one of:
+ * <ul>
+ * <li>{@link #EXPANDED_STATE_UNDEFINED}
+ * <li>{@link #EXPANDED_STATE_COLLAPSED}
+ * <li>{@link #EXPANDED_STATE_PARTIAL}
+ * <li>{@link #EXPANDED_STATE_FULL}
+ * </ul>
+ *
+ * @throws IllegalStateException If called from an AccessibilityService
+ */
+ @FlaggedApi(Flags.FLAG_A11Y_EXPANSION_STATE_API)
+ public void setExpandedState(@ExpandedState int state) {
+ enforceValidExpandedState(state);
+ enforceNotSealed();
+ mExpandedState = state;
+ }
+
+ /**
+ * Gets the expanded state for this node.
+ *
+ * @return The expanded state, one of:
+ * <ul>
+ * <li>{@link #EXPANDED_STATE_UNDEFINED}
+ * <li>{@link #EXPANDED_STATE_COLLAPSED}
+ * <li>{@link #EXPANDED_STATE_FULL}
+ * <li>{@link #EXPANDED_STATE_PARTIAL}
+ * </ul>
+ */
+ @FlaggedApi(Flags.FLAG_A11Y_EXPANSION_STATE_API)
+ public @ExpandedState int getExpandedState() {
+ return mExpandedState;
+ }
+
+ /**
* Sets the minimum time duration between two content change events, which is used in throttling
* content change events in accessibility services.
*
@@ -4369,6 +4464,20 @@
}
}
+ private void enforceValidExpandedState(int state) {
+ if (Flags.a11yExpansionStateApi()) {
+ switch (state) {
+ case EXPANDED_STATE_UNDEFINED:
+ case EXPANDED_STATE_COLLAPSED:
+ case EXPANDED_STATE_PARTIAL:
+ case EXPANDED_STATE_FULL:
+ return;
+ default:
+ throw new IllegalArgumentException("Unknown expanded state: " + state);
+ }
+ }
+ }
+
/**
* Enforces that this instance is not sealed.
*
@@ -4623,6 +4732,11 @@
if (mChecked != DEFAULT.mChecked) {
nonDefaultFields |= bitAt(fieldIndex);
}
+ fieldIndex++;
+ if (mExpandedState != DEFAULT.mExpandedState) {
+ nonDefaultFields |= bitAt(fieldIndex);
+ }
+
int totalFields = fieldIndex;
parcel.writeLong(nonDefaultFields);
@@ -4794,6 +4908,9 @@
if (isBitSet(nonDefaultFields, fieldIndex++)) {
parcel.writeInt(mChecked);
}
+ if (isBitSet(nonDefaultFields, fieldIndex++)) {
+ parcel.writeInt(mExpandedState);
+ }
if (DEBUG) {
fieldIndex--;
@@ -4883,6 +5000,7 @@
mLeashedParent = other.mLeashedParent;
mLeashedParentNodeId = other.mLeashedParentNodeId;
mChecked = other.mChecked;
+ mExpandedState = other.mExpandedState;
}
private void initCopyInfos(AccessibilityNodeInfo other) {
@@ -5075,6 +5193,9 @@
if (isBitSet(nonDefaultFields, fieldIndex++)) {
mChecked = parcel.readInt();
}
+ if (isBitSet(nonDefaultFields, fieldIndex++)) {
+ mExpandedState = parcel.readInt();
+ }
mSealed = sealed;
}
@@ -5249,6 +5370,26 @@
}
}
+ private static String getExpandedStateSymbolicName(int state) {
+ if (Flags.a11yExpansionStateApi()) {
+ switch (state) {
+ case EXPANDED_STATE_UNDEFINED:
+ return "EXPANDED_STATE_UNDEFINED";
+ case EXPANDED_STATE_COLLAPSED:
+ return "EXPANDED_STATE_COLLAPSED";
+ case EXPANDED_STATE_PARTIAL:
+ return "EXPANDED_STATE_PARTIAL";
+ case EXPANDED_STATE_FULL:
+ return "EXPANDED_STATE_FULL";
+ default:
+ throw new IllegalArgumentException("Unknown expanded state: " + state);
+ }
+ } else {
+ // TODO(b/362782158) Remove when flag is removed.
+ return "";
+ }
+ }
+
private static boolean canPerformRequestOverConnection(int connectionId,
int windowId, long accessibilityNodeId) {
final boolean hasWindowId = windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
@@ -5346,6 +5487,7 @@
builder.append("; containerTitle: ").append(mContainerTitle);
builder.append("; viewIdResName: ").append(mViewIdResourceName);
builder.append("; uniqueId: ").append(mUniqueId);
+ builder.append("; expandedState: ").append(getExpandedStateSymbolicName(mExpandedState));
builder.append("; checkable: ").append(isCheckable());
builder.append("; checked: ").append(isChecked());
diff --git a/core/java/android/window/BackEvent.java b/core/java/android/window/BackEvent.java
index 1b9235b..89b38d8 100644
--- a/core/java/android/window/BackEvent.java
+++ b/core/java/android/window/BackEvent.java
@@ -23,7 +23,6 @@
import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntDef;
-import android.util.TimeUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -76,7 +75,7 @@
}
/**
- * Creates a new {@link BackEvent} instance with the current uptime as frame time.
+ * Creates a new {@link BackEvent} instance with a frame time of 0.
*
* @param touchX Absolute X location of the touch point of this event.
* @param touchY Absolute Y location of the touch point of this event.
@@ -88,7 +87,7 @@
mTouchY = touchY;
mProgress = progress;
mSwipeEdge = swipeEdge;
- mFrameTimeMillis = System.nanoTime() / TimeUtils.NANOS_PER_MS;
+ mFrameTimeMillis = 0;
}
/**
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index b805f5a..c4a9e57 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -362,3 +362,10 @@
description: "Change the default display's windowing mode to freeform when display connected in extended mode."
bug: "374849026"
}
+
+flag {
+ name: "enable_desktop_windowing_pip"
+ namespace: "lse_desktop_experience"
+ description: "Enables PiP features in desktop mode."
+ bug: "350475854"
+}
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 003393c..0af4bea 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -16,13 +16,9 @@
package com.android.internal.jank;
-import static android.view.SurfaceControl.JankData.DISPLAY_HAL;
-import static android.view.SurfaceControl.JankData.JANK_APP_DEADLINE_MISSED;
+import static android.view.SurfaceControl.JankData.JANK_APPLICATION;
+import static android.view.SurfaceControl.JankData.JANK_COMPOSER;
import static android.view.SurfaceControl.JankData.JANK_NONE;
-import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_DEADLINE_MISSED;
-import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED;
-import static android.view.SurfaceControl.JankData.PREDICTION_ERROR;
-import static android.view.SurfaceControl.JankData.SURFACE_FLINGER_SCHEDULING;
import static com.android.internal.jank.DisplayRefreshRate.UNKNOWN_REFRESH_RATE;
import static com.android.internal.jank.DisplayRefreshRate.VARIABLE_REFRESH_RATE;
@@ -181,23 +177,11 @@
case JANK_NONE:
str.append("JANK_NONE");
break;
- case JANK_APP_DEADLINE_MISSED:
- str.append("JANK_APP_DEADLINE_MISSED");
+ case JANK_APPLICATION:
+ str.append("JANK_APPLICATION");
break;
- case JANK_SURFACEFLINGER_DEADLINE_MISSED:
- str.append("JANK_SURFACEFLINGER_DEADLINE_MISSED");
- break;
- case JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED:
- str.append("JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED");
- break;
- case DISPLAY_HAL:
- str.append("DISPLAY_HAL");
- break;
- case PREDICTION_ERROR:
- str.append("PREDICTION_ERROR");
- break;
- case SURFACE_FLINGER_SCHEDULING:
- str.append("SURFACE_FLINGER_SCHEDULING");
+ case JANK_COMPOSER:
+ str.append("JANK_COMPOSER");
break;
default:
str.append("UNKNOWN: ").append(jankType);
@@ -628,16 +612,12 @@
if (info.surfaceControlCallbackFired) {
totalFramesCount++;
boolean missedFrame = false;
- if ((info.jankType & JANK_APP_DEADLINE_MISSED) != 0) {
+ if ((info.jankType & JANK_APPLICATION) != 0) {
Log.w(TAG, "Missed App frame:" + info + ", CUJ=" + name);
missedAppFramesCount++;
missedFrame = true;
}
- if ((info.jankType & DISPLAY_HAL) != 0
- || (info.jankType & JANK_SURFACEFLINGER_DEADLINE_MISSED) != 0
- || (info.jankType & JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED) != 0
- || (info.jankType & SURFACE_FLINGER_SCHEDULING) != 0
- || (info.jankType & PREDICTION_ERROR) != 0) {
+ if ((info.jankType & JANK_COMPOSER) != 0) {
Log.w(TAG, "Missed SF frame:" + info + ", CUJ=" + name);
missedSfFramesCount++;
missedFrame = true;
diff --git a/core/java/com/android/internal/os/ApplicationSharedMemory.java b/core/java/com/android/internal/os/ApplicationSharedMemory.java
index 84f713e..e6ea29e 100644
--- a/core/java/com/android/internal/os/ApplicationSharedMemory.java
+++ b/core/java/com/android/internal/os/ApplicationSharedMemory.java
@@ -21,6 +21,7 @@
import com.android.internal.annotations.VisibleForTesting;
import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
import libcore.io.IoUtils;
@@ -293,4 +294,34 @@
throw new IllegalStateException("Not mutable");
}
}
+
+ /**
+ * Return true if the memory has been mapped. This never throws.
+ */
+ public boolean isMapped() {
+ return mPtr != 0;
+ }
+
+ /**
+ * Return true if the memory is mapped and mutable. This never throws. Note that it returns
+ * false if the memory is not mapped.
+ */
+ public boolean isMutable() {
+ return isMapped() && mMutable;
+ }
+
+ /**
+ * Provide access to the nonce block needed by {@link PropertyInvalidatedCache}. This method
+ * returns 0 if the shared memory is not (yet) mapped.
+ */
+ public long getSystemNonceBlock() {
+ return isMapped() ? nativeGetSystemNonceBlock(mPtr) : 0;
+ }
+
+ /**
+ * Return a pointer to the system nonce cache in the shared memory region. The method is
+ * idempotent.
+ */
+ @FastNative
+ private static native long nativeGetSystemNonceBlock(long ptr);
}
diff --git a/core/java/com/android/internal/os/flags.aconfig b/core/java/com/android/internal/os/flags.aconfig
index 07df248..25a9fbc 100644
--- a/core/java/com/android/internal/os/flags.aconfig
+++ b/core/java/com/android/internal/os/flags.aconfig
@@ -2,6 +2,48 @@
container: "system"
flag {
+ namespace: "ravenwood"
+ name: "ravenwood_flag_rw_1"
+ description: "Ravenwood test RW flag 1"
+ bug: "311370221"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "ravenwood"
+ name: "ravenwood_flag_rw_2"
+ description: "Ravenwood test RW flag 2"
+ bug: "311370221"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "ravenwood"
+ name: "ravenwood_flag_ro_1"
+ description: "Ravenwood test RO flag 1"
+ is_fixed_read_only: true
+ bug: "311370221"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "ravenwood"
+ name: "ravenwood_flag_ro_2"
+ description: "Ravenwood test RO flag 2"
+ is_fixed_read_only: true
+ bug: "311370221"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "enable_apache_http_legacy_preload"
namespace: "system_performance"
description: "Enables zygote preload of non-BCP org.apache.http.legacy.jar library."
diff --git a/core/java/com/android/internal/widget/NotificationProgressBar.java b/core/java/com/android/internal/widget/NotificationProgressBar.java
index d3b1f97..f2b36c3 100644
--- a/core/java/com/android/internal/widget/NotificationProgressBar.java
+++ b/core/java/com/android/internal/widget/NotificationProgressBar.java
@@ -21,6 +21,10 @@
import android.app.Notification.ProgressStyle;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.graphics.drawable.LayerDrawable;
@@ -52,7 +56,7 @@
* represent Notification ProgressStyle progress, such as for ridesharing and navigation.
*/
@RemoteViews.RemoteView
-public class NotificationProgressBar extends ProgressBar {
+public final class NotificationProgressBar extends ProgressBar {
private static final String TAG = "NotificationProgressBar";
private NotificationProgressModel mProgressModel;
@@ -61,7 +65,12 @@
private List<Part> mProgressDrawableParts = null;
@Nullable
- private Drawable mProgressTrackerDrawable = null;
+ private Drawable mTracker = null;
+ private final int mTrackerHeight;
+ private int mTrackerWidth;
+ private int mTrackerPos;
+ private final Matrix mMatrix = new Matrix();
+ private Matrix mTrackerDrawMatrix = null;
public NotificationProgressBar(Context context) {
this(context, null);
@@ -78,28 +87,49 @@
public NotificationProgressBar(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+
+ final TypedArray a = context.obtainStyledAttributes(
+ attrs, R.styleable.NotificationProgressBar, defStyleAttr, defStyleRes);
+ saveAttributeDataForStyleable(context, R.styleable.NotificationProgressBar, attrs, a,
+ defStyleAttr,
+ defStyleRes);
+
+ // Supports setting the tracker in xml, but ProgressStyle notifications set/override it
+ // via {@code setProgressTrackerIcon}.
+ final Drawable tracker = a.getDrawable(R.styleable.NotificationProgressBar_tracker);
+ setTracker(tracker);
+
+ // If this is configured to be non-zero, will scale the tracker drawable and ensure its
+ // aspect ration is between 2:1 to 1:2.
+ mTrackerHeight = a.getDimensionPixelSize(R.styleable.NotificationProgressBar_trackerHeight,
+ 0);
}
/**
* Setter for the notification progress model.
*
* @see NotificationProgressModel#fromBundle
- * @see #setProgressModelAsync
*/
- @RemotableViewMethod(asyncImpl = "setProgressModelAsync")
+ @RemotableViewMethod
public void setProgressModel(@Nullable Bundle bundle) {
Preconditions.checkArgument(bundle != null,
"Bundle shouldn't be null");
mProgressModel = NotificationProgressModel.fromBundle(bundle);
+ final boolean isIndeterminate = mProgressModel.isIndeterminate();
+ setIndeterminate(isIndeterminate);
- if (mProgressModel.isIndeterminate()) {
+ if (isIndeterminate) {
final int indeterminateColor = mProgressModel.getIndeterminateColor();
setIndeterminateTintList(ColorStateList.valueOf(indeterminateColor));
} else {
+ final int progress = mProgressModel.getProgress();
+ final int progressMax = mProgressModel.getProgressMax();
mProgressDrawableParts = processAndConvertToDrawableParts(mProgressModel.getSegments(),
mProgressModel.getPoints(),
- mProgressModel.getProgress(), mProgressModel.isStyledByProgress());
+ progress,
+ progressMax,
+ mProgressModel.isStyledByProgress());
try {
final NotificationProgressDrawable drawable = getNotificationProgressDrawable();
@@ -107,6 +137,9 @@
} catch (IllegalStateException ex) {
Log.e(TAG, "Can't set parts because can't get NotificationProgressDrawable", ex);
}
+
+ setMax(progressMax);
+ setProgress(progress);
}
}
@@ -137,6 +170,13 @@
*/
@RemotableViewMethod(asyncImpl = "setProgressTrackerIconAsync")
public void setProgressTrackerIcon(@Nullable Icon icon) {
+ final Drawable progressTrackerDrawable;
+ if (icon != null) {
+ progressTrackerDrawable = icon.loadDrawable(getContext());
+ } else {
+ progressTrackerDrawable = null;
+ }
+ setTracker(progressTrackerDrawable);
}
/**
@@ -150,12 +190,295 @@
progressTrackerDrawable = null;
}
return () -> {
- setProgressTrackerDrawable(progressTrackerDrawable);
+ setTracker(progressTrackerDrawable);
};
}
- private void setProgressTrackerDrawable(@Nullable Drawable drawable) {
- mProgressTrackerDrawable = drawable;
+ private void setTracker(@Nullable Drawable tracker) {
+ if (isIndeterminate() && tracker != null) {
+ return;
+ }
+
+ final boolean needUpdate = mTracker != null && tracker != mTracker;
+ if (needUpdate) {
+ mTracker.setCallback(null);
+ }
+
+ if (tracker != null) {
+ tracker.setCallback(this);
+ if (getMirrorForRtl()) {
+ tracker.setAutoMirrored(true);
+ }
+
+ if (canResolveLayoutDirection()) {
+ tracker.setLayoutDirection(getLayoutDirection());
+ }
+
+ // If we're updating get the new states
+ if (needUpdate && (tracker.getIntrinsicWidth() != mTracker.getIntrinsicWidth()
+ || tracker.getIntrinsicHeight() != mTracker.getIntrinsicHeight())) {
+ requestLayout();
+ }
+ }
+
+ mTracker = tracker;
+
+ configureTrackerBounds();
+
+ invalidate();
+
+ if (needUpdate) {
+ updateTrackerAndBarPos(getWidth(), getHeight());
+ if (tracker != null && tracker.isStateful()) {
+ // Note that if the states are different this won't work.
+ // For now, let's consider that an app bug.
+ tracker.setState(getDrawableState());
+ }
+ }
+ }
+
+ private void configureTrackerBounds() {
+ // Reset the tracker draw matrix to null
+ mTrackerDrawMatrix = null;
+
+ if (mTracker == null || mTrackerHeight <= 0) {
+ return;
+ }
+
+ final int dWidth = mTracker.getIntrinsicWidth();
+ final int dHeight = mTracker.getIntrinsicHeight();
+ if (dWidth <= 0 || dHeight <= 0) {
+ return;
+ }
+ final int maxDWidth = dHeight * 2;
+ final int maxDHeight = dWidth * 2;
+
+ mTrackerDrawMatrix = mMatrix;
+ float scale;
+ float dx = 0, dy = 0;
+
+ if (dWidth > maxDWidth) {
+ scale = (float) mTrackerHeight / (float) dHeight;
+ dx = (maxDWidth * scale - dWidth * scale) * 0.5f;
+ mTrackerWidth = (int) (maxDWidth * scale);
+ } else if (dHeight > maxDHeight) {
+ scale = (float) mTrackerHeight * 0.5f / (float) dWidth;
+ dy = (maxDHeight * scale - dHeight * scale) * 0.5f;
+ mTrackerWidth = mTrackerHeight / 2;
+ } else {
+ scale = (float) mTrackerHeight / (float) dHeight;
+ mTrackerWidth = (int) (dWidth * scale);
+ }
+
+ mTrackerDrawMatrix.setScale(scale, scale);
+ mTrackerDrawMatrix.postTranslate(Math.round(dx), Math.round(dy));
+ }
+
+ @Override
+ @RemotableViewMethod
+ public synchronized void setIndeterminate(boolean indeterminate) {
+ super.setIndeterminate(indeterminate);
+
+ if (isIndeterminate()) {
+ setTracker(null);
+ }
+ }
+
+ @Override
+ protected boolean verifyDrawable(@NonNull Drawable who) {
+ return who == mTracker || super.verifyDrawable(who);
+ }
+
+ @Override
+ public void jumpDrawablesToCurrentState() {
+ super.jumpDrawablesToCurrentState();
+
+ if (mTracker != null) {
+ mTracker.jumpToCurrentState();
+ }
+ }
+
+ @Override
+ protected void drawableStateChanged() {
+ super.drawableStateChanged();
+
+ final Drawable tracker = mTracker;
+ if (tracker != null && tracker.isStateful()
+ && tracker.setState(getDrawableState())) {
+ invalidateDrawable(tracker);
+ }
+ }
+
+ @Override
+ public void drawableHotspotChanged(float x, float y) {
+ super.drawableHotspotChanged(x, y);
+
+ if (mTracker != null) {
+ mTracker.setHotspot(x, y);
+ }
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ updateTrackerAndBarPos(w, h);
+ }
+
+ private void updateTrackerAndBarPos(int w, int h) {
+ final int paddedHeight = h - mPaddingTop - mPaddingBottom;
+ final Drawable bar = getCurrentDrawable();
+ final Drawable tracker = mTracker;
+
+ // The max height does not incorporate padding, whereas the height
+ // parameter does.
+ final int barHeight = Math.min(getMaxHeight(), paddedHeight);
+ final int trackerHeight = tracker == null ? 0
+ : ((mTrackerHeight == 0) ? tracker.getIntrinsicHeight() : mTrackerHeight);
+
+ // Apply offset to whichever item is taller.
+ final int barOffsetY;
+ final int trackerOffsetY;
+ if (trackerHeight > barHeight) {
+ final int offsetHeight = (paddedHeight - trackerHeight) / 2;
+ barOffsetY = offsetHeight + (trackerHeight - barHeight) / 2;
+ trackerOffsetY = offsetHeight;
+ } else {
+ final int offsetHeight = (paddedHeight - barHeight) / 2;
+ barOffsetY = offsetHeight;
+ trackerOffsetY = offsetHeight + (barHeight - trackerHeight) / 2;
+ }
+
+ if (bar != null) {
+ final int barWidth = w - mPaddingRight - mPaddingLeft;
+ bar.setBounds(0, barOffsetY, barWidth, barOffsetY + barHeight);
+ }
+
+ if (tracker != null) {
+ setTrackerPos(w, tracker, getScale(), trackerOffsetY);
+ }
+ }
+
+ private float getScale() {
+ int min = getMin();
+ int max = getMax();
+ int range = max - min;
+ return range > 0 ? (getProgress() - min) / (float) range : 0;
+ }
+
+ /**
+ * Updates the tracker drawable bounds.
+ *
+ * @param w Width of the view, including padding
+ * @param tracker Drawable used for the tracker
+ * @param scale Current progress between 0 and 1
+ * @param offsetY Vertical offset for centering. If set to
+ * {@link Integer#MIN_VALUE}, the current offset will be used.
+ */
+ private void setTrackerPos(int w, Drawable tracker, float scale, int offsetY) {
+ int available = w - mPaddingLeft - mPaddingRight;
+ final int trackerWidth = tracker.getIntrinsicWidth();
+ final int trackerHeight = tracker.getIntrinsicHeight();
+ available -= ((mTrackerHeight == 0) ? trackerWidth : mTrackerWidth);
+
+ final int trackerPos = (int) (scale * available + 0.5f);
+
+ final int top, bottom;
+ if (offsetY == Integer.MIN_VALUE) {
+ final Rect oldBounds = tracker.getBounds();
+ top = oldBounds.top;
+ bottom = oldBounds.bottom;
+ } else {
+ top = offsetY;
+ bottom = offsetY + trackerHeight;
+ }
+
+ mTrackerPos = (isLayoutRtl() && getMirrorForRtl()) ? available - trackerPos : trackerPos;
+ final int left = 0;
+ final int right = left + trackerWidth;
+
+ final Drawable background = getBackground();
+ if (background != null) {
+ final int bkgOffsetX = mPaddingLeft;
+ final int bkgOffsetY = mPaddingTop;
+ background.setHotspotBounds(left + bkgOffsetX, top + bkgOffsetY,
+ right + bkgOffsetX, bottom + bkgOffsetY);
+ }
+
+ // Canvas will be translated, so 0,0 is where we start drawing
+ tracker.setBounds(left, top, right, bottom);
+ }
+
+ @Override
+ public void onResolveDrawables(int layoutDirection) {
+ super.onResolveDrawables(layoutDirection);
+
+ if (mTracker != null) {
+ mTracker.setLayoutDirection(layoutDirection);
+ }
+ }
+
+ @Override
+ protected synchronized void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ drawTracker(canvas);
+ }
+
+ /**
+ * Draw the tracker.
+ */
+ private void drawTracker(Canvas canvas) {
+ if (mTracker != null) {
+ final int saveCount = canvas.save();
+ // Translate the canvas origin to tracker position to make the draw matrix and the RtL
+ // transformations work.
+ canvas.translate(mPaddingLeft + mTrackerPos, mPaddingTop);
+ canvas.clipRect(0, 0, mTrackerWidth, mTrackerHeight);
+ if (mTrackerDrawMatrix != null) {
+ canvas.concat(mTrackerDrawMatrix);
+ }
+ mTracker.draw(canvas);
+ canvas.restoreToCount(saveCount);
+ }
+ }
+
+ @Override
+ protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ Drawable d = getCurrentDrawable();
+
+ int trackerHeight = mTracker == null ? 0 : mTracker.getIntrinsicHeight();
+ int dw = 0;
+ int dh = 0;
+ if (d != null) {
+ dw = Math.max(getMinWidth(), Math.min(getMaxWidth(), d.getIntrinsicWidth()));
+ dh = Math.max(getMinHeight(), Math.min(getMaxHeight(), d.getIntrinsicHeight()));
+ dh = Math.max(trackerHeight, dh);
+ }
+ dw += mPaddingLeft + mPaddingRight;
+ dh += mPaddingTop + mPaddingBottom;
+
+ setMeasuredDimension(resolveSizeAndState(dw, widthMeasureSpec, 0),
+ resolveSizeAndState(dh, heightMeasureSpec, 0));
+ }
+
+ @Override
+ public CharSequence getAccessibilityClassName() {
+ return NotificationProgressBar.class.getName();
+ }
+
+ @Override
+ public void onRtlPropertiesChanged(int layoutDirection) {
+ super.onRtlPropertiesChanged(layoutDirection);
+
+ final Drawable tracker = mTracker;
+ if (tracker != null) {
+ setTrackerPos(getWidth(), tracker, getScale(), Integer.MIN_VALUE);
+
+ // Since we draw translated, the drawable's bounds that it signals
+ // for invalidation won't be the actual bounds we want invalidated,
+ // so just invalidate this whole view.
+ invalidate();
+ }
}
/**
@@ -167,12 +490,18 @@
List<ProgressStyle.Segment> segments,
List<ProgressStyle.Point> points,
int progress,
+ int progressMax,
boolean isStyledByProgress
) {
if (segments.isEmpty()) {
throw new IllegalArgumentException("List of segments shouldn't be empty");
}
+ final int totalLength = segments.stream().mapToInt(ProgressStyle.Segment::getLength).sum();
+ if (progressMax != totalLength) {
+ throw new IllegalArgumentException("Invalid progressMax : " + progressMax);
+ }
+
for (ProgressStyle.Segment segment : segments) {
final int length = segment.getLength();
if (length <= 0) {
@@ -180,14 +509,12 @@
}
}
- final int progressMax = segments.stream().mapToInt(ProgressStyle.Segment::getLength).sum();
-
if (progress < 0 || progress > progressMax) {
throw new IllegalArgumentException("Invalid progress : " + progress);
}
for (ProgressStyle.Point point : points) {
final int pos = point.getPosition();
- if (pos <= 0 || pos >= progressMax) {
+ if (pos < 0 || pos > progressMax) {
throw new IllegalArgumentException("Invalid Point position : " + pos);
}
}
@@ -208,7 +535,6 @@
isStyledByProgress);
}
-
// Any segment with a point on it gets split by the point.
// If isStyledByProgress is true, also split the segment with the progress value in its range.
private static Map<Integer, ProgressStyle.Segment> splitSegmentsByPointsAndProgress(
diff --git a/core/java/com/android/internal/widget/NotificationProgressModel.java b/core/java/com/android/internal/widget/NotificationProgressModel.java
index e51ea99..e8cb37e 100644
--- a/core/java/com/android/internal/widget/NotificationProgressModel.java
+++ b/core/java/com/android/internal/widget/NotificationProgressModel.java
@@ -96,6 +96,10 @@
return mProgress;
}
+ public int getProgressMax() {
+ return mSegments.stream().mapToInt(Notification.ProgressStyle.Segment::getLength).sum();
+ }
+
public boolean isStyledByProgress() {
return mIsStyledByProgress;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/CompanionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/CompanionOperation.java
index deae9a5..244bb3d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CompanionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CompanionOperation.java
@@ -17,15 +17,13 @@
import java.util.List;
-/**
- * Interface for the companion operations
- */
+/** Interface for the companion operations */
public interface CompanionOperation {
/**
* Read, create and add instance to operations
+ *
* @param buffer data to read to create operation
* @param operations command is to be added
*/
void read(WireBuffer buffer, List<Operation> operations);
}
-
diff --git a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
index 18be480..212df02 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
@@ -16,6 +16,7 @@
package com.android.internal.widget.remotecompose.core;
import com.android.internal.widget.remotecompose.core.operations.ComponentValue;
+import com.android.internal.widget.remotecompose.core.operations.IntegerExpression;
import com.android.internal.widget.remotecompose.core.operations.NamedVariable;
import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
import com.android.internal.widget.remotecompose.core.operations.Theme;
@@ -25,6 +26,8 @@
import com.android.internal.widget.remotecompose.core.operations.layout.ComponentEnd;
import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStartOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
+import com.android.internal.widget.remotecompose.core.operations.layout.LoopEnd;
+import com.android.internal.widget.remotecompose.core.operations.layout.LoopOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentModifiers;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ModifierOperation;
@@ -33,11 +36,12 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
/**
- * Represents a platform independent RemoteCompose document,
- * containing RemoteCompose operations + state
+ * Represents a platform independent RemoteCompose document, containing RemoteCompose operations +
+ * state
*/
public class CoreDocument {
@@ -66,6 +70,8 @@
RemoteComposeBuffer mBuffer = new RemoteComposeBuffer(mRemoteComposeState);
+ private final HashMap<Long, IntegerExpression> mIntegerExpressions = new HashMap<>();
+
private int mLastId = 1; // last component id when inflating the file
public String getContentDescription() {
@@ -133,21 +139,14 @@
/**
* Sets the way the player handles the content
*
- * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
+ * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
* @param alignment set the alignment of the content (TOP|CENTER|BOTTOM|START|END)
- * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
- * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes
- * the LAYOUT modes are:
- * - LAYOUT_MATCH_PARENT
- * - LAYOUT_WRAP_CONTENT
- * or adding an horizontal mode and a vertical mode:
- * - LAYOUT_HORIZONTAL_MATCH_PARENT
- * - LAYOUT_HORIZONTAL_WRAP_CONTENT
- * - LAYOUT_HORIZONTAL_FIXED
- * - LAYOUT_VERTICAL_MATCH_PARENT
- * - LAYOUT_VERTICAL_WRAP_CONTENT
- * - LAYOUT_VERTICAL_FIXED
- * The LAYOUT_*_FIXED modes will use the intrinsic document size
+ * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
+ * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes the LAYOUT modes are:
+ * - LAYOUT_MATCH_PARENT - LAYOUT_WRAP_CONTENT or adding an horizontal mode and a vertical
+ * mode: - LAYOUT_HORIZONTAL_MATCH_PARENT - LAYOUT_HORIZONTAL_WRAP_CONTENT -
+ * LAYOUT_HORIZONTAL_FIXED - LAYOUT_VERTICAL_MATCH_PARENT - LAYOUT_VERTICAL_WRAP_CONTENT -
+ * LAYOUT_VERTICAL_FIXED The LAYOUT_*_FIXED modes will use the intrinsic document size
*/
public void setRootContentBehavior(int scroll, int alignment, int sizing, int mode) {
this.mContentScroll = scroll;
@@ -160,8 +159,8 @@
* Given dimensions w x h of where to paint the content, returns the corresponding scale factor
* according to the contentSizing information
*
- * @param w horizontal dimension of the rendering area
- * @param h vertical dimension of the rendering area
+ * @param w horizontal dimension of the rendering area
+ * @param h vertical dimension of the rendering area
* @param scaleOutput will contain the computed scale factor
*/
public void computeScale(float w, float h, float[] scaleOutput) {
@@ -169,50 +168,47 @@
float contentScaleY = 1f;
if (mContentSizing == RootContentBehavior.SIZING_SCALE) {
// we need to add canvas transforms ops here
+ float scaleX = 1f;
+ float scaleY = 1f;
+ float scale = 1f;
switch (mContentMode) {
- case RootContentBehavior.SCALE_INSIDE: {
- float scaleX = w / mWidth;
- float scaleY = h / mHeight;
- float scale = Math.min(1f, Math.min(scaleX, scaleY));
+ case RootContentBehavior.SCALE_INSIDE:
+ scaleX = w / mWidth;
+ scaleY = h / mHeight;
+ scale = Math.min(1f, Math.min(scaleX, scaleY));
contentScaleX = scale;
contentScaleY = scale;
- }
- break;
- case RootContentBehavior.SCALE_FIT: {
- float scaleX = w / mWidth;
- float scaleY = h / mHeight;
- float scale = Math.min(scaleX, scaleY);
+ break;
+ case RootContentBehavior.SCALE_FIT:
+ scaleX = w / mWidth;
+ scaleY = h / mHeight;
+ scale = Math.min(scaleX, scaleY);
contentScaleX = scale;
contentScaleY = scale;
- }
- break;
- case RootContentBehavior.SCALE_FILL_WIDTH: {
- float scale = w / mWidth;
+ break;
+ case RootContentBehavior.SCALE_FILL_WIDTH:
+ scale = w / mWidth;
contentScaleX = scale;
contentScaleY = scale;
- }
- break;
- case RootContentBehavior.SCALE_FILL_HEIGHT: {
- float scale = h / mHeight;
+ break;
+ case RootContentBehavior.SCALE_FILL_HEIGHT:
+ scale = h / mHeight;
contentScaleX = scale;
contentScaleY = scale;
- }
- break;
- case RootContentBehavior.SCALE_CROP: {
- float scaleX = w / mWidth;
- float scaleY = h / mHeight;
- float scale = Math.max(scaleX, scaleY);
+ break;
+ case RootContentBehavior.SCALE_CROP:
+ scaleX = w / mWidth;
+ scaleY = h / mHeight;
+ scale = Math.max(scaleX, scaleY);
contentScaleX = scale;
contentScaleY = scale;
- }
- break;
- case RootContentBehavior.SCALE_FILL_BOUNDS: {
- float scaleX = w / mWidth;
- float scaleY = h / mHeight;
+ break;
+ case RootContentBehavior.SCALE_FILL_BOUNDS:
+ scaleX = w / mWidth;
+ scaleY = h / mHeight;
contentScaleX = scaleX;
contentScaleY = scaleY;
- }
- break;
+ break;
default:
// nothing
}
@@ -225,14 +221,14 @@
* Given dimensions w x h of where to paint the content, returns the corresponding translation
* according to the contentAlignment information
*
- * @param w horizontal dimension of the rendering area
- * @param h vertical dimension of the rendering area
- * @param contentScaleX the horizontal scale we are going to use for the content
- * @param contentScaleY the vertical scale we are going to use for the content
+ * @param w horizontal dimension of the rendering area
+ * @param h vertical dimension of the rendering area
+ * @param contentScaleX the horizontal scale we are going to use for the content
+ * @param contentScaleY the vertical scale we are going to use for the content
* @param translateOutput will contain the computed translation
*/
- private void computeTranslate(float w, float h, float contentScaleX, float contentScaleY,
- float[] translateOutput) {
+ private void computeTranslate(
+ float w, float h, float contentScaleX, float contentScaleY, float[] translateOutput) {
int horizontalContentAlignment = mContentAlignment & 0xF0;
int verticalContentAlignment = mContentAlignment & 0xF;
float translateX = 0f;
@@ -241,34 +237,28 @@
float contentHeight = mHeight * contentScaleY;
switch (horizontalContentAlignment) {
- case RootContentBehavior.ALIGNMENT_START: {
+ case RootContentBehavior.ALIGNMENT_START:
// nothing
- }
- break;
- case RootContentBehavior.ALIGNMENT_HORIZONTAL_CENTER: {
+ break;
+ case RootContentBehavior.ALIGNMENT_HORIZONTAL_CENTER:
translateX = (w - contentWidth) / 2f;
- }
- break;
- case RootContentBehavior.ALIGNMENT_END: {
+ break;
+ case RootContentBehavior.ALIGNMENT_END:
translateX = w - contentWidth;
- }
- break;
+ break;
default:
// nothing (same as alignment_start)
}
switch (verticalContentAlignment) {
- case RootContentBehavior.ALIGNMENT_TOP: {
+ case RootContentBehavior.ALIGNMENT_TOP:
// nothing
- }
- break;
- case RootContentBehavior.ALIGNMENT_VERTICAL_CENTER: {
+ break;
+ case RootContentBehavior.ALIGNMENT_VERTICAL_CENTER:
translateY = (h - contentHeight) / 2f;
- }
- break;
- case RootContentBehavior.ALIGNMENT_BOTTOM: {
+ break;
+ case RootContentBehavior.ALIGNMENT_BOTTOM:
translateY = h - contentHeight;
- }
- break;
+ break;
default:
// nothing (same as alignment_top)
}
@@ -279,6 +269,7 @@
/**
* Returns the list of click areas
+ *
* @return list of click areas in document coordinates
*/
public Set<ClickAreaRepresentation> getClickAreas() {
@@ -287,15 +278,14 @@
/**
* Returns the root layout component
+ *
* @return returns the root component if it exists, null otherwise
*/
public RootLayoutComponent getRootLayoutComponent() {
return mRootLayoutComponent;
}
- /**
- * Invalidate the document for layout measures. This will trigger a layout remeasure pass.
- */
+ /** Invalidate the document for layout measures. This will trigger a layout remeasure pass. */
public void invalidateMeasure() {
if (mRootLayoutComponent != null) {
mRootLayoutComponent.invalidateMeasure();
@@ -304,6 +294,7 @@
/**
* Returns the component with the given id
+ *
* @param id component id
* @return the component if it exists, null otherwise
*/
@@ -332,8 +323,21 @@
}
/**
- * Callback interface for host actions
+ * Execute an integer expression with the given id and put its value on the targetId
+ *
+ * @param expressionId the id of the integer expression
+ * @param targetId the id of the value to update with the expression
+ * @param context the current context
*/
+ public void evaluateIntExpression(long expressionId, int targetId, RemoteContext context) {
+ IntegerExpression expression = mIntegerExpressions.get(expressionId);
+ if (expression != null) {
+ int v = expression.evaluate(context);
+ context.overrideInteger(targetId, v);
+ }
+ }
+
+ /** Callback interface for host actions */
public interface ActionCallback {
// TODO: add payload support
void onAction(String name);
@@ -343,6 +347,7 @@
/**
* Warn action listeners for the given named action
+ *
* @param name the action name
*/
public void runNamedAction(String name) {
@@ -360,9 +365,7 @@
mActionListeners.add(callback);
}
- /**
- * Clear existing callbacks for named host actions
- */
+ /** Clear existing callbacks for named host actions */
public void clearActionCallbacks() {
mActionListeners.clear();
}
@@ -395,13 +398,14 @@
float mBottom;
String mMetadata;
- public ClickAreaRepresentation(int id,
- String contentDescription,
- float left,
- float top,
- float right,
- float bottom,
- String metadata) {
+ public ClickAreaRepresentation(
+ int id,
+ String contentDescription,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ String metadata) {
this.mId = id;
this.mContentDescription = contentDescription;
this.mLeft = left;
@@ -419,8 +423,7 @@
* @return x, y coordinate is within bounds
*/
public boolean contains(float x, float y) {
- return x >= mLeft && x < mRight
- && y >= mTop && y < mBottom;
+ return x >= mLeft && x < mRight && y >= mTop && y < mBottom;
}
public float getLeft() {
@@ -452,12 +455,16 @@
}
}
- /**
- * Load operations from the given buffer
- */
+ /** Load operations from the given buffer */
public void initFromBuffer(RemoteComposeBuffer buffer) {
mOperations = new ArrayList<Operation>();
buffer.inflateFromBuffer(mOperations);
+ for (Operation op : mOperations) {
+ if (op instanceof IntegerExpression) {
+ IntegerExpression expression = (IntegerExpression) op;
+ mIntegerExpressions.put((long) expression.mId, expression);
+ }
+ }
mOperations = inflateComponents(mOperations);
mBuffer = buffer;
for (Operation op : mOperations) {
@@ -473,6 +480,7 @@
/**
* Inflate a component tree
+ *
* @param operations flat list of operations
* @return nested list of operations / components
*/
@@ -482,6 +490,7 @@
ArrayList<Operation> finalOperationsList = new ArrayList<>();
ArrayList<Operation> ops = finalOperationsList;
ClickModifierOperation currentClickModifier = null;
+ LoopOperation currentLoop = null;
mLastId = -1;
for (Operation o : operations) {
@@ -509,11 +518,22 @@
} else if (o instanceof ClickModifierOperation) {
// TODO: refactor to add container <- component...
currentClickModifier = (ClickModifierOperation) o;
- ops = ((ClickModifierOperation) o).getList();
+ ops = currentClickModifier.getList();
} else if (o instanceof ClickModifierEnd) {
ops = currentComponent.getList();
ops.add(currentClickModifier);
currentClickModifier = null;
+ } else if (o instanceof LoopOperation) {
+ currentLoop = (LoopOperation) o;
+ ops = currentLoop.getList();
+ } else if (o instanceof LoopEnd) {
+ if (currentComponent != null) {
+ ops = currentComponent.getList();
+ ops.add(currentLoop);
+ } else {
+ ops = finalOperationsList;
+ }
+ currentLoop = null;
} else {
ops.add(o);
}
@@ -555,8 +575,8 @@
}
/**
- * Called when an initialization is needed, allowing the document to eg load
- * resources / cache them.
+ * Called when an initialization is needed, allowing the document to eg load resources / cache
+ * them.
*/
public void initializeContext(RemoteContext context) {
mRemoteComposeState.reset();
@@ -593,7 +613,7 @@
*
* @param majorVersion major version number, increased upon changes breaking the compatibility
* @param minorVersion minor version number, increased when adding new features
- * @param patch patch level, increased upon bugfixes
+ * @param patch patch level, increased upon bugfixes
*/
void setVersion(int majorVersion, int minorVersion, int patch) {
mVersion = new Version(majorVersion, minorVersion, patch);
@@ -605,22 +625,29 @@
/**
* Add a click area to the document, in root coordinates. We are not doing any specific sorting
- * through the declared areas on click detections, which means that the first one containing
- * the click coordinates will be the one reported; the order of addition of those click areas
- * is therefore meaningful.
+ * through the declared areas on click detections, which means that the first one containing the
+ * click coordinates will be the one reported; the order of addition of those click areas is
+ * therefore meaningful.
*
- * @param id the id of the area, which will be reported on click
+ * @param id the id of the area, which will be reported on click
* @param contentDescription the content description (used for accessibility)
- * @param left the left coordinate of the click area (in pixels)
- * @param top the top coordinate of the click area (in pixels)
- * @param right the right coordinate of the click area (in pixels)
- * @param bottom the bottom coordinate of the click area (in pixels)
- * @param metadata arbitrary metadata associated with the are, also reported on click
+ * @param left the left coordinate of the click area (in pixels)
+ * @param top the top coordinate of the click area (in pixels)
+ * @param right the right coordinate of the click area (in pixels)
+ * @param bottom the bottom coordinate of the click area (in pixels)
+ * @param metadata arbitrary metadata associated with the are, also reported on click
*/
- public void addClickArea(int id, String contentDescription,
- float left, float top, float right, float bottom, String metadata) {
- mClickAreas.add(new ClickAreaRepresentation(id,
- contentDescription, left, top, right, bottom, metadata));
+ public void addClickArea(
+ int id,
+ String contentDescription,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ String metadata) {
+ mClickAreas.add(
+ new ClickAreaRepresentation(
+ id, contentDescription, left, top, right, bottom, metadata));
}
/**
@@ -672,9 +699,7 @@
}
}
- /**
- * Warn click listeners when a click area is activated
- */
+ /** Warn click listeners when a click area is activated */
private void warnClickListeners(ClickAreaRepresentation clickArea) {
for (ClickCallbacks listener : mClickListeners) {
listener.click(clickArea.mId, clickArea.mMetadata);
@@ -743,10 +768,9 @@
* Paint the document
*
* @param context the provided PaintContext
- * @param theme the theme we want to use for this document.
+ * @param theme the theme we want to use for this document.
*/
public void paint(RemoteContext context, int theme) {
- long time = System.nanoTime();
context.getPaintContext().clearNeedsRepaint();
context.mMode = RemoteContext.ContextMode.UNSET;
@@ -760,8 +784,12 @@
if (mContentSizing == RootContentBehavior.SIZING_SCALE) {
// we need to add canvas transforms ops here
computeScale(context.mWidth, context.mHeight, mScaleOutput);
- computeTranslate(context.mWidth, context.mHeight,
- mScaleOutput[0], mScaleOutput[1], mTranslateOutput);
+ computeTranslate(
+ context.mWidth,
+ context.mHeight,
+ mScaleOutput[0],
+ mScaleOutput[1],
+ mTranslateOutput);
context.mPaintContext.translate(mTranslateOutput[0], mTranslateOutput[1]);
context.mPaintContext.scale(mScaleOutput[0], mScaleOutput[1]);
}
@@ -795,9 +823,10 @@
// or the theme is equal as the one passed in argument to paint.
boolean apply = true;
if (theme != Theme.UNSPECIFIED) {
- apply = op instanceof Theme // always apply a theme setter
- || context.getTheme() == theme
- || context.getTheme() == Theme.UNSPECIFIED;
+ apply =
+ op instanceof Theme // always apply a theme setter
+ || context.getTheme() == theme
+ || context.getTheme() == Theme.UNSPECIFIED;
}
if (apply) {
op.apply(context);
@@ -808,7 +837,10 @@
mRepaintNext = 1;
}
context.mMode = RemoteContext.ContextMode.UNSET;
- // System.out.println(">> " + ( System.nanoTime() - time)*1E-6f+" ms");
+ // System.out.println(">> " + ( System.nanoTime() - time)*1E-6f+" ms");
+ if (DEBUG && mRootLayoutComponent != null) {
+ System.out.println(mRootLayoutComponent.displayHierarchy());
+ }
}
public String[] getStats() {
@@ -831,7 +863,6 @@
if (mOperation instanceof Component) {
Component com = (Component) mOperation;
count += addChildren(com, map, buffer);
-
}
}
@@ -893,5 +924,8 @@
}
}
}
-}
+ public List<Operation> getOperations() {
+ return mOperations;
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/DocumentedCompanion.java b/core/java/com/android/internal/widget/remotecompose/core/DocumentedCompanion.java
index 661cf7c..8a792a4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/DocumentedCompanion.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/DocumentedCompanion.java
@@ -17,6 +17,4 @@
import com.android.internal.widget.remotecompose.core.documentation.DocumentedCompanionOperation;
-public interface DocumentedCompanion extends CompanionOperation , DocumentedCompanionOperation {
-
-}
+public interface DocumentedCompanion extends CompanionOperation, DocumentedCompanionOperation {}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operation.java b/core/java/com/android/internal/widget/remotecompose/core/Operation.java
index 4a8b3d7..9f565a2 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operation.java
@@ -15,14 +15,10 @@
*/
package com.android.internal.widget.remotecompose.core;
-/**
- * Base interface for RemoteCompose operations
- */
+/** Base interface for RemoteCompose operations */
public interface Operation {
- /**
- * add the operation to the buffer
- */
+ /** add the operation to the buffer */
void write(WireBuffer buffer);
/**
@@ -32,8 +28,6 @@
*/
void apply(RemoteContext context);
- /**
- * Debug utility to display an operation + indentation
- */
+ /** Debug utility to display an operation + indentation */
String deepToString(String indent);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operations.java b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
index 4a25b5e..acebe07 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
@@ -25,15 +25,18 @@
import com.android.internal.widget.remotecompose.core.operations.DataListFloat;
import com.android.internal.widget.remotecompose.core.operations.DataListIds;
import com.android.internal.widget.remotecompose.core.operations.DataMapIds;
+import com.android.internal.widget.remotecompose.core.operations.DataMapLookup;
import com.android.internal.widget.remotecompose.core.operations.DrawArc;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmap;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmapInt;
+import com.android.internal.widget.remotecompose.core.operations.DrawBitmapScaled;
import com.android.internal.widget.remotecompose.core.operations.DrawCircle;
import com.android.internal.widget.remotecompose.core.operations.DrawLine;
import com.android.internal.widget.remotecompose.core.operations.DrawOval;
import com.android.internal.widget.remotecompose.core.operations.DrawPath;
import com.android.internal.widget.remotecompose.core.operations.DrawRect;
import com.android.internal.widget.remotecompose.core.operations.DrawRoundRect;
+import com.android.internal.widget.remotecompose.core.operations.DrawSector;
import com.android.internal.widget.remotecompose.core.operations.DrawText;
import com.android.internal.widget.remotecompose.core.operations.DrawTextAnchored;
import com.android.internal.widget.remotecompose.core.operations.DrawTextOnPath;
@@ -56,6 +59,10 @@
import com.android.internal.widget.remotecompose.core.operations.ShaderData;
import com.android.internal.widget.remotecompose.core.operations.TextData;
import com.android.internal.widget.remotecompose.core.operations.TextFromFloat;
+import com.android.internal.widget.remotecompose.core.operations.TextLength;
+import com.android.internal.widget.remotecompose.core.operations.TextLookup;
+import com.android.internal.widget.remotecompose.core.operations.TextLookupInt;
+import com.android.internal.widget.remotecompose.core.operations.TextMeasure;
import com.android.internal.widget.remotecompose.core.operations.TextMerge;
import com.android.internal.widget.remotecompose.core.operations.Theme;
import com.android.internal.widget.remotecompose.core.operations.layout.CanvasContent;
@@ -64,12 +71,15 @@
import com.android.internal.widget.remotecompose.core.operations.layout.ComponentEnd;
import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStart;
import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponentContent;
+import com.android.internal.widget.remotecompose.core.operations.layout.LoopEnd;
+import com.android.internal.widget.remotecompose.core.operations.layout.LoopOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent;
import com.android.internal.widget.remotecompose.core.operations.layout.animation.AnimationSpec;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.BoxLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.CanvasLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.ColumnLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.RowLayout;
+import com.android.internal.widget.remotecompose.core.operations.layout.managers.StateLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.TextLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BackgroundModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BorderModifierOperation;
@@ -81,6 +91,7 @@
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.PaddingModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.RoundedClipRectModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ValueIntegerChangeActionOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ValueIntegerExpressionChangeActionOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ValueStringChangeActionOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap;
@@ -88,9 +99,7 @@
import com.android.internal.widget.remotecompose.core.types.IntegerConstant;
import com.android.internal.widget.remotecompose.core.types.LongConstant;
-/**
- * List of operations supported in a RemoteCompose document
- */
+/** List of operations supported in a RemoteCompose document */
public class Operations {
////////////////////////////////////////
@@ -112,7 +121,7 @@
public static final int DATA_SHADER = 45;
public static final int DATA_TEXT = 102;
- /////////////////////////////=====================
+ ///////////////////////////// =====================
public static final int CLIP_PATH = 38;
public static final int CLIP_RECT = 39;
public static final int PAINT_VALUES = 40;
@@ -121,7 +130,7 @@
public static final int DRAW_CIRCLE = 46;
public static final int DRAW_LINE = 47;
public static final int DRAW_ROUND_RECT = 51;
- public static final int DRAW_ARC = 52;
+ public static final int DRAW_SECTOR = 52;
public static final int DRAW_TEXT_ON_PATH = 53;
public static final int DRAW_OVAL = 56;
public static final int DATA_PATH = 123;
@@ -149,8 +158,15 @@
public static final int ID_LIST = 146;
public static final int FLOAT_LIST = 147;
public static final int DATA_LONG = 148;
+ public static final int DRAW_BITMAP_SCALED = 149;
+ public static final int TEXT_LOOKUP = 151;
+ public static final int DRAW_ARC = 152;
+ public static final int TEXT_LOOKUP_INT = 153;
+ public static final int DATA_MAP_LOOKUP = 154;
+ public static final int TEXT_MEASURE = 155;
+ public static final int TEXT_LENGTH = 156;
- /////////////////////////////////////////======================
+ ///////////////////////////////////////// ======================
////////////////////////////////////////
// Layout commands
@@ -163,10 +179,12 @@
public static final int LAYOUT_COLUMN = 204;
public static final int LAYOUT_CANVAS = 205;
public static final int LAYOUT_CANVAS_CONTENT = 207;
-
public static final int LAYOUT_TEXT = 208;
+ public static final int LAYOUT_STATE = 217;
+
public static final int COMPONENT_START = 2;
public static final int COMPONENT_END = 3;
+
public static final int MODIFIER_WIDTH = 16;
public static final int MODIFIER_HEIGHT = 67;
public static final int MODIFIER_BACKGROUND = 55;
@@ -178,6 +196,8 @@
public static final int MODIFIER_CLICK = 59;
public static final int MODIFIER_CLICK_END = 214;
+ public static final int LOOP_START = 215;
+ public static final int LOOP_END = 216;
public static final int MODIFIER_VISIBILITY = 211;
public static final int HOST_ACTION = 209;
@@ -185,6 +205,7 @@
public static final int VALUE_INTEGER_CHANGE_ACTION = 212;
public static final int VALUE_STRING_CHANGE_ACTION = 213;
+ public static final int VALUE_INTEGER_EXPRESSION_CHANGE_ACTION = 218;
public static final int ANIMATION_SPEC = 14;
@@ -194,7 +215,7 @@
static class UniqueIntMap<T> extends IntMap<T> {
@Override
- public T put(int key, T value) {
+ public T put(int key, T value) {
assert null == get(key) : "Opcode " + key + " already used in Operations !";
return super.put(key, value);
}
@@ -210,7 +231,7 @@
map.put(ROOT_CONTENT_BEHAVIOR, RootContentBehavior::read);
map.put(ROOT_CONTENT_DESCRIPTION, RootContentDescription::read);
- map.put(DRAW_ARC, DrawArc::read);
+ map.put(DRAW_SECTOR, DrawSector::read);
map.put(DRAW_BITMAP, DrawBitmap::read);
map.put(DRAW_CIRCLE, DrawCircle::read);
map.put(DRAW_LINE, DrawLine::read);
@@ -247,6 +268,12 @@
map.put(ID_LIST, DataListIds::read);
map.put(FLOAT_LIST, DataListFloat::read);
map.put(DATA_LONG, LongConstant::read);
+ map.put(DRAW_BITMAP_SCALED, DrawBitmapScaled::read);
+ map.put(TEXT_LOOKUP, TextLookup::read);
+ map.put(TEXT_LOOKUP_INT, TextLookupInt::read);
+
+ map.put(LOOP_START, LoopOperation::read);
+ map.put(LOOP_END, LoopEnd::read);
// Layout
@@ -267,6 +294,9 @@
map.put(HOST_ACTION, HostActionOperation::read);
map.put(HOST_NAMED_ACTION, HostNamedActionOperation::read);
map.put(VALUE_INTEGER_CHANGE_ACTION, ValueIntegerChangeActionOperation::read);
+ map.put(
+ VALUE_INTEGER_EXPRESSION_CHANGE_ACTION,
+ ValueIntegerExpressionChangeActionOperation::read);
map.put(VALUE_STRING_CHANGE_ACTION, ValueStringChangeActionOperation::read);
map.put(LAYOUT_ROOT, RootLayoutComponent::read);
@@ -278,6 +308,12 @@
map.put(LAYOUT_CANVAS_CONTENT, CanvasContent::read);
map.put(LAYOUT_TEXT, TextLayout::read);
+ map.put(LAYOUT_STATE, StateLayout::read);
+
map.put(COMPONENT_VALUE, ComponentValue::read);
+ map.put(DRAW_ARC, DrawArc::read);
+ map.put(DATA_MAP_LOOKUP, DataMapLookup::read);
+ map.put(TEXT_MEASURE, TextMeasure::read);
+ map.put(TEXT_LENGTH, TextLength::read);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
index 4770b12..13d6f78 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
@@ -17,15 +17,14 @@
import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
-/**
- * Specify an abstract paint context used by RemoteCompose commands to draw
- */
+/** Specify an abstract paint context used by RemoteCompose commands to draw */
public abstract class PaintContext {
public static final int TEXT_MEASURE_MONOSPACE_WIDTH = 0x01;
public static final int TEXT_MEASURE_FONT_HEIGHT = 0x02;
protected RemoteContext mContext;
private boolean mNeedsRepaint = false;
+
public RemoteContext getContext() {
return mContext;
}
@@ -46,43 +45,43 @@
this.mContext = context;
}
- /**
- * convenience function to call matrixSave()
- */
+ /** convenience function to call matrixSave() */
public void save() {
matrixSave();
}
- /**
- * convenience function to call matrixRestore()
- */
+ /** convenience function to call matrixRestore() */
public void restore() {
matrixRestore();
}
- /**
- * convenience function to call matrixSave()
- */
+ /** convenience function to call matrixSave() */
public void saveLayer(float x, float y, float width, float height) {
// TODO
matrixSave();
}
- public abstract void drawBitmap(int imageId,
- int srcLeft, int srcTop, int srcRight, int srcBottom,
- int dstLeft, int dstTop, int dstRight, int dstBottom,
- int cdId);
+ public abstract void drawBitmap(
+ int imageId,
+ int srcLeft,
+ int srcTop,
+ int srcRight,
+ int srcBottom,
+ int dstLeft,
+ int dstTop,
+ int dstRight,
+ int dstBottom,
+ int cdId);
public abstract void scale(float scaleX, float scaleY);
public abstract void translate(float translateX, float translateY);
- public abstract void drawArc(float left,
- float top,
- float right,
- float bottom,
- float startAngle,
- float sweepAngle);
+ public abstract void drawArc(
+ float left, float top, float right, float bottom, float startAngle, float sweepAngle);
+
+ public abstract void drawSector(
+ float left, float top, float right, float bottom, float startAngle, float sweepAngle);
public abstract void drawBitmap(int id, float left, float top, float right, float bottom);
@@ -96,43 +95,29 @@
public abstract void drawRect(float left, float top, float right, float bottom);
- /**
- * this caches the paint to a paint stack
- */
- public abstract void savePaint();
+ /** this caches the paint to a paint stack */
+ public abstract void savePaint();
- /**
- * This restores the paint form the paint stack
- */
- public abstract void restorePaint();
+ /** This restores the paint form the paint stack */
+ public abstract void restorePaint();
- public abstract void drawRoundRect(float left,
- float top,
- float right,
- float bottom,
- float radiusX,
- float radiusY);
+ public abstract void drawRoundRect(
+ float left, float top, float right, float bottom, float radiusX, float radiusY);
public abstract void drawTextOnPath(int textId, int pathId, float hOffset, float vOffset);
/**
- * Return the dimensions (left, top, right, bottom).
- * Relative to a drawTextRun x=0, y=0;
+ * Return the dimensions (left, top, right, bottom). Relative to a drawTextRun x=0, y=0;
*
* @param textId
* @param start
- * @param end if end is -1 it means the whole string
- * @param flags how to measure:
- * TEXT_MEASURE_MONOSPACE_WIDTH - measure as a monospace font
- * TEXT_MEASURE_FULL_HEIGHT - measure bounds of the given string using the
- * max ascend and descent of the font (not just of the measured text)
+ * @param end if end is -1 it means the whole string
+ * @param flags how to measure: TEXT_MEASURE_MONOSPACE_WIDTH - measure as a monospace font
+ * TEXT_MEASURE_FULL_HEIGHT - measure bounds of the given string using the max ascend and
+ * descent of the font (not just of the measured text)
* @param bounds the bounds (left, top, right, bottom)
*/
- public abstract void getTextBounds(int textId,
- int start,
- int end,
- int flags,
- float[]bounds);
+ public abstract void getTextBounds(int textId, int start, int end, int flags, float[] bounds);
/**
* Draw a text starting ast x,y
@@ -146,38 +131,38 @@
* @param y
* @param rtl
*/
- public abstract void drawTextRun(int textId,
- int start,
- int end,
- int contextStart,
- int contextEnd,
- float x,
- float y,
- boolean rtl);
+ public abstract void drawTextRun(
+ int textId,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean rtl);
/**
* Draw an interpolation between two paths
+ *
* @param path1Id
* @param path2Id
- * @param tween 0.0 = is path1 1.0 is path2
+ * @param tween 0.0 = is path1 1.0 is path2
* @param start
* @param stop
*/
- public abstract void drawTweenPath(int path1Id,
- int path2Id,
- float tween,
- float start,
- float stop);
+ public abstract void drawTweenPath(
+ int path1Id, int path2Id, float tween, float start, float stop);
/**
* This applies changes to the current paint
+ *
* @param mPaintData the list of changes
*/
public abstract void applyPaint(PaintBundle mPaintData);
/**
- * Scale the rendering by scaleX and saleY (1.0 = no scale).
- * Scaling is done about centerX,centerY.
+ * Scale the rendering by scaleX and saleY (1.0 = no scale). Scaling is done about
+ * centerX,centerY.
*
* @param scaleX
* @param scaleY
@@ -188,6 +173,7 @@
/**
* Translate the rendering
+ *
* @param translateX
* @param translateY
*/
@@ -195,33 +181,30 @@
/**
* Skew the rendering
+ *
* @param skewX
* @param skewY
*/
public abstract void matrixSkew(float skewX, float skewY);
/**
- * Rotate the rendering.
- * Note rotates are cumulative.
+ * Rotate the rendering. Note rotates are cumulative.
+ *
* @param rotate angle to rotate
* @param pivotX x-coordinate about which to rotate
* @param pivotY y-coordinate about which to rotate
*/
public abstract void matrixRotate(float rotate, float pivotX, float pivotY);
- /**
- * Save the current state of the transform
- */
+ /** Save the current state of the transform */
public abstract void matrixSave();
- /**
- * Restore the previously saved state of the transform
- */
+ /** Restore the previously saved state of the transform */
public abstract void matrixRestore();
/**
- * Set the clip to a rectangle.
- * Drawing outside the current clip region will have no effect
+ * Set the clip to a rectangle. Drawing outside the current clip region will have no effect
+ *
* @param left
* @param top
* @param right
@@ -231,13 +214,15 @@
/**
* Clip based on a path.
+ *
* @param pathId
* @param regionOp
*/
public abstract void clipPath(int pathId, int regionOp);
/**
- * Clip based ona round rect
+ * Clip based ona round rect
+ *
* @param width
* @param height
* @param topStart
@@ -245,13 +230,15 @@
* @param bottomStart
* @param bottomEnd
*/
- public abstract void roundedClipRect(float width, float height,
- float topStart, float topEnd,
- float bottomStart, float bottomEnd);
+ public abstract void roundedClipRect(
+ float width,
+ float height,
+ float topStart,
+ float topEnd,
+ float bottomStart,
+ float bottomEnd);
- /**
- * Reset the paint
- */
+ /** Reset the paint */
public abstract void reset();
/**
@@ -285,4 +272,3 @@
mNeedsRepaint = true;
}
}
-
diff --git a/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java b/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java
index 4a1ccc9..9b7b50f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java
@@ -16,8 +16,8 @@
package com.android.internal.widget.remotecompose.core;
/**
- * PaintOperation interface, used for operations aimed at painting
- * (while any operation _can_ paint, this make it a little more explicit)
+ * PaintOperation interface, used for operations aimed at painting (while any operation _can_ paint,
+ * this make it a little more explicit)
*/
public abstract class PaintOperation implements Operation {
@@ -37,4 +37,13 @@
}
public abstract void paint(PaintContext context);
+
+ /**
+ * Will return true if the operation is similar enough to the current one, in the context of an
+ * animated transition.
+ */
+ public boolean suitableForTransition(Operation op) {
+ // by default expects the op to not be suitable
+ return false;
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Platform.java b/core/java/com/android/internal/widget/remotecompose/core/Platform.java
index 903dab4..6725e7e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Platform.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Platform.java
@@ -15,13 +15,36 @@
*/
package com.android.internal.widget.remotecompose.core;
-/**
- * Services that are needed to be provided by the platform during encoding.
- */
+/** Services that are needed to be provided by the platform during encoding. */
public interface Platform {
byte[] imageToByteArray(Object image);
- int getImageWidth(Object image);
- int getImageHeight(Object image);
- float[] pathToFloatArray(Object image);
-}
+ int getImageWidth(Object image);
+
+ int getImageHeight(Object image);
+
+ float[] pathToFloatArray(Object path);
+
+ Platform None =
+ new Platform() {
+ @Override
+ public byte[] imageToByteArray(Object image) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getImageWidth(Object image) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getImageHeight(Object image) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public float[] pathToFloatArray(Object path) {
+ throw new UnsupportedOperationException();
+ }
+ };
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
index 6b1828f..5b5adc2 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
@@ -25,15 +25,18 @@
import com.android.internal.widget.remotecompose.core.operations.DataListFloat;
import com.android.internal.widget.remotecompose.core.operations.DataListIds;
import com.android.internal.widget.remotecompose.core.operations.DataMapIds;
+import com.android.internal.widget.remotecompose.core.operations.DataMapLookup;
import com.android.internal.widget.remotecompose.core.operations.DrawArc;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmap;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmapInt;
+import com.android.internal.widget.remotecompose.core.operations.DrawBitmapScaled;
import com.android.internal.widget.remotecompose.core.operations.DrawCircle;
import com.android.internal.widget.remotecompose.core.operations.DrawLine;
import com.android.internal.widget.remotecompose.core.operations.DrawOval;
import com.android.internal.widget.remotecompose.core.operations.DrawPath;
import com.android.internal.widget.remotecompose.core.operations.DrawRect;
import com.android.internal.widget.remotecompose.core.operations.DrawRoundRect;
+import com.android.internal.widget.remotecompose.core.operations.DrawSector;
import com.android.internal.widget.remotecompose.core.operations.DrawText;
import com.android.internal.widget.remotecompose.core.operations.DrawTextAnchored;
import com.android.internal.widget.remotecompose.core.operations.DrawTextOnPath;
@@ -55,6 +58,10 @@
import com.android.internal.widget.remotecompose.core.operations.RootContentDescription;
import com.android.internal.widget.remotecompose.core.operations.TextData;
import com.android.internal.widget.remotecompose.core.operations.TextFromFloat;
+import com.android.internal.widget.remotecompose.core.operations.TextLength;
+import com.android.internal.widget.remotecompose.core.operations.TextLookup;
+import com.android.internal.widget.remotecompose.core.operations.TextLookupInt;
+import com.android.internal.widget.remotecompose.core.operations.TextMeasure;
import com.android.internal.widget.remotecompose.core.operations.TextMerge;
import com.android.internal.widget.remotecompose.core.operations.Theme;
import com.android.internal.widget.remotecompose.core.operations.Utils;
@@ -62,11 +69,14 @@
import com.android.internal.widget.remotecompose.core.operations.layout.ComponentEnd;
import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStart;
import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponentContent;
+import com.android.internal.widget.remotecompose.core.operations.layout.LoopEnd;
+import com.android.internal.widget.remotecompose.core.operations.layout.LoopOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.BoxLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.CanvasLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.ColumnLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.RowLayout;
+import com.android.internal.widget.remotecompose.core.operations.layout.managers.StateLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.TextLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BackgroundModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BorderModifierOperation;
@@ -76,7 +86,9 @@
import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
import com.android.internal.widget.remotecompose.core.operations.utilities.NanMap;
import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation;
+import com.android.internal.widget.remotecompose.core.types.BooleanConstant;
import com.android.internal.widget.remotecompose.core.types.IntegerConstant;
+import com.android.internal.widget.remotecompose.core.types.LongConstant;
import java.io.File;
import java.io.FileInputStream;
@@ -86,9 +98,7 @@
import java.util.ArrayList;
import java.util.Arrays;
-/**
- * Provides an abstract buffer to encode/decode RemoteCompose operations
- */
+/** Provides an abstract buffer to encode/decode RemoteCompose operations */
public class RemoteComposeBuffer {
public static final int EASING_CUBIC_STANDARD = FloatAnimation.CUBIC_STANDARD;
public static final int EASING_CUBIC_ACCELERATE = FloatAnimation.CUBIC_ACCELERATE;
@@ -156,13 +166,13 @@
/**
* Insert a header
*
- * @param width the width of the document in pixels
- * @param height the height of the document in pixels
+ * @param width the width of the document in pixels
+ * @param height the height of the document in pixels
* @param contentDescription content description of the document
- * @param capabilities bitmask indicating needed capabilities (unused for now)
+ * @param capabilities bitmask indicating needed capabilities (unused for now)
*/
- public void header(int width, int height, String contentDescription,
- float density, long capabilities) {
+ public void header(
+ int width, int height, String contentDescription, float density, long capabilities) {
Header.apply(mBuffer, width, height, density, capabilities);
int contentDescriptionId = 0;
if (contentDescription != null) {
@@ -174,8 +184,8 @@
/**
* Insert a header
*
- * @param width the width of the document in pixels
- * @param height the height of the document in pixels
+ * @param width the width of the document in pixels
+ * @param height the height of the document in pixels
* @param contentDescription content description of the document
*/
public void header(int width, int height, String contentDescription) {
@@ -185,23 +195,31 @@
/**
* Insert a bitmap
*
- * @param image an opaque image that we'll add to the buffer
- * @param imageWidth the width of the image
+ * @param image an opaque image that we'll add to the buffer
+ * @param imageWidth the width of the image
* @param imageHeight the height of the image
- * @param srcLeft left coordinate of the source area
- * @param srcTop top coordinate of the source area
- * @param srcRight right coordinate of the source area
- * @param srcBottom bottom coordinate of the source area
- * @param dstLeft left coordinate of the destination area
- * @param dstTop top coordinate of the destination area
- * @param dstRight right coordinate of the destination area
- * @param dstBottom bottom coordinate of the destination area
+ * @param srcLeft left coordinate of the source area
+ * @param srcTop top coordinate of the source area
+ * @param srcRight right coordinate of the source area
+ * @param srcBottom bottom coordinate of the source area
+ * @param dstLeft left coordinate of the destination area
+ * @param dstTop top coordinate of the destination area
+ * @param dstRight right coordinate of the destination area
+ * @param dstBottom bottom coordinate of the destination area
*/
- public void drawBitmap(Object image,
- int imageWidth, int imageHeight,
- int srcLeft, int srcTop, int srcRight, int srcBottom,
- int dstLeft, int dstTop, int dstRight, int dstBottom,
- String contentDescription) {
+ public void drawBitmap(
+ Object image,
+ int imageWidth,
+ int imageHeight,
+ int srcLeft,
+ int srcTop,
+ int srcRight,
+ int srcBottom,
+ int dstLeft,
+ int dstTop,
+ int dstRight,
+ int dstBottom,
+ String contentDescription) {
int imageId = mRemoteComposeState.dataGetId(image);
if (imageId == -1) {
imageId = mRemoteComposeState.cacheData(image);
@@ -213,14 +231,39 @@
contentDescriptionId = addText(contentDescription);
}
DrawBitmapInt.apply(
- mBuffer, imageId, srcLeft, srcTop, srcRight, srcBottom,
- dstLeft, dstTop, dstRight, dstBottom, contentDescriptionId
- );
+ mBuffer,
+ imageId,
+ srcLeft,
+ srcTop,
+ srcRight,
+ srcBottom,
+ dstLeft,
+ dstTop,
+ dstRight,
+ dstBottom,
+ contentDescriptionId);
}
/**
- * Adds a text string data to the stream and returns its id
- * Will be used to insert string with bitmaps etc.
+ * look up map and return the id of the object looked up
+ *
+ * @param mapId the map to access
+ * @param strId the string to lookup
+ * @return id containing the result of the lookup
+ */
+ public int mapLookup(int mapId, int strId) {
+ int hash = mapId + strId * 33;
+ int id = mRemoteComposeState.dataGetId(hash);
+ if (id == -1) {
+ id = mRemoteComposeState.cacheData(hash);
+ DataMapLookup.apply(mBuffer, id, mapId, strId);
+ }
+ return id;
+ }
+
+ /**
+ * Adds a text string data to the stream and returns its id Will be used to insert string with
+ * bitmaps etc.
*
* @param text the string to inject in the buffer
*/
@@ -236,13 +279,13 @@
/**
* Add a click area to the document
*
- * @param id the id of the click area, reported in the click listener callback
+ * @param id the id of the click area, reported in the click listener callback
* @param contentDescription the content description of that click area (accessibility)
- * @param left left coordinate of the area bounds
- * @param top top coordinate of the area bounds
- * @param right right coordinate of the area bounds
- * @param bottom bottom coordinate of the area bounds
- * @param metadata associated metadata, user-provided
+ * @param left left coordinate of the area bounds
+ * @param top top coordinate of the area bounds
+ * @param right right coordinate of the area bounds
+ * @param bottom bottom coordinate of the area bounds
+ * @param metadata associated metadata, user-provided
*/
public void addClickArea(
int id,
@@ -251,8 +294,7 @@
float top,
float right,
float bottom,
- String metadata
- ) {
+ String metadata) {
int contentDescriptionId = 0;
if (contentDescription != null) {
contentDescriptionId = addText(contentDescription);
@@ -261,77 +303,84 @@
if (metadata != null) {
metadataId = addText(metadata);
}
- ClickArea.apply(mBuffer, id, contentDescriptionId,
- left, top, right, bottom, metadataId);
+ ClickArea.apply(mBuffer, id, contentDescriptionId, left, top, right, bottom, metadataId);
}
/**
* Sets the way the player handles the content
*
- * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
+ * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
* @param alignment set the alignment of the content (TOP|CENTER|BOTTOM|START|END)
- * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
- * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes
- * the LAYOUT modes are:
- * - LAYOUT_MATCH_PARENT
- * - LAYOUT_WRAP_CONTENT
- * or adding an horizontal mode and a vertical mode:
- * - LAYOUT_HORIZONTAL_MATCH_PARENT
- * - LAYOUT_HORIZONTAL_WRAP_CONTENT
- * - LAYOUT_HORIZONTAL_FIXED
- * - LAYOUT_VERTICAL_MATCH_PARENT
- * - LAYOUT_VERTICAL_WRAP_CONTENT
- * - LAYOUT_VERTICAL_FIXED
- * The LAYOUT_*_FIXED modes will use the intrinsic document size
+ * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
+ * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes the LAYOUT modes are:
+ * - LAYOUT_MATCH_PARENT - LAYOUT_WRAP_CONTENT or adding an horizontal mode and a vertical
+ * mode: - LAYOUT_HORIZONTAL_MATCH_PARENT - LAYOUT_HORIZONTAL_WRAP_CONTENT -
+ * LAYOUT_HORIZONTAL_FIXED - LAYOUT_VERTICAL_MATCH_PARENT - LAYOUT_VERTICAL_WRAP_CONTENT -
+ * LAYOUT_VERTICAL_FIXED The LAYOUT_*_FIXED modes will use the intrinsic document size
*/
public void setRootContentBehavior(int scroll, int alignment, int sizing, int mode) {
RootContentBehavior.apply(mBuffer, scroll, alignment, sizing, mode);
}
/**
- * add Drawing the specified arc, which will be scaled to fit inside the specified oval.
- * <br>
+ * add Drawing the specified arc, which will be scaled to fit inside the specified oval. <br>
* If the start angle is negative or >= 360, the start angle is treated as start angle modulo
- * 360.
- * <br>
+ * 360. <br>
* If the sweep angle is >= 360, then the oval is drawn completely. Note that this differs
* slightly from SkPath::arcTo, which treats the sweep angle modulo 360. If the sweep angle is
- * negative, the sweep angle is treated as sweep angle modulo 360
- * <br>
+ * negative, the sweep angle is treated as sweep angle modulo 360 <br>
* The arc is drawn clockwise. An angle of 0 degrees correspond to the geometric angle of 0
- * degrees (3 o'clock on a watch.)
- * <br>
+ * degrees (3 o'clock on a watch.) <br>
*
- * @param left left coordinate of oval used to define the shape and size of the arc
- * @param top top coordinate of oval used to define the shape and size of the arc
- * @param right right coordinate of oval used to define the shape and size of the arc
- * @param bottom bottom coordinate of oval used to define the shape and size of the arc
+ * @param left left coordinate of oval used to define the shape and size of the arc
+ * @param top top coordinate of oval used to define the shape and size of the arc
+ * @param right right coordinate of oval used to define the shape and size of the arc
+ * @param bottom bottom coordinate of oval used to define the shape and size of the arc
* @param startAngle Starting angle (in degrees) where the arc begins
* @param sweepAngle Sweep angle (in degrees) measured clockwise
*/
- public void addDrawArc(float left,
- float top,
- float right,
- float bottom,
- float startAngle,
- float sweepAngle) {
+ public void addDrawArc(
+ float left, float top, float right, float bottom, float startAngle, float sweepAngle) {
DrawArc.apply(mBuffer, left, top, right, bottom, startAngle, sweepAngle);
}
/**
- * @param image The bitmap to be drawn
- * @param left left coordinate of rectangle that the bitmap will be to fit into
- * @param top top coordinate of rectangle that the bitmap will be to fit into
- * @param right right coordinate of rectangle that the bitmap will be to fit into
- * @param bottom bottom coordinate of rectangle that the bitmap will be to fit into
+ * add Drawing the specified sector, which will be scaled to fit inside the specified oval. <br>
+ * If the start angle is negative or >= 360, the start angle is treated as start angle modulo
+ * 360. <br>
+ * If the sweep angle is >= 360, then the oval is drawn completely. Note that this differs
+ * slightly from SkPath::arcTo, which treats the sweep angle modulo 360. If the sweep angle is
+ * negative, the sweep angle is treated as sweep angle modulo 360 <br>
+ * The arc is drawn clockwise. An angle of 0 degrees correspond to the geometric angle of 0
+ * degrees (3 o'clock on a watch.) <br>
+ *
+ * @param left left coordinate of oval used to define the shape and size of the arc
+ * @param top top coordinate of oval used to define the shape and size of the arc
+ * @param right right coordinate of oval used to define the shape and size of the arc
+ * @param bottom bottom coordinate of oval used to define the shape and size of the arc
+ * @param startAngle Starting angle (in degrees) where the arc begins
+ * @param sweepAngle Sweep angle (in degrees) measured clockwise
+ */
+ public void addDrawSector(
+ float left, float top, float right, float bottom, float startAngle, float sweepAngle) {
+ DrawSector.apply(mBuffer, left, top, right, bottom, startAngle, sweepAngle);
+ }
+
+ /**
+ * @param image The bitmap to be drawn
+ * @param left left coordinate of rectangle that the bitmap will be to fit into
+ * @param top top coordinate of rectangle that the bitmap will be to fit into
+ * @param right right coordinate of rectangle that the bitmap will be to fit into
+ * @param bottom bottom coordinate of rectangle that the bitmap will be to fit into
* @param contentDescription content description of the image
*/
- public void addDrawBitmap(Object image,
- float left,
- float top,
- float right,
- float bottom,
- String contentDescription) {
+ public void addDrawBitmap(
+ Object image,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ String contentDescription) {
int imageId = mRemoteComposeState.dataGetId(image);
if (imageId == -1) {
imageId = mRemoteComposeState.cacheData(image);
@@ -345,9 +394,182 @@
if (contentDescription != null) {
contentDescriptionId = addText(contentDescription);
}
- DrawBitmap.apply(
- mBuffer, imageId, left, top, right, bottom, contentDescriptionId
- );
+ DrawBitmap.apply(mBuffer, imageId, left, top, right, bottom, contentDescriptionId);
+ }
+
+ /**
+ * @param imageId The Id bitmap to be drawn
+ * @param left left coordinate of rectangle that the bitmap will be to fit into
+ * @param top top coordinate of rectangle that the bitmap will be to fit into
+ * @param right right coordinate of rectangle that the bitmap will be to fit into
+ * @param bottom bottom coordinate of rectangle that the bitmap will be to fit into
+ * @param contentDescription content description of the image
+ */
+ public void addDrawBitmap(
+ int imageId,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ String contentDescription) {
+ int contentDescriptionId = 0;
+ if (contentDescription != null) {
+ contentDescriptionId = addText(contentDescription);
+ }
+ DrawBitmap.apply(mBuffer, imageId, left, top, right, bottom, contentDescriptionId);
+ }
+
+ /**
+ * @param image The bitmap to be drawn
+ * @param srcLeft left coordinate in the source bitmap will be to extracted
+ * @param srcTop top coordinate in the source bitmap will be to extracted
+ * @param srcRight right coordinate in the source bitmap will be to extracted
+ * @param srcBottom bottom coordinate in the source bitmap will be to extracted
+ * @param dstLeft left coordinate of rectangle that the bitmap will be to fit into
+ * @param dstTop top coordinate of rectangle that the bitmap will be to fit into
+ * @param dstRight right coordinate of rectangle that the bitmap will be to fit into
+ * @param dstBottom bottom coordinate of rectangle that the bitmap will be to fit into
+ * @param scaleType The type of scaling to allow the image to fit.
+ * @param scaleFactor the scale factor when scale type is FIXED_SCALE (type = 7)
+ * @param contentDescription associate a string with image for accessibility
+ */
+ public void drawScaledBitmap(
+ Object image,
+ float srcLeft,
+ float srcTop,
+ float srcRight,
+ float srcBottom,
+ float dstLeft,
+ float dstTop,
+ float dstRight,
+ float dstBottom,
+ int scaleType,
+ float scaleFactor,
+ String contentDescription) {
+ int imageId = mRemoteComposeState.dataGetId(image);
+ if (imageId == -1) {
+ imageId = mRemoteComposeState.cacheData(image);
+ byte[] data = mPlatform.imageToByteArray(image);
+ int imageWidth = mPlatform.getImageWidth(image);
+ int imageHeight = mPlatform.getImageHeight(image);
+
+ BitmapData.apply(mBuffer, imageId, imageWidth, imageHeight, data);
+ }
+ int contentDescriptionId = 0;
+ if (contentDescription != null) {
+ contentDescriptionId = addText(contentDescription);
+ }
+ DrawBitmapScaled.apply(
+ mBuffer,
+ imageId,
+ srcLeft,
+ srcTop,
+ srcRight,
+ srcBottom,
+ dstLeft,
+ dstTop,
+ dstRight,
+ dstBottom,
+ scaleType,
+ scaleFactor,
+ contentDescriptionId);
+ }
+
+ /**
+ * Transmit bitmap so the you can use the id form. This is useful if
+ *
+ * @param image drawScaledBitmap
+ * @return id of the image useful with
+ */
+ public int addBitmap(Object image) {
+ int imageId = mRemoteComposeState.dataGetId(image);
+ if (imageId == -1) {
+ imageId = mRemoteComposeState.cacheData(image);
+ byte[] data = mPlatform.imageToByteArray(image);
+ int imageWidth = mPlatform.getImageWidth(image);
+ int imageHeight = mPlatform.getImageHeight(image);
+
+ BitmapData.apply(mBuffer, imageId, imageWidth, imageHeight, data);
+ }
+ return imageId;
+ }
+
+ /**
+ * Transmit bitmap so the you can use the id form. This is useful if
+ *
+ * @param image drawScaledBitmap
+ * @return id of the image useful with
+ */
+ public int addBitmap(Object image, String name) {
+ int imageId = mRemoteComposeState.dataGetId(image);
+ if (imageId == -1) {
+ imageId = mRemoteComposeState.cacheData(image);
+ byte[] data = mPlatform.imageToByteArray(image);
+ int imageWidth = mPlatform.getImageWidth(image);
+ int imageHeight = mPlatform.getImageHeight(image);
+
+ BitmapData.apply(mBuffer, imageId, imageWidth, imageHeight, data);
+ setBitmapName(imageId, name);
+ }
+
+ return imageId;
+ }
+
+ /**
+ * This defines the name of the color given the id.
+ *
+ * @param id of the Bitmap
+ * @param name Name of the color
+ */
+ public void setBitmapName(int id, String name) {
+ NamedVariable.apply(mBuffer, id, NamedVariable.IMAGE_TYPE, name);
+ }
+
+ /**
+ * @param imageId The id of the bitmap to be drawn
+ * @param srcLeft left coordinate in the source bitmap will be to extracted
+ * @param srcTop top coordinate in the source bitmap will be to extracted
+ * @param srcRight right coordinate in the source bitmap will be to extracted
+ * @param srcBottom bottom coordinate in the source bitmap will be to extracted
+ * @param dstLeft left coordinate of rectangle that the bitmap will be to fit into
+ * @param dstTop top coordinate of rectangle that the bitmap will be to fit into
+ * @param dstRight right coordinate of rectangle that the bitmap will be to fit into
+ * @param dstBottom bottom coordinate of rectangle that the bitmap will be to fit into
+ * @param scaleType The type of scaling to allow the image to fit.
+ * @param scaleFactor the scale factor when scale type is FIXED_SCALE (type = 7)
+ * @param contentDescription associate a string with image for accessibility
+ */
+ public void drawScaledBitmap(
+ int imageId,
+ float srcLeft,
+ float srcTop,
+ float srcRight,
+ float srcBottom,
+ float dstLeft,
+ float dstTop,
+ float dstRight,
+ float dstBottom,
+ int scaleType,
+ float scaleFactor,
+ String contentDescription) {
+ int contentDescriptionId = 0;
+ if (contentDescription != null) {
+ contentDescriptionId = addText(contentDescription);
+ }
+ DrawBitmapScaled.apply(
+ mBuffer,
+ imageId,
+ srcLeft,
+ srcTop,
+ srcRight,
+ srcBottom,
+ dstLeft,
+ dstTop,
+ dstRight,
+ dstBottom,
+ scaleType,
+ scaleFactor,
+ contentDescriptionId);
}
/**
@@ -356,7 +578,7 @@
*
* @param centerX The x-coordinate of the center of the circle to be drawn
* @param centerY The y-coordinate of the center of the circle to be drawn
- * @param radius The radius of the circle to be drawn
+ * @param radius The radius of the circle to be drawn
*/
public void addDrawCircle(float centerX, float centerY, float radius) {
DrawCircle.apply(mBuffer, centerX, centerY, radius);
@@ -378,9 +600,9 @@
/**
* Draw the specified oval using the specified paint.
*
- * @param left left coordinate of oval
- * @param top top coordinate of oval
- * @param right right coordinate of oval
+ * @param left left coordinate of oval
+ * @param top top coordinate of oval
+ * @param right right coordinate of oval
* @param bottom bottom coordinate of oval
*/
public void addDrawOval(float left, float top, float right, float bottom) {
@@ -389,9 +611,9 @@
/**
* Draw the specified path
- * <p>
- * Note: path objects are not immutable
- * modifying them and calling this will not change the drawing
+ *
+ * <p>Note: path objects are not immutable modifying them and calling this will not change the
+ * drawing
*
* @param path The path to be drawn
*/
@@ -415,9 +637,9 @@
/**
* Draw the specified Rect
*
- * @param left left coordinate of rectangle to be drawn
- * @param top top coordinate of rectangle to be drawn
- * @param right right coordinate of rectangle to be drawn
+ * @param left left coordinate of rectangle to be drawn
+ * @param top top coordinate of rectangle to be drawn
+ * @param right right coordinate of rectangle to be drawn
* @param bottom bottom coordinate of rectangle to be drawn
*/
public void addDrawRect(float left, float top, float right, float bottom) {
@@ -427,23 +649,23 @@
/**
* Draw the specified round-rect
*
- * @param left left coordinate of rectangle to be drawn
- * @param top left coordinate of rectangle to be drawn
- * @param right left coordinate of rectangle to be drawn
- * @param bottom left coordinate of rectangle to be drawn
+ * @param left left coordinate of rectangle to be drawn
+ * @param top left coordinate of rectangle to be drawn
+ * @param right left coordinate of rectangle to be drawn
+ * @param bottom left coordinate of rectangle to be drawn
* @param radiusX The x-radius of the oval used to round the corners
* @param radiusY The y-radius of the oval used to round the corners
*/
- public void addDrawRoundRect(float left, float top, float right, float bottom,
- float radiusX, float radiusY) {
+ public void addDrawRoundRect(
+ float left, float top, float right, float bottom, float radiusX, float radiusY) {
DrawRoundRect.apply(mBuffer, left, top, right, bottom, radiusX, radiusY);
}
/**
* Draw the text, with origin at (x,y) along the specified path.
*
- * @param text The text to be drawn
- * @param path The path the text should follow for its baseline
+ * @param text The text to be drawn
+ * @param path The path the text should follow for its baseline
* @param hOffset The distance along the path to add to the text's starting position
* @param vOffset The distance above(-) or below(+) the path to position the text
*/
@@ -457,91 +679,79 @@
}
/**
- * Draw the text, with origin at (x,y). The origin is interpreted
- * based on the Align setting in the paint.
+ * Draw the text, with origin at (x,y). The origin is interpreted based on the Align setting in
+ * the paint.
*
- * @param text The text to be drawn
- * @param start The index of the first character in text to draw
- * @param end (end - 1) is the index of the last character in text to draw
+ * @param text The text to be drawn
+ * @param start The index of the first character in text to draw
+ * @param end (end - 1) is the index of the last character in text to draw
* @param contextStart
* @param contextEnd
- * @param x The x-coordinate of the origin of the text being drawn
- * @param y The y-coordinate of the baseline of the text being drawn
- * @param rtl Draw RTTL
+ * @param x The x-coordinate of the origin of the text being drawn
+ * @param y The y-coordinate of the baseline of the text being drawn
+ * @param rtl Draw RTTL
*/
- public void addDrawTextRun(String text,
- int start,
- int end,
- int contextStart,
- int contextEnd,
- float x,
- float y,
- boolean rtl) {
+ public void addDrawTextRun(
+ String text,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean rtl) {
int textId = addText(text);
- DrawText.apply(
- mBuffer, textId, start, end,
- contextStart, contextEnd, x, y, rtl);
+ DrawText.apply(mBuffer, textId, start, end, contextStart, contextEnd, x, y, rtl);
}
/**
- * Draw the text, with origin at (x,y). The origin is interpreted
- * based on the Align setting in the paint.
+ * Draw the text, with origin at (x,y). The origin is interpreted based on the Align setting in
+ * the paint.
*
- * @param textId The text to be drawn
- * @param start The index of the first character in text to draw
- * @param end (end - 1) is the index of the last character in text to draw
+ * @param textId The text to be drawn
+ * @param start The index of the first character in text to draw
+ * @param end (end - 1) is the index of the last character in text to draw
* @param contextStart
* @param contextEnd
- * @param x The x-coordinate of the origin of the text being drawn
- * @param y The y-coordinate of the baseline of the text being drawn
- * @param rtl Draw RTTL
+ * @param x The x-coordinate of the origin of the text being drawn
+ * @param y The y-coordinate of the baseline of the text being drawn
+ * @param rtl Draw RTTL
*/
- public void addDrawTextRun(int textId,
- int start,
- int end,
- int contextStart,
- int contextEnd,
- float x,
- float y,
- boolean rtl) {
- DrawText.apply(
- mBuffer, textId, start, end,
- contextStart, contextEnd, x, y, rtl);
+ public void addDrawTextRun(
+ int textId,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean rtl) {
+ DrawText.apply(mBuffer, textId, start, end, contextStart, contextEnd, x, y, rtl);
}
/**
- * Draw a text on canvas at relative to position (x, y),
- * offset panX and panY.
- * <br>
- * The panning factors (panX, panY) mapped to the
- * resulting bounding box of the text, in such a way that a
- * panning factor of (0.0, 0.0) would center the text at (x, y)
+ * Draw a text on canvas at relative to position (x, y), offset panX and panY. <br>
+ * The panning factors (panX, panY) mapped to the resulting bounding box of the text, in such a
+ * way that a panning factor of (0.0, 0.0) would center the text at (x, y)
+ *
* <ul>
- * <li> Panning of -1.0, -1.0 - the text above & right of x,y.</li>
- * <li>Panning of 1.0, 1.0 - the text is below and to the left</li>
- * <li>Panning of 1.0, 0.0 - the test is centered & to the right of x,y</li>
+ * <li>Panning of -1.0, -1.0 - the text above & right of x,y.
+ * <li>Panning of 1.0, 1.0 - the text is below and to the left
+ * <li>Panning of 1.0, 0.0 - the test is centered & to the right of x,y
* </ul>
+ *
* Setting panY to NaN results in y being the baseline of the text.
*
- * @param text text to draw
- * @param x Coordinate of the Anchor
- * @param y Coordinate of the Anchor
- * @param panX justifies text -1.0=right, 0.0=center, 1.0=left
- * @param panY position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline
+ * @param text text to draw
+ * @param x Coordinate of the Anchor
+ * @param y Coordinate of the Anchor
+ * @param panX justifies text -1.0=right, 0.0=center, 1.0=left
+ * @param panY position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline
* @param flags 1 = RTL
*/
- public void drawTextAnchored(String text,
- float x,
- float y,
- float panX,
- float panY,
- int flags) {
+ public void drawTextAnchored(String text, float x, float y, float panX, float panY, int flags) {
int textId = addText(text);
- DrawTextAnchored.apply(
- mBuffer, textId,
- x, y,
- panX, panY,
- flags);
+ DrawTextAnchored.apply(mBuffer, textId, x, y, panX, panY, flags);
}
/**
@@ -556,6 +766,7 @@
/**
* Merge two text (from id's) output one id
+ *
* @param id1 left id
* @param id2 right id
* @return new id that merges the two text
@@ -576,77 +787,68 @@
/**
* Create a TextFromFloat command which creates text from a Float.
*
- * @param value The value to convert
+ * @param value The value to convert
* @param digitsBefore the digits before the decimal point
- * @param digitsAfter the digits after the decimal point
- * @param flags configure the behaviour using PAD_PRE_* and PAD_AFTER* flags
+ * @param digitsAfter the digits after the decimal point
+ * @param flags configure the behaviour using PAD_PRE_* and PAD_AFTER* flags
* @return id of the string that can be passed to drawTextAnchored
*/
- public int createTextFromFloat(float value, short digitsBefore,
- short digitsAfter, int flags) {
- String placeHolder = Utils.floatToString(value)
- + "(" + digitsBefore + "," + digitsAfter + "," + flags + ")";
+ public int createTextFromFloat(float value, short digitsBefore, short digitsAfter, int flags) {
+ String placeHolder =
+ Utils.floatToString(value)
+ + "("
+ + digitsBefore
+ + ","
+ + digitsAfter
+ + ","
+ + flags
+ + ")";
int id = mRemoteComposeState.dataGetId(placeHolder);
if (id == -1) {
id = mRemoteComposeState.cacheData(placeHolder);
// TextData.apply(mBuffer, id, text);
}
- TextFromFloat.apply(mBuffer, id, value, digitsBefore,
- digitsAfter, flags);
+ TextFromFloat.apply(mBuffer, id, value, digitsBefore, digitsAfter, flags);
return id;
}
/**
- * Draw a text on canvas at relative to position (x, y),
- * offset panX and panY.
- * <br>
- * The panning factors (panX, panY) mapped to the
- * resulting bounding box of the text, in such a way that a
- * panning factor of (0.0, 0.0) would center the text at (x, y)
+ * Draw a text on canvas at relative to position (x, y), offset panX and panY. <br>
+ * The panning factors (panX, panY) mapped to the resulting bounding box of the text, in such a
+ * way that a panning factor of (0.0, 0.0) would center the text at (x, y)
+ *
* <ul>
- * <li> Panning of -1.0, -1.0 - the text above & right of x,y.</li>
- * <li>Panning of 1.0, 1.0 - the text is below and to the left</li>
- * <li>Panning of 1.0, 0.0 - the test is centered & to the right of x,y</li>
+ * <li>Panning of -1.0, -1.0 - the text above & right of x,y.
+ * <li>Panning of 1.0, 1.0 - the text is below and to the left
+ * <li>Panning of 1.0, 0.0 - the test is centered & to the right of x,y
* </ul>
+ *
* Setting panY to NaN results in y being the baseline of the text.
*
* @param textId text to draw
- * @param x Coordinate of the Anchor
- * @param y Coordinate of the Anchor
- * @param panX justifies text -1.0=right, 0.0=center, 1.0=left
- * @param panY position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline
- * @param flags 1 = RTL
+ * @param x Coordinate of the Anchor
+ * @param y Coordinate of the Anchor
+ * @param panX justifies text -1.0=right, 0.0=center, 1.0=left
+ * @param panY position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline
+ * @param flags 1 = RTL
*/
- public void drawTextAnchored(int textId,
- float x,
- float y,
- float panX,
- float panY,
- int flags) {
+ public void drawTextAnchored(int textId, float x, float y, float panX, float panY, int flags) {
- DrawTextAnchored.apply(
- mBuffer, textId,
- x, y,
- panX, panY,
- flags);
+ DrawTextAnchored.apply(mBuffer, textId, x, y, panX, panY, flags);
}
/**
* draw an interpolation between two paths that have the same pattern
- * <p>
- * Warning paths objects are not immutable and this is not taken into consideration
+ *
+ * <p>Warning paths objects are not immutable and this is not taken into consideration
*
* @param path1 The path1 to be drawn between
* @param path2 The path2 to be drawn between
* @param tween The ratio of path1 and path2 to 0 = all path 1, 1 = all path2
* @param start The start of the subrange of paths to draw 0 = start form start 0.5 is half way
- * @param stop The end of the subrange of paths to draw 1 = end at the end 0.5 is end half way
+ * @param stop The end of the subrange of paths to draw 1 = end at the end 0.5 is end half way
*/
- public void addDrawTweenPath(Object path1,
- Object path2,
- float tween,
- float start,
- float stop) {
+ public void addDrawTweenPath(Object path1, Object path2, float tween, float start, float stop) {
int path1Id = mRemoteComposeState.dataGetId(path1);
if (path1Id == -1) { // never been seen before
path1Id = addPathData(path1);
@@ -663,18 +865,12 @@
*
* @param path1Id The path1 to be drawn between
* @param path2Id The path2 to be drawn between
- * @param tween The ratio of path1 and path2 to 0 = all path 1, 1 = all path2
- * @param start The start of the subrange of paths to draw 0 = start form start .5 is 1/2 way
- * @param stop The end of the subrange of paths to draw 1 = end at the end .5 is end 1/2 way
+ * @param tween The ratio of path1 and path2 to 0 = all path 1, 1 = all path2
+ * @param start The start of the subrange of paths to draw 0 = start form start .5 is 1/2 way
+ * @param stop The end of the subrange of paths to draw 1 = end at the end .5 is end 1/2 way
*/
- public void addDrawTweenPath(int path1Id,
- int path2Id,
- float tween,
- float start,
- float stop) {
- DrawTweenPath.apply(
- mBuffer, path1Id, path2Id,
- tween, start, stop);
+ public void addDrawTweenPath(int path1Id, int path2Id, float tween, float start, float stop) {
+ DrawTweenPath.apply(mBuffer, path1Id, path2Id, tween, start, stop);
}
/**
@@ -692,11 +888,13 @@
/**
* Adds a paint Bundle to the doc
+ *
* @param paint
*/
public void addPaint(PaintBundle paint) {
PaintData.apply(mBuffer, paint);
}
+
///////////////////////////////////////////////////////////////////////////////////////////////
public void inflateFromBuffer(ArrayList<Operation> operations) {
@@ -741,30 +939,29 @@
return "v1.0";
}
- public static RemoteComposeBuffer fromFile(String path,
- RemoteComposeState remoteComposeState)
+ public static RemoteComposeBuffer fromFile(String path, RemoteComposeState remoteComposeState)
throws IOException {
RemoteComposeBuffer buffer = new RemoteComposeBuffer(remoteComposeState);
read(new File(path), buffer);
return buffer;
}
- public RemoteComposeBuffer fromFile(File file,
- RemoteComposeState remoteComposeState) throws IOException {
+ public RemoteComposeBuffer fromFile(File file, RemoteComposeState remoteComposeState)
+ throws IOException {
RemoteComposeBuffer buffer = new RemoteComposeBuffer(remoteComposeState);
read(file, buffer);
return buffer;
}
- public static RemoteComposeBuffer fromInputStream(InputStream inputStream,
- RemoteComposeState remoteComposeState) {
+ public static RemoteComposeBuffer fromInputStream(
+ InputStream inputStream, RemoteComposeState remoteComposeState) {
RemoteComposeBuffer buffer = new RemoteComposeBuffer(remoteComposeState);
read(inputStream, buffer);
return buffer;
}
- RemoteComposeBuffer copyFromOperations(ArrayList<Operation> operations,
- RemoteComposeBuffer buffer) {
+ RemoteComposeBuffer copyFromOperations(
+ ArrayList<Operation> operations, RemoteComposeBuffer buffer) {
for (Operation operation : operations) {
operation.write(buffer.mBuffer);
@@ -832,9 +1029,9 @@
}
/**
- * This call balances a previous call to save(), and is used to remove all
- * modifications to the matrix/clip state since the last save call.
- * Do not call restore() more times than save() was called.
+ * This call balances a previous call to save(), and is used to remove all modifications to the
+ * matrix/clip state since the last save call. Do not call restore() more times than save() was
+ * called.
*/
public void addMatrixRestore() {
MatrixRestore.apply(mBuffer);
@@ -842,11 +1039,10 @@
/**
* Add a saves the current matrix and clip onto a private stack.
- * <p>
- * Subsequent calls to translate,scale,rotate,skew,concat or clipRect,
- * clipPath will all operate as usual, but when the balancing call to
- * restore() is made, those calls will be forgotten, and the settings that
- * existed before the save() will be reinstated.
+ *
+ * <p>Subsequent calls to translate,scale,rotate,skew,concat or clipRect, clipPath will all
+ * operate as usual, but when the balancing call to restore() is made, those calls will be
+ * forgotten, and the settings that existed before the save() will be reinstated.
*/
public void addMatrixSave() {
MatrixSave.apply(mBuffer);
@@ -855,7 +1051,7 @@
/**
* add a pre-concat the current matrix with the specified rotation.
*
- * @param angle The amount to rotate, in degrees
+ * @param angle The amount to rotate, in degrees
* @param centerX The x-coord for the pivot point (unchanged by the rotation)
* @param centerY The y-coord for the pivot point (unchanged by the rotation)
*/
@@ -886,8 +1082,8 @@
/**
* Add a pre-concat of the current matrix with the specified scale.
*
- * @param scaleX The amount to scale in X
- * @param scaleY The amount to scale in Y
+ * @param scaleX The amount to scale in X
+ * @param scaleY The amount to scale in Y
* @param centerX The x-coord for the pivot point (unchanged by the scale)
* @param centerY The y-coord for the pivot point (unchanged by the scale)
*/
@@ -897,6 +1093,7 @@
/**
* sets the clip based on clip id
+ *
* @param pathId 0 clears the clip
*/
public void addClipPath(int pathId) {
@@ -905,10 +1102,11 @@
/**
* Sets the clip based on clip rec
- * @param left left coordinate of the clip rectangle
- * @param top top coordinate of the clip rectangle
- * @param right right coordinate of the clip rectangle
- * @param bottom bottom coordinate of the clip rectangle
+ *
+ * @param left left coordinate of the clip rectangle
+ * @param top top coordinate of the clip rectangle
+ * @param right right coordinate of the clip rectangle
+ * @param bottom bottom coordinate of the clip rectangle
*/
public void addClipRect(float left, float top, float right, float bottom) {
ClipRect.apply(mBuffer, left, top, right, bottom);
@@ -916,6 +1114,7 @@
/**
* Add a float return a NaN number pointing to that float
+ *
* @param value the value of the float
* @return the nan id of float
*/
@@ -925,9 +1124,9 @@
return Utils.asNan(id);
}
-
/**
* Add a Integer return an id number pointing to that float.
+ *
* @param value adds an integer and assigns it an id
* @return the id of the integer to be used
*/
@@ -938,7 +1137,32 @@
}
/**
+ * Add a long constant return a id. They can be used as parameters to Custom Attributes.
+ *
+ * @param value the value of the long
+ * @return the id of the command representing long
+ */
+ public int addLong(long value) {
+ int id = mRemoteComposeState.cacheData(value);
+ LongConstant.apply(mBuffer, id, value);
+ return id;
+ }
+
+ /**
+ * Add a boolean constant return a id. They can be used as parameters to Custom Attributes.
+ *
+ * @param value the value of the boolean
+ * @return the id
+ */
+ public int addBoolean(boolean value) {
+ int id = mRemoteComposeState.cacheData(value);
+ BooleanConstant.apply(mBuffer, id, value);
+ return id;
+ }
+
+ /**
* Add a IntegerId as float ID.
+ *
* @param id id to be converted
* @return the id wrapped in a NaN
*/
@@ -948,6 +1172,7 @@
/**
* Add a float that is a computation based on variables
+ *
* @param value A RPN style float operation i.e. "4, 3, ADD" outputs 7
* @return NaN id of the result of the calculation
*/
@@ -958,8 +1183,8 @@
}
/**
- * Add a float that is a computation based on variables.
- * see packAnimation
+ * Add a float that is a computation based on variables. see packAnimation
+ *
* @param value A RPN style float operation i.e. "4, 3, ADD" outputs 7
* @param animation Array of floats that represents animation
* @return NaN id of the result of the calculation
@@ -970,12 +1195,37 @@
return Utils.asNan(id);
}
+ /**
+ * measure the text and return a measure as a float
+ *
+ * @param textId id of the text
+ * @param mode the mode 0 is the width
+ * @return
+ */
+ public float textMeasure(int textId, int mode) {
+ int id = mRemoteComposeState.cacheData(textId + mode * 31);
+ TextMeasure.apply(mBuffer, id, textId, mode);
+ return Utils.asNan(id);
+ }
+
+ /**
+ * measure the text and return the length of the text as float
+ *
+ * @param textId id of the text
+ * @return id of a float that is the length
+ */
+ public float textLength(int textId) {
+ // The cache id is computed buy merging the two values together
+ // to create a relatively unique value
+ int id = mRemoteComposeState.cacheData(textId + (TextLength.id() << 16));
+ TextLength.apply(mBuffer, id, textId);
+ return Utils.asNan(id);
+ }
/**
* add a float array
*
* @param values
- *
* @return the id of the array, encoded as a float NaN
*/
public float addFloatArray(float[] values) {
@@ -986,23 +1236,23 @@
/**
* This creates a list of individual floats
- * @param values array of floats to be individually stored
*
+ * @param values array of floats to be individually stored
* @return id of the list
*/
public float addFloatList(float[] values) {
- int []listId = new int[values.length];
+ int[] listId = new int[values.length];
for (int i = 0; i < listId.length; i++) {
listId[i] = mRemoteComposeState.cacheFloat(values[i]);
- FloatConstant.apply(mBuffer, listId[i], values[i]);
+ FloatConstant.apply(mBuffer, listId[i], values[i]);
}
return addList(listId);
}
/**
* This creates a list of individual floats
- * @param listId array id to be stored
*
+ * @param listId array id to be stored
* @return id of the list
*/
public float addList(int[] listId) {
@@ -1016,16 +1266,17 @@
*
* @param keys
* @param values
- *
* @return the id of the map, encoded as a float NaN
*/
- public float addFloatMap(String[]keys, float[] values) {
- int []listId = new int[values.length];
+ public float addFloatMap(String[] keys, float[] values) {
+ int[] listId = new int[values.length];
+ byte[] type = new byte[values.length];
for (int i = 0; i < listId.length; i++) {
listId[i] = mRemoteComposeState.cacheFloat(values[i]);
- FloatConstant.apply(mBuffer, listId[i], values[i]);
+ FloatConstant.apply(mBuffer, listId[i], values[i]);
+ type[i] = DataMapIds.TYPE_FLOAT;
}
- return addMap(keys, listId);
+ return addMap(keys, type, listId);
}
/**
@@ -1033,17 +1284,49 @@
*
* @param keys
* @param listId
- *
* @return the id of the map, encoded as a float NaN
*/
- public float addMap(String []keys, int[] listId) {
+ public int addMap(String[] keys, byte[] types, int[] listId) {
int id = mRemoteComposeState.cacheData(listId, NanMap.TYPE_ARRAY);
- DataMapIds.apply(mBuffer, id, keys, listId);
- return Utils.asNan(id);
+ DataMapIds.apply(mBuffer, id, keys, types, listId);
+ return id;
+ }
+
+ /**
+ * This provides access to text in RemoteList
+ *
+ * @param dataSet
+ * @param index index as a float variable
+ * @return
+ */
+ public int textLookup(float dataSet, float index) {
+ long hash =
+ ((long) Float.floatToRawIntBits(dataSet))
+ << (32 + Float.floatToRawIntBits(index)); // TODO: is this the correct ()s?
+ int id = mRemoteComposeState.cacheData(hash);
+ TextLookup.apply(mBuffer, id, Utils.idFromNan(dataSet), index);
+ return id;
+ }
+
+ /**
+ * This provides access to text in RemoteList
+ *
+ * @param dataSet
+ * @param index index as an int variable
+ * @return
+ */
+ public int textLookup(float dataSet, int index) {
+ long hash =
+ ((long) Float.floatToRawIntBits(dataSet))
+ << (32 + Float.floatToRawIntBits(index)); // TODO: is this the correct ()s?
+ int id = mRemoteComposeState.cacheData(hash);
+ TextLookupInt.apply(mBuffer, id, Utils.idFromNan(dataSet), index);
+ return id;
}
/**
* Add and integer expression
+ *
* @param mask defines which elements are operators or variables
* @param value array of values to calculate maximum 32
* @return the id as an integer
@@ -1051,11 +1334,12 @@
public int addIntegerExpression(int mask, int[] value) {
int id = mRemoteComposeState.cacheData(value);
IntegerExpression.apply(mBuffer, id, mask, value);
- return id;
+ return id;
}
/**
* Add a simple color
+ *
* @param color the RGB color value
* @return id that represents that color
*/
@@ -1067,9 +1351,9 @@
return id;
}
-
/**
* Add a color that represents the tween between two colors
+ *
* @param color1 the ARGB value of the first color
* @param color2 the ARGB value of the second color
* @param tween the interpolation bet
@@ -1084,8 +1368,8 @@
}
/**
- * Add a color that represents the tween between two colors where color1
- * is the id of a color
+ * Add a color that represents the tween between two colors where color1 is the id of a color
+ *
* @param color1 id of color
* @param color2 rgb color value
* @param tween the tween between color1 and color2 (1 = color2)
@@ -1100,8 +1384,8 @@
}
/**
- * Add a color that represents the tween between two colors where color2
- * is the id of a color
+ * Add a color that represents the tween between two colors where color2 is the id of a color
+ *
* @param color1 the ARGB value of the first color
* @param color2 id of the second color
* @param tween the tween between color1 and color2 (1 = color2)
@@ -1116,8 +1400,9 @@
}
/**
- * Add a color that represents the tween between two colors where color1 &
- * color2 are the ids of colors
+ * Add a color that represents the tween between two colors where color1 & color2 are the ids of
+ * colors
+ *
* @param color1 id of the first color
* @param color2 id of the second color
* @param tween the tween between color1 and color2 (1 = color2)
@@ -1132,8 +1417,9 @@
}
/**
- * Color calculated by Hue saturation and value.
- * (as floats they can be variables used to create color transitions)
+ * Color calculated by Hue saturation and value. (as floats they can be variables used to create
+ * color transitions)
+ *
* @param hue the Hue
* @param sat the saturation
* @param value the value
@@ -1148,8 +1434,9 @@
}
/**
- * Color calculated by Alpha, Hue saturation and value.
- * (as floats they can be variables used to create color transitions)
+ * Color calculated by Alpha, Hue saturation and value. (as floats they can be variables used to
+ * create color transitions)
+ *
* @param alpha the Alpha
* @param hue the hue
* @param sat the saturation
@@ -1165,8 +1452,9 @@
}
/**
- * create and animation based on description and return as an array of
- * floats. see addAnimatedFloat
+ * create and animation based on description and return as an array of floats. see
+ * addAnimatedFloat
+ *
* @param duration the duration of the aimation
* @param type the type of animation
* @param spec the parameters of the animation if any
@@ -1174,23 +1462,20 @@
* @param wrap the wraps value so (e.g 360 so angles 355 would animate to 5)
* @return
*/
- public static float[] packAnimation(float duration,
- int type,
- float[] spec,
- float initialValue,
- float wrap) {
+ public static float[] packAnimation(
+ float duration, int type, float[] spec, float initialValue, float wrap) {
return FloatAnimation.packToFloatArray(duration, type, spec, initialValue, wrap);
}
/**
* This defines the name of the color given the id.
+ *
* @param id of the color
* @param name Name of the color
*/
public void setColorName(int id, String name) {
- NamedVariable.apply(mBuffer, id,
- NamedVariable.COLOR_TYPE, name);
+ NamedVariable.apply(mBuffer, id, NamedVariable.COLOR_TYPE, name);
}
/**
@@ -1200,16 +1485,14 @@
* @param name name of the string
*/
public void setStringName(int id, String name) {
- NamedVariable.apply(mBuffer, id,
- NamedVariable.STRING_TYPE, name);
+ NamedVariable.apply(mBuffer, id, NamedVariable.STRING_TYPE, name);
}
/**
- * Returns a usable component id -- either the one passed in parameter if not -1
- * or a generated one.
+ * Returns a usable component id -- either the one passed in parameter if not -1 or a generated
+ * one.
*
* @param id the current component id (if -1, we'll generate a new one)
- *
* @return a usable component id
*/
private int getComponentId(int id) {
@@ -1225,63 +1508,64 @@
/**
* Add a component start tag
+ *
* @param type type of component
* @param id component id
*/
public void addComponentStart(int type, int id) {
mLastComponentId = getComponentId(id);
- ComponentStart.apply(mBuffer,
- type, mLastComponentId, 0f, 0f);
+ ComponentStart.apply(mBuffer, type, mLastComponentId, 0f, 0f);
}
/**
* Add a component start tag
+ *
* @param type type of component
*/
public void addComponentStart(int type) {
addComponentStart(type, -1);
}
- /**
- * Add a component end tag
- */
+ /** Add a component end tag */
public void addComponentEnd() {
ComponentEnd.apply(mBuffer);
}
/**
* Add a background modifier of provided color
+ *
* @param color the color of the background
* @param shape the background shape -- SHAPE_RECTANGLE, SHAPE_CIRCLE
*/
public void addModifierBackground(int color, int shape) {
- float r = ((color >> 16) & 0xff) / 255.0f;
- float g = ((color >> 8) & 0xff) / 255.0f;
- float b = ((color) & 0xff) / 255.0f;
- float a = ((color >> 24) & 0xff) / 255.0f;
- BackgroundModifierOperation.apply(mBuffer, 0f, 0f, 0f, 0f,
- r, g, b, a, shape);
+ float r = (color >> 16 & 0xff) / 255.0f;
+ float g = (color >> 8 & 0xff) / 255.0f;
+ float b = (color & 0xff) / 255.0f;
+ float a = (color >> 24 & 0xff) / 255.0f;
+ BackgroundModifierOperation.apply(mBuffer, 0f, 0f, 0f, 0f, r, g, b, a, shape);
}
/**
* Add a border modifier
+ *
* @param borderWidth the border width
* @param borderRoundedCorner the rounded corner radius if the shape is ROUNDED_RECT
* @param color the color of the border
* @param shape the shape of the border
*/
- public void addModifierBorder(float borderWidth, float borderRoundedCorner,
- int color, int shape) {
- float r = ((color >> 16) & 0xff) / 255.0f;
- float g = ((color >> 8) & 0xff) / 255.0f;
- float b = ((color) & 0xff) / 255.0f;
- float a = ((color >> 24) & 0xff) / 255.0f;
- BorderModifierOperation.apply(mBuffer, 0f, 0f, 0f, 0f,
- borderWidth, borderRoundedCorner, r, g, b, a, shape);
+ public void addModifierBorder(
+ float borderWidth, float borderRoundedCorner, int color, int shape) {
+ float r = (color >> 16 & 0xff) / 255.0f;
+ float g = (color >> 8 & 0xff) / 255.0f;
+ float b = (color & 0xff) / 255.0f;
+ float a = (color >> 24 & 0xff) / 255.0f;
+ BorderModifierOperation.apply(
+ mBuffer, 0f, 0f, 0f, 0f, borderWidth, borderRoundedCorner, r, g, b, a, shape);
}
/**
* Add a padding modifier
+ *
* @param left left padding
* @param top top padding
* @param right right padding
@@ -1293,24 +1577,36 @@
/**
* Sets the clip based on rounded clip rect
+ *
* @param topStart
* @param topEnd
* @param bottomStart
* @param bottomEnd
*/
- public void addRoundClipRectModifier(float topStart, float topEnd,
- float bottomStart, float bottomEnd) {
- RoundedClipRectModifierOperation.apply(mBuffer,
- topStart, topEnd, bottomStart, bottomEnd);
+ public void addRoundClipRectModifier(
+ float topStart, float topEnd, float bottomStart, float bottomEnd) {
+ RoundedClipRectModifierOperation.apply(mBuffer, topStart, topEnd, bottomStart, bottomEnd);
}
- /**
- * Add a clip rect modifier
- */
+ /** Add a clip rect modifier */
public void addClipRectModifier() {
ClipRectModifierOperation.apply(mBuffer);
}
+ public void addLoopStart(float count, float from, float step, int indexId) {
+ LoopOperation.apply(mBuffer, count, from, step, indexId);
+ }
+
+ public void addLoopEnd() {
+ LoopEnd.apply(mBuffer);
+ }
+
+ public void addStateLayout(
+ int componentId, int animationId, int horizontal, int vertical, int indexId) {
+ mLastComponentId = getComponentId(componentId);
+ StateLayout.apply(mBuffer, mLastComponentId, animationId, horizontal, vertical, indexId);
+ }
+
/**
* Add a box start tag
*
@@ -1319,11 +1615,9 @@
* @param horizontal horizontal alignment
* @param vertical vertical alignment
*/
- public void addBoxStart(int componentId, int animationId,
- int horizontal, int vertical) {
+ public void addBoxStart(int componentId, int animationId, int horizontal, int vertical) {
mLastComponentId = getComponentId(componentId);
- BoxLayout.apply(mBuffer, mLastComponentId, animationId,
- horizontal, vertical);
+ BoxLayout.apply(mBuffer, mLastComponentId, animationId, horizontal, vertical);
}
/**
@@ -1335,11 +1629,10 @@
* @param vertical vertical alignment
* @param spacedBy spacing between items
*/
- public void addRowStart(int componentId, int animationId,
- int horizontal, int vertical, float spacedBy) {
+ public void addRowStart(
+ int componentId, int animationId, int horizontal, int vertical, float spacedBy) {
mLastComponentId = getComponentId(componentId);
- RowLayout.apply(mBuffer, mLastComponentId, animationId,
- horizontal, vertical, spacedBy);
+ RowLayout.apply(mBuffer, mLastComponentId, animationId, horizontal, vertical, spacedBy);
}
/**
@@ -1351,15 +1644,15 @@
* @param vertical vertical alignment
* @param spacedBy spacing between items
*/
- public void addColumnStart(int componentId, int animationId,
- int horizontal, int vertical, float spacedBy) {
+ public void addColumnStart(
+ int componentId, int animationId, int horizontal, int vertical, float spacedBy) {
mLastComponentId = getComponentId(componentId);
- ColumnLayout.apply(mBuffer, mLastComponentId, animationId,
- horizontal, vertical, spacedBy);
+ ColumnLayout.apply(mBuffer, mLastComponentId, animationId, horizontal, vertical, spacedBy);
}
/**
* Add a canvas start tag
+ *
* @param componentId component id
* @param animationId animation id
*/
@@ -1370,6 +1663,7 @@
/**
* Add a canvas content start tag
+ *
* @param componentId component id
*/
public void addCanvasContentStart(int componentId) {
@@ -1377,17 +1671,13 @@
CanvasContent.apply(mBuffer, mLastComponentId);
}
- /**
- * Add a root start tag
- */
+ /** Add a root start tag */
public void addRootStart() {
mLastComponentId = getComponentId(-1);
RootLayoutComponent.apply(mBuffer, mLastComponentId);
}
- /**
- * Add a content start tag
- */
+ /** Add a content start tag */
public void addContentStart() {
mLastComponentId = getComponentId(-1);
LayoutComponentContent.apply(mBuffer, mLastComponentId);
@@ -1395,6 +1685,7 @@
/**
* Add a component width value
+ *
* @param id id of the value
*/
public void addComponentWidthValue(int id) {
@@ -1422,16 +1713,29 @@
* @param fontWeight font weight (1 to 1000, normal is 400)
* @param fontFamily font family or null
*/
- public void addTextComponentStart(int componentId, int animationId,
- int textId, int color, float fontSize,
- int fontStyle, float fontWeight, String fontFamily) {
+ public void addTextComponentStart(
+ int componentId,
+ int animationId,
+ int textId,
+ int color,
+ float fontSize,
+ int fontStyle,
+ float fontWeight,
+ String fontFamily) {
mLastComponentId = getComponentId(componentId);
int fontFamilyId = -1;
if (fontFamily != null) {
fontFamilyId = addText(fontFamily);
}
- TextLayout.apply(mBuffer, mLastComponentId, animationId, textId, color, fontSize,
- fontStyle, fontWeight, fontFamilyId);
+ TextLayout.apply(
+ mBuffer,
+ mLastComponentId,
+ animationId,
+ textId,
+ color,
+ fontSize,
+ fontStyle,
+ fontWeight,
+ fontFamilyId);
}
}
-
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeOperation.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeOperation.java
index c7ec3359..e60695f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeOperation.java
@@ -15,6 +15,4 @@
*/
package com.android.internal.widget.remotecompose.core;
-public interface RemoteComposeOperation extends Operation {
-
-}
+public interface RemoteComposeOperation extends Operation {}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
index 839522e..51445f2 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
@@ -15,30 +15,24 @@
*/
package com.android.internal.widget.remotecompose.core;
-import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_CONTINUOUS_SEC;
-import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_TIME_IN_MIN;
-import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_TIME_IN_SEC;
-import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_WINDOW_HEIGHT;
-import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_WINDOW_WIDTH;
-import static com.android.internal.widget.remotecompose.core.operations.utilities.NanMap.START_ARRAY;
-import static com.android.internal.widget.remotecompose.core.operations.utilities.NanMap.START_VAR;
-
import com.android.internal.widget.remotecompose.core.operations.utilities.ArrayAccess;
import com.android.internal.widget.remotecompose.core.operations.utilities.CollectionsAccess;
+import com.android.internal.widget.remotecompose.core.operations.utilities.DataMap;
import com.android.internal.widget.remotecompose.core.operations.utilities.IntFloatMap;
import com.android.internal.widget.remotecompose.core.operations.utilities.IntIntMap;
import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap;
+import com.android.internal.widget.remotecompose.core.operations.utilities.NanMap;
import java.util.ArrayList;
import java.util.HashMap;
/**
- * Represents runtime state for a RemoteCompose document
- * State includes things like the value of variables
+ * Represents runtime state for a RemoteCompose document State includes things like the value of
+ * variables
*/
public class RemoteComposeState implements CollectionsAccess {
public static final int START_ID = 42;
- private static final int MAX_FLOATS = 500;
+ // private static final int MAX_FLOATS = 500;
private static final int MAX_COLORS = 200;
private static final int MAX_DATA = 1000;
@@ -48,6 +42,8 @@
private final IntFloatMap mFloatMap = new IntFloatMap(); // efficient cache
private final IntIntMap mIntegerMap = new IntIntMap(); // efficient cache
private final IntIntMap mColorMap = new IntIntMap(); // efficient cache
+ private final IntMap<DataMap> mDataMapMap = new IntMap<>();
+ private final IntMap<Object> mObjectMap = new IntMap<>();
private final boolean[] mColorOverride = new boolean[MAX_COLORS];
private final IntMap<ArrayAccess> mCollectionMap = new IntMap<>();
@@ -56,13 +52,12 @@
private final boolean[] mIntegerOverride = new boolean[MAX_DATA];
private int mNextId = START_ID;
- private int[] mIdMaps = new int[]{START_ID, START_VAR, START_ARRAY};
+ private int[] mIdMaps = new int[] {START_ID, NanMap.START_VAR, NanMap.START_ARRAY};
private RemoteContext mRemoteContext = null;
-
/**
- * Get Object based on id. The system will cache things like bitmaps
- * Paths etc. They can be accessed with this command
+ * Get Object based on id. The system will cache things like bitmaps Paths etc. They can be
+ * accessed with this command
*
* @param id
* @return
@@ -81,9 +76,7 @@
return mIntDataMap.get(id) != null;
}
- /**
- * Return the id of an item from the cache.
- */
+ /** Return the id of an item from the cache. */
public int dataGetId(Object data) {
Integer res = mDataIntMap.get(data);
if (res == null) {
@@ -93,8 +86,8 @@
}
/**
- * Add an item to the cache. Generates an id for the item and adds it to the cache based on
- * that id.
+ * Add an item to the cache. Generates an id for the item and adds it to the cache based on that
+ * id.
*/
public int cacheData(Object item) {
int id = nextId();
@@ -104,8 +97,8 @@
}
/**
- * Add an item to the cache. Generates an id for the item and adds it to the cache based on
- * that id.
+ * Add an item to the cache. Generates an id for the item and adds it to the cache based on that
+ * id.
*/
public int cacheData(Object item, int type) {
int id = nextId(type);
@@ -114,23 +107,22 @@
return id;
}
- /**
- * Insert an item in the cache
- */
+ /** Insert an item in the cache */
public void cacheData(int id, Object item) {
mDataIntMap.put(item, id);
mIntDataMap.put(id, item);
}
- /**
- * Insert an item in the cache
- */
+ /** Insert an item in the cache */
public void updateData(int id, Object item) {
if (!mDataOverride[id]) {
- mDataIntMap.remove(mIntDataMap.get(id));
- mDataIntMap.put(item, id);
- mIntDataMap.put(id, item);
- updateListeners(id);
+ Object previous = mIntDataMap.get(id);
+ if (previous != item) {
+ mDataIntMap.remove(previous);
+ mDataIntMap.put(item, id);
+ mIntDataMap.put(id, item);
+ updateListeners(id);
+ }
}
}
@@ -141,16 +133,17 @@
* @param item the new value
*/
public void overrideData(int id, Object item) {
- mDataIntMap.remove(mIntDataMap.get(id));
- mDataIntMap.put(item, id);
- mIntDataMap.put(id, item);
- mDataOverride[id] = true;
- updateListeners(id);
+ Object previous = mIntDataMap.get(id);
+ if (previous != item) {
+ mDataIntMap.remove(previous);
+ mDataIntMap.put(item, id);
+ mIntDataMap.put(id, item);
+ mDataOverride[id] = true;
+ updateListeners(id);
+ }
}
- /**
- * Insert an item in the cache
- */
+ /** Insert an item in the cache */
public int cacheFloat(float item) {
int id = nextId();
mFloatMap.put(id, item);
@@ -158,25 +151,22 @@
return id;
}
- /**
- * Insert an item in the cache
- */
+ /** Insert an item in the cache */
public void cacheFloat(int id, float item) {
mFloatMap.put(id, item);
}
- /**
- * Insert an float item in the cache
- */
- public void updateFloat(int id, float item) {
- mFloatMap.put(id, item);
- mIntegerMap.put(id, (int) item);
- updateListeners(id);
+ /** Insert an float item in the cache */
+ public void updateFloat(int id, float value) {
+ float previous = mFloatMap.get(id);
+ if (previous != value) {
+ mFloatMap.put(id, value);
+ mIntegerMap.put(id, (int) value);
+ updateListeners(id);
+ }
}
- /**
- * Insert an item in the cache
- */
+ /** Insert an item in the cache */
public int cacheInteger(int item) {
int id = nextId();
mIntegerMap.put(id, item);
@@ -184,14 +174,15 @@
return id;
}
- /**
- * Insert an integer item in the cache
- */
- public void updateInteger(int id, int item) {
+ /** Insert an integer item in the cache */
+ public void updateInteger(int id, int value) {
if (!mIntegerOverride[id]) {
- mFloatMap.put(id, item);
- mIntegerMap.put(id, item);
- updateListeners(id);
+ int previous = mIntegerMap.get(id);
+ if (previous != value) {
+ mFloatMap.put(id, value);
+ mIntegerMap.put(id, value);
+ updateListeners(id);
+ }
}
}
@@ -202,10 +193,13 @@
* @param value the new value
*/
public void overrideInteger(int id, int value) {
- mIntegerMap.put(id, value);
- mFloatMap.put(id, value);
- mIntegerOverride[id] = true;
- updateListeners(id);
+ int previous = mIntegerMap.get(id);
+ if (previous != value) {
+ mIntegerMap.put(id, value);
+ mFloatMap.put(id, value);
+ mIntegerOverride[id] = true;
+ updateListeners(id);
+ }
}
/**
@@ -262,8 +256,7 @@
}
/**
- * Adds a colorOverride.
- * This is a list of ids and their colors optimized for playback;
+ * Adds a colorOverride. This is a list of ids and their colors optimized for playback;
*
* @param id
* @param color
@@ -273,9 +266,7 @@
mColorMap.put(id, color);
}
- /**
- * Clear the color Overrides
- */
+ /** Clear the color Overrides */
public void clearColorOverride() {
for (int i = 0; i < mColorOverride.length; i++) {
mColorOverride[i] = false;
@@ -310,16 +301,12 @@
return !mIntWrittenMap.get(id);
}
- /**
- * Method to mark that a value, represented by its id, has been written to the WireBuffer
- */
+ /** Method to mark that a value, represented by its id, has been written to the WireBuffer */
public void markWritten(int id) {
mIntWrittenMap.put(id, true);
}
- /**
- * Clear the record of the values that have been written to the WireBuffer.
- */
+ /** Clear the record of the values that have been written to the WireBuffer. */
public void reset() {
mIntWrittenMap.clear();
mDataIntMap.clear();
@@ -388,13 +375,13 @@
for (VariableSupport vs : mAllVarListeners) {
vs.updateVariables(context);
}
- if (mVarListeners.get(ID_CONTINUOUS_SEC) != null) {
+ if (mVarListeners.get(RemoteContext.ID_CONTINUOUS_SEC) != null) {
return 1;
}
- if (mVarListeners.get(ID_TIME_IN_SEC) != null) {
+ if (mVarListeners.get(RemoteContext.ID_TIME_IN_SEC) != null) {
return 1000;
}
- if (mVarListeners.get(ID_TIME_IN_MIN) != null) {
+ if (mVarListeners.get(RemoteContext.ID_TIME_IN_MIN) != null) {
return 1000 * 60;
}
return -1;
@@ -406,7 +393,7 @@
* @param width
*/
public void setWindowWidth(float width) {
- updateFloat(ID_WINDOW_WIDTH, width);
+ updateFloat(RemoteContext.ID_WINDOW_WIDTH, width);
}
/**
@@ -415,7 +402,7 @@
* @param height
*/
public void setWindowHeight(float height) {
- updateFloat(ID_WINDOW_HEIGHT, height);
+ updateFloat(RemoteContext.ID_WINDOW_HEIGHT, height);
}
public void addCollection(int id, ArrayAccess collection) {
@@ -426,17 +413,39 @@
public float getFloatValue(int id, int index) {
return mCollectionMap.get(id & 0xFFFFF).getFloatValue(index);
}
+
@Override
public float[] getFloats(int id) {
return mCollectionMap.get(id & 0xFFFFF).getFloats();
}
@Override
- public int getFloatsLength(int id) {
- return mCollectionMap.get(id & 0xFFFFF).getFloatsLength();
+ public int getId(int id, int index) {
+ return mCollectionMap.get(id & 0xFFFFF).getId(index);
+ }
+
+ public void putDataMap(int id, DataMap map) {
+ mDataMapMap.put(id, map);
+ }
+
+ public DataMap getDataMap(int id) {
+ return mDataMapMap.get(id);
+ }
+
+ @Override
+ public int getListLength(int id) {
+ return mCollectionMap.get(id & 0xFFFFF).getLength();
}
public void setContext(RemoteContext context) {
mRemoteContext = context;
}
+
+ public void updateObject(int id, Object value) {
+ mObjectMap.put(id, value);
+ }
+
+ public Object getObject(int id) {
+ return mObjectMap.get(id);
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
index 0df0aa0..1066e7d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
@@ -22,6 +22,7 @@
import com.android.internal.widget.remotecompose.core.operations.layout.Component;
import com.android.internal.widget.remotecompose.core.operations.utilities.ArrayAccess;
import com.android.internal.widget.remotecompose.core.operations.utilities.CollectionsAccess;
+import com.android.internal.widget.remotecompose.core.operations.utilities.DataMap;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
@@ -31,9 +32,9 @@
/**
* Specify an abstract context used to playback RemoteCompose documents
*
- * This allows us to intercept the different operations in a document and react to them.
+ * <p>This allows us to intercept the different operations in a document and react to them.
*
- * We also contain a PaintContext, so that any operation can draw as needed.
+ * <p>We also contain a PaintContext, so that any operation can draw as needed.
*/
public abstract class RemoteContext {
protected CoreDocument mDocument;
@@ -73,8 +74,7 @@
}
/**
- * Load a path under an id.
- * Paths can be use in clip drawPath and drawTweenPath
+ * Load a path under an id. Paths can be use in clip drawPath and drawTweenPath
*
* @param instanceId
* @param floatPath
@@ -85,7 +85,7 @@
* Associate a name with a give id.
*
* @param varName the name
- * @param varId the id (color,integer,float etc.)
+ * @param varId the id (color,integer,float etc.)
* @param varType thetype
*/
public abstract void loadVariableName(String varName, int varId, int varType);
@@ -93,7 +93,7 @@
/**
* Save a color under a given id
*
- * @param id the id of the color
+ * @param id the id of the color
* @param color the color to set
*/
public abstract void loadColor(int id, int color);
@@ -118,35 +118,33 @@
}
/**
- * Set the value of a named Color.
- * This overrides the color in the document
+ * Set the value of a named Color. This overrides the color in the document
*
* @param colorName the name of the color to override
- * @param color Override the default color
+ * @param color Override the default color
*/
public abstract void setNamedColorOverride(String colorName, int color);
/**
- * Set the value of a named String.
- * This overrides the string in the document
+ * Set the value of a named String. This overrides the string in the document
+ *
* @param stringName the name of the string to override
* @param value Override the default string
*/
public abstract void setNamedStringOverride(String stringName, String value);
-
/**
* Allows to clear a named String.
*
- * If an override exists, we revert back to the default value in the document.
+ * <p>If an override exists, we revert back to the default value in the document.
*
* @param stringName the name of the string to override
*/
public abstract void clearNamedStringOverride(String stringName);
/**
- * Set the value of a named Integer.
- * This overrides the integer in the document
+ * Set the value of a named Integer. This overrides the integer in the document
+ *
* @param integerName the name of the integer to override
* @param value Override the default integer
*/
@@ -155,34 +153,41 @@
/**
* Allows to clear a named Integer.
*
- * If an override exists, we revert back to the default value in the document.
+ * <p>If an override exists, we revert back to the default value in the document.
*
* @param integerName the name of the integer to override
*/
public abstract void clearNamedIntegerOverride(String integerName);
-
/**
* Support Collections by registering this collection
*
- * @param id id of the collection
+ * @param id id of the collection
* @param collection the collection under this id
*/
public abstract void addCollection(int id, ArrayAccess collection);
+ public abstract void putDataMap(int id, DataMap map);
+
+ public abstract DataMap getDataMap(int id);
+
public abstract void runAction(int id, String metadata);
public abstract void runNamedAction(int textId);
+ public abstract void putObject(int mId, Object command);
+
+ public abstract Object getObject(int mId);
/**
* The context can be used in a few different mode, allowing operations to skip being executed:
- * - UNSET : all operations will get executed
- * - DATA : only operations dealing with DATA (eg loading a bitmap) should execute
- * - PAINT : only operations painting should execute
+ * - UNSET : all operations will get executed - DATA : only operations dealing with DATA (eg
+ * loading a bitmap) should execute - PAINT : only operations painting should execute
*/
public enum ContextMode {
- UNSET, DATA, PAINT
+ UNSET,
+ DATA,
+ PAINT
}
public int getTheme() {
@@ -229,9 +234,13 @@
// Operations
///////////////////////////////////////////////////////////////////////////////////////////////
- public void header(int majorVersion, int minorVersion, int patchVersion,
- int width, int height, long capabilities
- ) {
+ public void header(
+ int majorVersion,
+ int minorVersion,
+ int patchVersion,
+ int width,
+ int height,
+ long capabilities) {
mRemoteComposeState.setWindowWidth(width);
mRemoteComposeState.setWindowHeight(height);
mDocument.setVersion(majorVersion, minorVersion, patchVersion);
@@ -243,21 +252,14 @@
/**
* Sets the way the player handles the content
*
- * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
+ * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
* @param alignment set the alignment of the content (TOP|CENTER|BOTTOM|START|END)
- * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
- * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes
- * the LAYOUT modes are:
- * - LAYOUT_MATCH_PARENT
- * - LAYOUT_WRAP_CONTENT
- * or adding an horizontal mode and a vertical mode:
- * - LAYOUT_HORIZONTAL_MATCH_PARENT
- * - LAYOUT_HORIZONTAL_WRAP_CONTENT
- * - LAYOUT_HORIZONTAL_FIXED
- * - LAYOUT_VERTICAL_MATCH_PARENT
- * - LAYOUT_VERTICAL_WRAP_CONTENT
- * - LAYOUT_VERTICAL_FIXED
- * The LAYOUT_*_FIXED modes will use the intrinsic document size
+ * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
+ * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes the LAYOUT modes are:
+ * - LAYOUT_MATCH_PARENT - LAYOUT_WRAP_CONTENT or adding an horizontal mode and a vertical
+ * mode: - LAYOUT_HORIZONTAL_MATCH_PARENT - LAYOUT_HORIZONTAL_WRAP_CONTENT -
+ * LAYOUT_HORIZONTAL_FIXED - LAYOUT_VERTICAL_MATCH_PARENT - LAYOUT_VERTICAL_WRAP_CONTENT -
+ * LAYOUT_VERTICAL_FIXED The LAYOUT_*_FIXED modes will use the intrinsic document size
*/
public void setRootContentBehavior(int scroll, int alignment, int sizing, int mode) {
mDocument.setRootContentBehavior(scroll, alignment, sizing, mode);
@@ -281,16 +283,16 @@
* Save a bitmap under an imageId
*
* @param imageId the id of the image
- * @param width the width of the image
- * @param height the height of the image
- * @param bitmap the bytes that represent the image
+ * @param width the width of the image
+ * @param height the height of the image
+ * @param bitmap the bytes that represent the image
*/
public abstract void loadBitmap(int imageId, int width, int height, byte[] bitmap);
/**
* Save a string under a given id
*
- * @param id the id of the string
+ * @param id the id of the string
* @param text the value to set
*/
public abstract void loadText(int id, String text);
@@ -306,7 +308,7 @@
/**
* Load a float
*
- * @param id id of the float
+ * @param id id of the float
* @param value the value to set
*/
public abstract void loadFloat(int id, float value);
@@ -314,21 +316,19 @@
/**
* Load a integer
*
- * @param id id of the integer
+ * @param id id of the integer
* @param value the value to set
*/
public abstract void loadInteger(int id, int value);
-
public abstract void overrideInteger(int id, int value);
public abstract void overrideText(int id, int valueId);
/**
- * Load an animated float associated with an id
- * Todo: Remove?
+ * Load an animated float associated with an id Todo: Remove?
*
- * @param id the id of the float
+ * @param id the id of the float
* @param animatedFloat The animated float
*/
public abstract void loadAnimatedFloat(int id, FloatExpression animatedFloat);
@@ -336,7 +336,7 @@
/**
* Save a shader under and ID
*
- * @param id the id of the Shader
+ * @param id the id of the Shader
* @param value the shader
*/
public abstract void loadShader(int id, ShaderData value);
@@ -368,7 +368,7 @@
/**
* called to notify system that a command is interested in a variable
*
- * @param id track when this id changes value
+ * @param id track when this id changes value
* @param variableSupport call back when value changes
*/
public abstract void listensTo(int id, VariableSupport variableSupport);
@@ -401,33 +401,25 @@
public static final int ID_WEEK_DAY = 11;
public static final int ID_DAY_OF_MONTH = 12;
- /**
- * CONTINUOUS_SEC is seconds from midnight looping every hour 0-3600
- */
+ /** CONTINUOUS_SEC is seconds from midnight looping every hour 0-3600 */
public static final float FLOAT_CONTINUOUS_SEC = Utils.asNan(ID_CONTINUOUS_SEC);
- /**
- * seconds run from Midnight=0 quantized to seconds hour 0..3599
- */
+
+ /** seconds run from Midnight=0 quantized to seconds hour 0..3599 */
public static final float FLOAT_TIME_IN_SEC = Utils.asNan(ID_TIME_IN_SEC);
- /**
- * minutes run from Midnight=0 quantized to minutes 0..1439
- */
+
+ /** minutes run from Midnight=0 quantized to minutes 0..1439 */
public static final float FLOAT_TIME_IN_MIN = Utils.asNan(ID_TIME_IN_MIN);
- /**
- * hours run from Midnight=0 quantized to Hours 0-23
- */
+
+ /** hours run from Midnight=0 quantized to Hours 0-23 */
public static final float FLOAT_TIME_IN_HR = Utils.asNan(ID_TIME_IN_HR);
- /**
- * Moth of Year quantized to MONTHS 1-12. 1 = January
- */
+
+ /** Moth of Year quantized to MONTHS 1-12. 1 = January */
public static final float FLOAT_CALENDAR_MONTH = Utils.asNan(ID_CALENDAR_MONTH);
- /**
- * DAY OF THE WEEK 1-7. 1 = Monday
- */
+
+ /** DAY OF THE WEEK 1-7. 1 = Monday */
public static final float FLOAT_WEEK_DAY = Utils.asNan(ID_WEEK_DAY);
- /**
- * DAY OF THE MONTH 1-31
- */
+
+ /** DAY OF THE MONTH 1-31 */
public static final float FLOAT_DAY_OF_MONTH = Utils.asNan(ID_DAY_OF_MONTH);
public static final float FLOAT_WINDOW_WIDTH = Utils.asNan(ID_WINDOW_WIDTH);
@@ -446,7 +438,8 @@
}
public static float getTime(float fl) {
- LocalDateTime dateTime = LocalDateTime.now();
+ LocalDateTime dateTime =
+ LocalDateTime.now(ZoneId.systemDefault()); // TODO, pass in a timezone explicitly?
// This define the time in the format
// seconds run from Midnight=0 quantized to seconds hour 0..3599
// minutes run from Midnight=0 quantized to minutes 0..1439
@@ -464,7 +457,6 @@
float sec = currentSeconds + dateTime.getNano() * 1E-9f;
int day_week = dateTime.getDayOfWeek().getValue();
-
ZoneId zone = ZoneId.systemDefault();
OffsetDateTime offsetDateTime = dateTime.atZone(zone).toOffsetDateTime();
ZoneOffset offset = offsetDateTime.getOffset();
@@ -488,8 +480,6 @@
return fl;
}
-
-
public abstract void addClickArea(
int id,
int contentDescription,
@@ -497,7 +487,5 @@
float top,
float right,
float bottom,
- int metadataId
- );
+ int metadataId);
}
-
diff --git a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
index 04e04bbb..fa0cf3f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
@@ -20,9 +20,7 @@
import java.time.ZoneId;
import java.time.ZoneOffset;
-/**
- * This generates the standard system variables for time.
- */
+/** This generates the standard system variables for time. */
public class TimeVariables {
/**
* This class populates all time variables in the system
@@ -30,7 +28,8 @@
* @param context
*/
public void updateTime(RemoteContext context) {
- LocalDateTime dateTime = LocalDateTime.now();
+ LocalDateTime dateTime =
+ LocalDateTime.now(ZoneId.systemDefault()); // TODO, pass in a timezone explicitly?
// This define the time in the format
// seconds run from Midnight=0 quantized to seconds hour 0..3599
// minutes run from Midnight=0 quantized to minutes 0..1439
@@ -47,7 +46,6 @@
float sec = currentSeconds + dateTime.getNano() * 1E-9f;
int day_week = dateTime.getDayOfWeek().getValue();
-
ZoneId zone = ZoneId.systemDefault();
OffsetDateTime offsetDateTime = dateTime.atZone(zone).toOffsetDateTime();
ZoneOffset offset = offsetDateTime.getOffset();
@@ -60,6 +58,5 @@
context.loadFloat(RemoteContext.ID_CALENDAR_MONTH, month);
context.loadFloat(RemoteContext.ID_DAY_OF_MONTH, month);
context.loadFloat(RemoteContext.ID_WEEK_DAY, day_week);
-
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java b/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java
index ee6d579..51e58a1 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java
@@ -16,20 +16,21 @@
package com.android.internal.widget.remotecompose.core;
/**
- * Interface for operators that interact with variables
- * Through this they register to listen to particular variables
- * and are notified when they change
+ * Interface for operators that interact with variables Through this they register to listen to
+ * particular variables and are notified when they change
*/
public interface VariableSupport {
/**
- * Call to allow an operator to register interest in variables.
- * Typically they call context.listensTo(id, this)
+ * Call to allow an operator to register interest in variables. Typically they call
+ * context.listensTo(id, this)
+ *
* @param context
*/
void registerListening(RemoteContext context);
/**
* Called to be notified that the variables you are interested have changed.
+ *
* @param context
*/
void updateVariables(RemoteContext context);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java
index fc3202e..c71b490 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java
@@ -17,9 +17,7 @@
import java.util.Arrays;
-/**
- * The base communication buffer capable of encoding and decoding various types
- */
+/** The base communication buffer capable of encoding and decoding various types */
public class WireBuffer {
private static final int BUFFER_SIZE = 1024 * 1024 * 1;
int mMaxSize;
@@ -130,6 +128,7 @@
int v2 = (mBuffer[mIndex++] & 0xFF) << 0;
return v1 + v2;
}
+
public int peekInt() {
int tmp = mIndex;
int v1 = (mBuffer[tmp++] & 0xFF) << 24;
@@ -177,8 +176,8 @@
public byte[] readBuffer(int maxSize) {
int count = readInt();
if (count < 0 || count > maxSize) {
- throw new RuntimeException("attempt read a buff of invalid size 0 <= "
- + count + " > " + maxSize);
+ throw new RuntimeException(
+ "attempt read a buff of invalid size 0 <= " + count + " > " + maxSize);
}
byte[] b = Arrays.copyOfRange(mBuffer, mIndex, mIndex + count);
mIndex += count;
@@ -201,7 +200,7 @@
public void writeBoolean(boolean value) {
resize(1);
- mBuffer[mIndex++] = (byte) ((value) ? 1 : 0);
+ mBuffer[mIndex++] = (byte) (value ? 1 : 0);
mSize++;
}
@@ -256,7 +255,6 @@
writeInt(b.length);
for (int i = 0; i < b.length; i++) {
mBuffer[mIndex++] = b[i];
-
}
mSize += b.length;
}
@@ -265,6 +263,4 @@
byte[] buffer = content.getBytes();
writeBuffer(buffer);
}
-
}
-
diff --git a/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentationBuilder.java b/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentationBuilder.java
index ccbcdf6..f6dfe2e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentationBuilder.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentationBuilder.java
@@ -17,6 +17,8 @@
public interface DocumentationBuilder {
void add(String value);
- Operation operation(String category, int id, String name);
- Operation wipOperation(String category, int id, String name);
+
+ DocumentedOperation operation(String category, int id, String name);
+
+ DocumentedOperation wipOperation(String category, int id, String name);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentedOperation.java b/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentedOperation.java
new file mode 100644
index 0000000..c33ae24
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentedOperation.java
@@ -0,0 +1,181 @@
+/*
+ * 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.internal.widget.remotecompose.core.documentation;
+
+import java.util.ArrayList;
+
+public class DocumentedOperation {
+ public static final int LAYOUT = 0;
+ public static final int INT = 0;
+ public static final int FLOAT = 1;
+ public static final int BOOLEAN = 2;
+ public static final int BUFFER = 4;
+ public static final int UTF8 = 5;
+ public static final int BYTE = 6;
+ public static final int VALUE = 7;
+ public static final int LONG = 8;
+ public static final int SHORT = 9;
+
+ public static final int FLOAT_ARRAY = 10;
+ public static final int INT_ARRAY = 11;
+
+ String mCategory;
+ int mId;
+ String mName;
+ String mDescription;
+
+ boolean mWIP;
+ String mTextExamples;
+
+ ArrayList<StringPair> mExamples = new ArrayList<>();
+ ArrayList<OperationField> mFields = new ArrayList<>();
+ String mVarSize = "";
+ int mExamplesWidth = 100;
+ int mExamplesHeight = 100;
+
+ public static String getType(int type) {
+ switch (type) {
+ case INT:
+ return "INT";
+ case FLOAT:
+ return "FLOAT";
+ case BOOLEAN:
+ return "BOOLEAN";
+ case BUFFER:
+ return "BUFFER";
+ case UTF8:
+ return "UTF8";
+ case BYTE:
+ return "BYTE";
+ case VALUE:
+ return "VALUE";
+ case LONG:
+ return "LONG";
+ case SHORT:
+ return "SHORT";
+ case FLOAT_ARRAY:
+ return "FLOAT[]";
+ case INT_ARRAY:
+ return "INT[]";
+ }
+ return "UNKNOWN";
+ }
+
+ public DocumentedOperation(String category, int id, String name, boolean wip) {
+ mCategory = category;
+ mId = id;
+ mName = name;
+ mWIP = wip;
+ }
+
+ public DocumentedOperation(String category, int id, String name) {
+ this(category, id, name, false);
+ }
+
+ public ArrayList<OperationField> getFields() {
+ return mFields;
+ }
+
+ public String getCategory() {
+ return mCategory;
+ }
+
+ public int getId() {
+ return mId;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public boolean isWIP() {
+ return mWIP;
+ }
+
+ public String getVarSize() {
+ return mVarSize;
+ }
+
+ public int getSizeFields() {
+ int size = 0;
+ mVarSize = "";
+ for (OperationField field : mFields) {
+ size += Math.max(0, field.getSize());
+ if (field.getSize() < 0) {
+ mVarSize += " + " + field.getVarSize() + " x 4";
+ }
+ }
+ return size;
+ }
+
+ public String getDescription() {
+ return mDescription;
+ }
+
+ public String getTextExamples() {
+ return mTextExamples;
+ }
+
+ public ArrayList<StringPair> getExamples() {
+ return mExamples;
+ }
+
+ public int getExamplesWidth() {
+ return mExamplesWidth;
+ }
+
+ public int getExamplesHeight() {
+ return mExamplesHeight;
+ }
+
+ public DocumentedOperation field(int type, String name, String description) {
+ mFields.add(new OperationField(type, name, description));
+ return this;
+ }
+
+ public DocumentedOperation field(int type, String name, String varSize, String description) {
+ mFields.add(new OperationField(type, name, varSize, description));
+ return this;
+ }
+
+ public DocumentedOperation possibleValues(String name, int value) {
+ if (!mFields.isEmpty()) {
+ mFields.get(mFields.size() - 1).possibleValue(name, "" + value);
+ }
+ return this;
+ }
+
+ public DocumentedOperation description(String description) {
+ mDescription = description;
+ return this;
+ }
+
+ public DocumentedOperation examples(String examples) {
+ mTextExamples = examples;
+ return this;
+ }
+
+ public DocumentedOperation exampleImage(String name, String imagePath) {
+ mExamples.add(new StringPair(name, imagePath));
+ return this;
+ }
+
+ public DocumentedOperation examplesDimension(int width, int height) {
+ mExamplesWidth = width;
+ mExamplesHeight = height;
+ return this;
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/documentation/Operation.java b/core/java/com/android/internal/widget/remotecompose/core/documentation/Operation.java
index 9ccb2be..f02a385 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/documentation/Operation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/documentation/Operation.java
@@ -46,20 +46,30 @@
int mExamplesWidth = 100;
int mExamplesHeight = 100;
-
public static String getType(int type) {
switch (type) {
- case (INT): return "INT";
- case (FLOAT): return "FLOAT";
- case (BOOLEAN): return "BOOLEAN";
- case (BUFFER): return "BUFFER";
- case (UTF8): return "UTF8";
- case (BYTE): return "BYTE";
- case (VALUE): return "VALUE";
- case (LONG): return "LONG";
- case (SHORT): return "SHORT";
- case (FLOAT_ARRAY): return "FLOAT[]";
- case (INT_ARRAY): return "INT[]";
+ case INT:
+ return "INT";
+ case FLOAT:
+ return "FLOAT";
+ case BOOLEAN:
+ return "BOOLEAN";
+ case BUFFER:
+ return "BUFFER";
+ case UTF8:
+ return "UTF8";
+ case BYTE:
+ return "BYTE";
+ case VALUE:
+ return "VALUE";
+ case LONG:
+ return "LONG";
+ case SHORT:
+ return "SHORT";
+ case FLOAT_ARRAY:
+ return "FLOAT[]";
+ case INT_ARRAY:
+ return "INT[]";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/documentation/OperationField.java b/core/java/com/android/internal/widget/remotecompose/core/documentation/OperationField.java
index 0dd3039..c770483 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/documentation/OperationField.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/documentation/OperationField.java
@@ -41,20 +41,23 @@
public int getType() {
return mType;
}
+
public String getName() {
return mName;
}
+
public String getDescription() {
return mDescription;
}
+
public ArrayList<StringPair> getPossibleValues() {
return mPossibleValues;
}
-
public void possibleValue(String name, String value) {
mPossibleValues.add(new StringPair(name, value));
}
+
public boolean hasEnumeratedValues() {
return !mPossibleValues.isEmpty();
}
@@ -65,14 +68,22 @@
public int getSize() {
switch (mType) {
- case (Operation.BYTE) : return 1;
- case (Operation.INT) : return 4;
- case (Operation.FLOAT) : return 4;
- case (Operation.LONG) : return 8;
- case (Operation.SHORT) : return 2;
- case (Operation.INT_ARRAY): return -1;
- case (Operation.FLOAT_ARRAY): return -1;
- default : return 0;
+ case DocumentedOperation.BYTE:
+ return 1;
+ case DocumentedOperation.INT:
+ return 4;
+ case DocumentedOperation.FLOAT:
+ return 4;
+ case DocumentedOperation.LONG:
+ return 8;
+ case DocumentedOperation.SHORT:
+ return 2;
+ case DocumentedOperation.INT_ARRAY:
+ return -1;
+ case DocumentedOperation.FLOAT_ARRAY:
+ return -1;
+ default:
+ return 0;
}
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/documentation/StringPair.java b/core/java/com/android/internal/widget/remotecompose/core/documentation/StringPair.java
index 787bb54..5b0cedb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/documentation/StringPair.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/documentation/StringPair.java
@@ -14,6 +14,7 @@
* limitations under the License.
*/
package com.android.internal.widget.remotecompose.core.documentation;
+
public class StringPair {
String mName;
String mValue;
@@ -26,6 +27,7 @@
public String getName() {
return mName;
}
+
public String getValue() {
return mValue;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
index 58be641..20ba8c31 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT_ARRAY;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,14 +24,14 @@
import com.android.internal.widget.remotecompose.core.SerializableToString;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
import java.util.List;
/**
- * Operation to deal with bitmap data
- * On getting an Image during a draw call the bitmap is compressed and saved
- * in playback the image is decompressed
+ * Operation to deal with bitmap data On getting an Image during a draw call the bitmap is
+ * compressed and saved in playback the image is decompressed
*/
public class BitmapData implements Operation, SerializableToString {
private static final int OP_CODE = Operations.DATA_BITMAP;
@@ -67,7 +67,6 @@
return "BITMAP DATA " + mImageId;
}
-
public static String name() {
return CLASS_NAME;
}
@@ -84,7 +83,6 @@
buffer.writeBuffer(bitmap);
}
-
public static void read(WireBuffer buffer, List<Operation> operations) {
int imageId = buffer.readInt();
int width = buffer.readInt();
@@ -99,19 +97,13 @@
operations.add(new BitmapData(imageId, width, height, bitmap));
}
-
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Data Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Bitmap data")
- .field(INT, "id", "id of bitmap data")
- .field(INT, "width",
- "width of the image")
- .field(INT, "height",
- "height of the image")
- .field(INT_ARRAY, "values", "length",
- "Array of ints");
+ .field(DocumentedOperation.INT, "id", "id of bitmap data")
+ .field(INT, "width", "width of the image")
+ .field(INT, "height", "height of the image")
+ .field(INT_ARRAY, "values", "length", "Array of ints");
}
@Override
@@ -124,9 +116,10 @@
return indent + toString();
}
+ @Override
public void serializeToString(int indent, StringSerializer serializer) {
- serializer.append(indent, CLASS_NAME
- + " id " + mImageId + " (" + mImageWidth + "x" + mImageHeight + ")");
+ serializer.append(
+ indent,
+ CLASS_NAME + " id " + mImageId + " (" + mImageWidth + "x" + mImageHeight + ")");
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java
index e72e24a..8b9e5a8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java
@@ -15,20 +15,17 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteComposeOperation;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Add a click area to the document
- */
+/** Add a click area to the document */
public class ClickArea implements RemoteComposeOperation {
private static final int OP_CODE = Operations.CLICK_AREA;
private static final String CLASS_NAME = "ClickArea";
@@ -43,8 +40,8 @@
/**
* Add a click area to the document
*
- * @param id the id of the click area, which will be reported in the listener
- * callback on the player
+ * @param id the id of the click area, which will be reported in the listener callback on the
+ * player
* @param contentDescription the content description (used for accessibility, as a textID)
* @param left left coordinate of the area bounds
* @param top top coordinate of the area bounds
@@ -52,10 +49,14 @@
* @param bottom bottom coordinate of the area bounds
* @param metadata associated metadata, user-provided (as a textID, pointing to a string)
*/
- public ClickArea(int id, int contentDescription,
- float left, float top,
- float right, float bottom,
- int metadata) {
+ public ClickArea(
+ int id,
+ int contentDescription,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ int metadata) {
this.mId = id;
this.mContentDescription = contentDescription;
this.mLeft = left;
@@ -72,10 +73,27 @@
@Override
public String toString() {
- return "CLICK_AREA <" + mId + " <" + mContentDescription + "> "
- + "<" + mMetadata + ">+" + mLeft + " "
- + mTop + " " + mRight + " " + mBottom + "+"
- + " (" + (mRight - mLeft) + " x " + (mBottom - mTop) + " }";
+ return "CLICK_AREA <"
+ + mId
+ + " <"
+ + mContentDescription
+ + "> "
+ + "<"
+ + mMetadata
+ + ">+"
+ + mLeft
+ + " "
+ + mTop
+ + " "
+ + mRight
+ + " "
+ + mBottom
+ + "+"
+ + " ("
+ + (mRight - mLeft)
+ + " x "
+ + (mBottom - mTop)
+ + " }";
}
@Override
@@ -95,14 +113,19 @@
return CLASS_NAME;
}
-
public static int id() {
return OP_CODE;
}
- public static void apply(WireBuffer buffer, int id, int contentDescription,
- float left, float top, float right, float bottom,
- int metadata) {
+ public static void apply(
+ WireBuffer buffer,
+ int id,
+ int contentDescription,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ int metadata) {
buffer.start(OP_CODE);
buffer.writeInt(id);
buffer.writeInt(contentDescription);
@@ -113,7 +136,6 @@
buffer.writeInt(metadata);
}
-
public static void read(WireBuffer buffer, List<Operation> operations) {
int id = buffer.readInt();
int contentDescription = buffer.readInt();
@@ -122,26 +144,21 @@
float right = buffer.readFloat();
float bottom = buffer.readFloat();
int metadata = buffer.readInt();
- ClickArea clickArea = new ClickArea(id, contentDescription,
- left, top, right, bottom, metadata);
+ ClickArea clickArea =
+ new ClickArea(id, contentDescription, left, top, right, bottom, metadata);
operations.add(clickArea);
}
-
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Define a region you can click on")
- .field(FLOAT, "left",
- "The left side of the region")
- .field(FLOAT, "top",
- "The top of the region")
- .field(FLOAT, "right",
- "The right side of the region")
- .field(FLOAT, "bottom",
- "The bottom of the region")
- .field(FLOAT, "metadata",
+ .field(DocumentedOperation.FLOAT, "left", "The left side of the region")
+ .field(DocumentedOperation.FLOAT, "top", "The top of the region")
+ .field(DocumentedOperation.FLOAT, "right", "The right side of the region")
+ .field(DocumentedOperation.FLOAT, "bottom", "The bottom of the region")
+ .field(
+ DocumentedOperation.FLOAT,
+ "metadata",
"user defined string accessible in callback");
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
index d77d53c..96b600a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
@@ -15,21 +15,19 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.PaintOperation;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
/**
- * Defines a path that clips a the subsequent drawing commands
- * Use MatrixSave and MatrixRestore commands to remove clip
- * TODO allow id 0 to mean null?
+ * Defines a path that clips a the subsequent drawing commands Use MatrixSave and MatrixRestore
+ * commands to remove clip TODO allow id 0 to mean null?
*/
public class ClipPath extends PaintOperation {
private static final int OP_CODE = Operations.CLIP_PATH;
@@ -68,7 +66,6 @@
return "ClipPath " + mId + ";";
}
-
public static void read(WireBuffer buffer, List<Operation> operations) {
int pack = buffer.readInt();
int id = pack & 0xFFFFF;
@@ -77,12 +74,10 @@
operations.add(op);
}
-
public static String name() {
return CLASS_NAME;
}
-
public static int id() {
return OP_CODE;
}
@@ -93,17 +88,13 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Intersect the current clip with the path")
- .field(INT, "id",
- "id of the path");
+ .field(DocumentedOperation.INT, "id", "id of the path");
}
-
@Override
public void paint(PaintContext context) {
context.clipPath(mId, mRegionOp);
}
-}
\ No newline at end of file
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java
index ec9b6fe..b101bfb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java
@@ -15,24 +15,20 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Support clip with a rectangle
- */
+/** Support clip with a rectangle */
public class ClipRect extends DrawBase4 {
public static final int OP_CODE = Operations.CLIP_RECT;
public static final String CLASS_NAME = "ClipRect";
-
public static void read(WireBuffer buffer, List<Operation> operations) {
Maker m = ClipRect::new;
read(m, buffer, operations);
@@ -46,36 +42,33 @@
return CLASS_NAME;
}
-
- protected void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3,
- float v4) {
+ @Override
+ protected void write(WireBuffer buffer, float v1, float v2, float v3, float v4) {
apply(buffer, v1, v2, v3, v4);
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Expressions Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("Intersect the current clip with rectangle")
- .field(FLOAT, "left",
+ .field(
+ DocumentedOperation.FLOAT,
+ "left",
"The left side of the rectangle to intersect with the current clip")
- .field(FLOAT, "top",
+ .field(
+ DocumentedOperation.FLOAT,
+ "top",
"The top of the rectangle to intersect with the current clip")
- .field(FLOAT, "right",
+ .field(
+ DocumentedOperation.FLOAT,
+ "right",
"The right side of the rectangle to intersect with the current clip")
- .field(FLOAT, "bottom",
+ .field(
+ DocumentedOperation.FLOAT,
+ "bottom",
"The bottom of the rectangle to intersect with the current clip");
}
-
- public ClipRect(
- float left,
- float top,
- float right,
- float bottom) {
+ public ClipRect(float left, float top, float right, float bottom) {
super(left, top, right, bottom);
mName = CLASS_NAME;
}
@@ -89,16 +82,12 @@
* Writes out the clipRect to the buffer
*
* @param buffer buffer to write to
- * @param x1 start x of DrawOval
- * @param y1 start y of the DrawOval
- * @param x2 end x of the DrawOval
- * @param y2 end y of the DrawOval
+ * @param x1 start x of DrawOval
+ * @param y1 start y of the DrawOval
+ * @param x2 end x of the DrawOval
+ * @param y2 end y of the DrawOval
*/
- public static void apply(WireBuffer buffer,
- float x1,
- float y1,
- float x2,
- float y2) {
+ public static void apply(WireBuffer buffer, float x1, float y1, float x2, float y2) {
write(buffer, OP_CODE, x1, y1, x2, y2);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java
index 2562e18..19d80da 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java
@@ -15,20 +15,18 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Operation that defines a simple Color based on ID
- * Mainly for colors in theming.
- */
+/** Operation that defines a simple Color based on ID Mainly for colors in theming. */
public class ColorConstant implements Operation {
private static final int OP_CODE = Operations.COLOR_CONSTANT;
private static final String CLASS_NAME = "ColorConstant";
@@ -78,14 +76,10 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Expressions Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("Define a Color")
- .field(INT, "id",
- "Id of the color")
- .field(INT, "color",
- "32 bit ARGB color");
+ .field(DocumentedOperation.INT, "id", "Id of the color")
+ .field(INT, "color", "32 bit ARGB color");
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
index 96d6674..b6041ea 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,17 +24,13 @@
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
/**
- * Operation to Colors
- * Color modes
- * mMode = 0 two colors and a tween
- * mMode = 1 color1 is a colorID.
- * mMode = 2 color2 is a colorID.
- * mMode = 3 color1 & color2 are ids
- * mMode = 4 H S V mode
+ * Operation to Colors Color modes mMode = 0 two colors and a tween mMode = 1 color1 is a colorID.
+ * mMode = 2 color2 is a colorID. mMode = 3 color1 & color2 are ids mMode = 4 H S V mode
*/
public class ColorExpression implements Operation, VariableSupport {
private static final int OP_CODE = Operations.COLOR_EXPRESSIONS;
@@ -45,7 +41,6 @@
public int mColor2;
public float mTween = 0.0f;
-
public float mHue = 0; // only in Mode 4
public float mSat = 0;
public float mValue = 0;
@@ -122,7 +117,6 @@
}
}
-
@Override
public void registerListening(RemoteContext context) {
if (mMode == 4) {
@@ -151,8 +145,8 @@
@Override
public void apply(RemoteContext context) {
if (mMode == 4) {
- context.loadColor(mId, (mAlpha << 24)
- | (0xFFFFFF & Utils.hsvToRgb(mOutHue, mOutSat, mOutValue)));
+ context.loadColor(
+ mId, (mAlpha << 24) | (0xFFFFFF & Utils.hsvToRgb(mOutHue, mOutSat, mOutValue)));
return;
}
if (mOutTween == 0.0) {
@@ -165,8 +159,7 @@
mOutColor2 = context.getColor(mColor2);
}
- context.loadColor(mId,
- Utils.interpolateColor(mOutColor1, mOutColor2, mOutTween));
+ context.loadColor(mId, Utils.interpolateColor(mOutColor1, mOutColor2, mOutTween));
}
}
@@ -179,23 +172,34 @@
@Override
public String toString() {
if (mMode == 4) {
- return "ColorExpression[" + mId + "] = hsv (" + Utils.floatToString(mHue)
- + ", " + Utils.floatToString(mSat)
- + ", " + Utils.floatToString(mValue) + ")";
+ return "ColorExpression["
+ + mId
+ + "] = hsv ("
+ + Utils.floatToString(mHue)
+ + ", "
+ + Utils.floatToString(mSat)
+ + ", "
+ + Utils.floatToString(mValue)
+ + ")";
}
String c1 = (mMode & 1) == 1 ? "[" + mColor1 + "]" : Utils.colorInt(mColor1);
String c2 = (mMode & 2) == 2 ? "[" + mColor2 + "]" : Utils.colorInt(mColor2);
- return "ColorExpression[" + mId + "] = tween(" + c1
- + ", " + c2 + ", "
- + Utils.floatToString(mTween) + ")";
+ return "ColorExpression["
+ + mId
+ + "] = tween("
+ + c1
+ + ", "
+ + c2
+ + ", "
+ + Utils.floatToString(mTween)
+ + ")";
}
public static String name() {
return CLASS_NAME;
}
-
public static int id() {
return OP_CODE;
}
@@ -204,22 +208,20 @@
* Call to write a ColorExpression object on the buffer
*
* @param buffer
- * @param id of the ColorExpression object
- * @param mode if colors are id or actual values
+ * @param id of the ColorExpression object
+ * @param mode if colors are id or actual values
* @param color1
* @param color2
* @param tween
*/
- public static void apply(WireBuffer buffer,
- int id, int mode,
- int color1, int color2, float tween) {
+ public static void apply(
+ WireBuffer buffer, int id, int mode, int color1, int color2, float tween) {
buffer.start(OP_CODE);
buffer.writeInt(id);
buffer.writeInt(mode);
buffer.writeInt(color1);
buffer.writeInt(color2);
buffer.writeFloat(tween);
-
}
public static void read(WireBuffer buffer, List<Operation> operations) {
@@ -233,29 +235,22 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Expressions Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("A Color defined by an expression")
- .field(INT, "id", "Id of the color")
+ .field(DocumentedOperation.INT, "id", "Id of the color")
.field(INT, "mode", "The use of the next 3 fields")
.possibleValues("COLOR_COLOR_INTERPOLATE", 0)
.possibleValues("COLOR_ID_INTERPOLATE", 1)
.possibleValues("ID_COLOR_INTERPOLATE", 2)
.possibleValues("ID_ID_INTERPOLATE", 3)
.possibleValues("HSV", 4)
- .field(INT, "color1",
- "32 bit ARGB color")
- .field(INT, "color2",
- "32 bit ARGB color")
- .field(FLOAT, "tween",
- "32 bit ARGB color");
-
+ .field(INT, "color1", "32 bit ARGB color")
+ .field(INT, "color2", "32 bit ARGB color")
+ .field(FLOAT, "tween", "32 bit ARGB color");
}
@Override
public String deepToString(String indent) {
return indent + toString();
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java
index 22fe673..9929720 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -23,6 +23,7 @@
import com.android.internal.widget.remotecompose.core.SerializableToString;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
import java.util.List;
@@ -82,15 +83,16 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Expressions Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("Encode a component-related value (eg its width, height etc.)")
- .field(INT, "TYPE",
+ .field(
+ DocumentedOperation.INT,
+ "TYPE",
"The type of value, either WIDTH(0) or HEIGHT(1)")
- .field(INT, "COMPONENT_ID",
- "The component id to reference")
- .field(INT, "VALUE_ID",
+ .field(INT, "COMPONENT_ID", "The component id to reference")
+ .field(
+ INT,
+ "VALUE_ID",
"The id of the RemoteFloat representing the described"
+ " component value, which can be used in expressions");
}
@@ -105,14 +107,11 @@
* Writes out the ComponentValue to the buffer
*
* @param buffer buffer to write to
- * @param type type of value (WIDTH or HEIGHT)
- * @param componentId component id to reference
- * @param valueId remote float used to represent the component value
+ * @param type type of value (WIDTH or HEIGHT)
+ * @param componentId component id to reference
+ * @param valueId remote float used to represent the component value
*/
- public static void apply(WireBuffer buffer,
- int type,
- int componentId,
- int valueId) {
+ public static void apply(WireBuffer buffer, int type, int componentId, int valueId) {
buffer.start(OP_CODE);
buffer.writeInt(type);
buffer.writeInt(componentId);
@@ -124,13 +123,20 @@
return null;
}
+ @Override
public void serializeToString(int indent, StringSerializer serializer) {
String type = "WIDTH";
if (mType == HEIGHT) {
type = "HEIGHT";
}
- serializer.append(indent, CLASS_NAME
- + " value " + mValueId + " set to "
- + type + " of Component " + mComponentID);
+ serializer.append(
+ indent,
+ CLASS_NAME
+ + " value "
+ + mValueId
+ + " set to "
+ + type
+ + " of Component "
+ + mComponentID);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java
index edcb5fe..0075869 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT_ARRAY;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,6 +24,7 @@
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import com.android.internal.widget.remotecompose.core.operations.utilities.ArrayAccess;
import java.util.Arrays;
@@ -63,7 +64,7 @@
@Override
public String toString() {
- return "DataListFloat[A_" + (mId & 0xFFFF) + "] " + Arrays.toString(mValues);
+ return "DataListFloat[" + Utils.idString(mId) + "] " + Arrays.toString(mValues);
}
public static void apply(WireBuffer buffer, int id, float[] values) {
@@ -90,14 +91,11 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Data Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("a list of Floats")
- .field(INT, "id", "id the array (2xxxxx)")
+ .field(DocumentedOperation.INT, "id", "id the array (2xxxxx)")
.field(INT, "length", "number of floats")
- .field(FLOAT_ARRAY, "values", "length",
- "array of floats");
+ .field(FLOAT_ARRAY, "values", "length", "array of floats");
}
@Override
@@ -121,7 +119,7 @@
}
@Override
- public int getFloatsLength() {
+ public int getLength() {
return mValues.length;
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java
index bde376e..c43dab4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT_ARRAY;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,39 +24,29 @@
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import com.android.internal.widget.remotecompose.core.operations.utilities.ArrayAccess;
import java.util.Arrays;
import java.util.List;
-public class DataListIds implements VariableSupport, ArrayAccess, Operation {
+public class DataListIds implements VariableSupport, ArrayAccess, Operation {
private static final int OP_CODE = Operations.ID_LIST;
private static final String CLASS_NAME = "IdListData";
int mId;
int[] mIds;
- float[] mValues;
private static final int MAX_LIST = 2000;
public DataListIds(int id, int[] ids) {
mId = id;
mIds = ids;
- mValues = new float[ids.length];
}
@Override
- public void updateVariables(RemoteContext context) {
- for (int i = 0; i < mIds.length; i++) {
- int id = mIds[i];
- mValues[i] = context.getFloat(id);
- }
- }
+ public void updateVariables(RemoteContext context) {}
@Override
- public void registerListening(RemoteContext context) {
- for (int mId : mIds) {
- context.listensTo(mId, this);
- }
- }
+ public void registerListening(RemoteContext context) {}
@Override
public void write(WireBuffer buffer) {
@@ -65,7 +55,7 @@
@Override
public String toString() {
- return "map " + "\"" + Arrays.toString(mIds) + "\"";
+ return "map[" + Utils.idString(mId) + "] \"" + Arrays.toString(mIds) + "\"";
}
public static void apply(WireBuffer buffer, int id, int[] ids) {
@@ -92,15 +82,11 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Data Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("a list of id's")
- .field(INT, "id", "id the array")
+ .field(DocumentedOperation.INT, "id", "id the array")
.field(INT, "length", "number of ids")
- .field(INT_ARRAY, "ids[n]", "length",
- "ids of other variables");
-
+ .field(INT_ARRAY, "ids[n]", "length", "ids of other variables");
}
@Override
@@ -115,16 +101,26 @@
@Override
public float getFloatValue(int index) {
- return mValues[index];
+ return Float.NaN;
+ }
+
+ @Override
+ public int getId(int index) {
+ return mIds[index];
}
@Override
public float[] getFloats() {
- return mValues;
+ return null;
}
@Override
- public int getFloatsLength() {
- return mValues.length;
+ public int getLength() {
+ return mIds.length;
+ }
+
+ @Override
+ public int getIntValue(int index) {
+ return 0;
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java
index 53143dc..75db29d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java
@@ -15,78 +15,82 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.UTF8;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.UTF8;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
-import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
-import com.android.internal.widget.remotecompose.core.operations.utilities.ArrayAccess;
+import com.android.internal.widget.remotecompose.core.operations.utilities.DataMap;
import java.util.List;
-public class DataMapIds implements VariableSupport, ArrayAccess, Operation {
+/** This is a map of strings to type & Id */
+public class DataMapIds implements Operation {
private static final int OP_CODE = Operations.ID_MAP;
- private static final String CLASS_NAME = "IdMapData";
+ private static final String CLASS_NAME = "DataMapIds";
int mId;
- String[] mNames;
- int[] mIds;
- float[] mValues;
+ DataMap mDataMap;
+
private static final int MAX_MAP = 2000;
- public DataMapIds(int id, String[] names, int[] ids) {
+ public static final byte TYPE_STRING = 0;
+ public static final byte TYPE_INT = 1;
+ public static final byte TYPE_FLOAT = 2;
+ public static final byte TYPE_LONG = 3;
+ public static final byte TYPE_BOOLEAN = 4;
+
+ private String typeString(byte type) {
+ switch (type) {
+ case TYPE_STRING:
+ return "String";
+ case TYPE_INT:
+ return "Int";
+ case TYPE_FLOAT:
+ return "Float";
+ case TYPE_LONG:
+ return "Long";
+ case TYPE_BOOLEAN:
+ return "Boolean";
+ }
+ return "?";
+ }
+
+ public DataMapIds(int id, String[] names, byte[] types, int[] ids) {
mId = id;
- mNames = names;
- mIds = ids;
- mValues = new float[ids.length];
-
- }
-
- @Override
- public void updateVariables(RemoteContext context) {
- for (int i = 0; i < mIds.length; i++) {
- int id = mIds[i];
- mValues[i] = context.getFloat(id);
- }
- }
-
- @Override
- public void registerListening(RemoteContext context) {
- for (int mId : mIds) {
- context.listensTo(mId, this);
- }
+ mDataMap = new DataMap(names, types, ids);
}
@Override
public void write(WireBuffer buffer) {
- apply(buffer, mId, mNames, mIds);
+ apply(buffer, mId, mDataMap.mNames, mDataMap.mTypes, mDataMap.mIds);
}
@Override
public String toString() {
- StringBuilder builder = new StringBuilder("DataMapIds ");
- for (int i = 0; i < mNames.length; i++) {
+ StringBuilder builder = new StringBuilder("DataMapIds[" + Utils.idString(mId) + "] ");
+ for (int i = 0; i < mDataMap.mNames.length; i++) {
if (i != 0) {
builder.append(" ");
}
- builder.append(mNames[i]);
+ builder.append(typeString(mDataMap.mTypes[i]));
builder.append("[");
- builder.append(mIds[i]);
- builder.append("]");
-
+ builder.append(mDataMap.mNames[i]);
+ builder.append("]=");
+ builder.append(mDataMap.mIds[i]);
}
return builder.toString();
}
- public static void apply(WireBuffer buffer, int id, String[] names, int[] ids) {
+ public static void apply(WireBuffer buffer, int id, String[] names, byte[] type, int[] ids) {
buffer.start(OP_CODE);
buffer.writeInt(id);
buffer.writeInt(names.length);
for (int i = 0; i < names.length; i++) {
buffer.writeUTF8(names[i]);
+ buffer.writeByte(type == null ? 2 : type[i]);
buffer.writeInt(ids[i]);
}
}
@@ -99,25 +103,23 @@
}
String[] names = new String[len];
int[] ids = new int[len];
+ byte[] types = new byte[len];
for (int i = 0; i < names.length; i++) {
names[i] = buffer.readUTF8();
+ types[i] = (byte) buffer.readByte();
ids[i] = buffer.readInt();
}
- DataMapIds data = new DataMapIds(id, names, ids);
+ DataMapIds data = new DataMapIds(id, names, types, ids);
operations.add(data);
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Data Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Encode a collection of name id pairs")
.field(INT, "id", "id the array")
.field(INT, "length", "number of entries")
- .field(INT, "names[0]", "length",
- "path encoded as floats")
- .field(UTF8, "id[0]", "length",
- "path encoded as floats");
+ .field(INT, "names[0]", "length", "path encoded as floats")
+ .field(UTF8, "id[0]", "length", "path encoded as floats");
}
@Override
@@ -127,21 +129,6 @@
@Override
public void apply(RemoteContext context) {
- context.addCollection(mId, this);
- }
-
- @Override
- public float getFloatValue(int index) {
- return mValues[index];
- }
-
- @Override
- public float[] getFloats() {
- return mValues;
- }
-
- @Override
- public int getFloatsLength() {
- return mValues.length;
+ context.putDataMap(mId, mDataMap);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapLookup.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapLookup.java
new file mode 100644
index 0000000..fb5e5d1
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapLookup.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.operations.utilities.DataMap;
+import com.android.internal.widget.remotecompose.core.types.BooleanConstant;
+import com.android.internal.widget.remotecompose.core.types.LongConstant;
+
+import java.util.List;
+
+/** This can lookup in a map given a string writing the results to an id. */
+public class DataMapLookup implements Operation {
+ private static final int OP_CODE = Operations.DATA_MAP_LOOKUP;
+ private static final String CLASS_NAME = "DataMapLookup";
+ public int mId;
+ public int mDataMapId;
+ public int mStringId;
+
+ /**
+ * Create an access to a data map
+ *
+ * @param id of the output value
+ * @param dataMapId the id of the data map
+ * @param keyStringId the string to be looked up
+ */
+ public DataMapLookup(int id, int dataMapId, int keyStringId) {
+ this.mId = id;
+ this.mDataMapId = dataMapId;
+ this.mStringId = keyStringId;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ apply(buffer, mId, mDataMapId, mStringId);
+ }
+
+ @Override
+ public String toString() {
+ return "DataMapLookup[" + mId + "] = " + Utils.idString(mDataMapId) + " " + mStringId;
+ }
+
+ /**
+ * The class name
+ *
+ * @return the name of the class
+ */
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ /**
+ * The opcode
+ *
+ * @return the opcode
+ */
+ public static int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ *
+ * @param buffer write command to this buffer
+ * @param id the id
+ * @param dataMapId the map to extract from
+ * @param keyStringId the map to extract from
+ */
+ public static void apply(WireBuffer buffer, int id, int dataMapId, int keyStringId) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(id);
+ buffer.writeInt(dataMapId);
+ buffer.writeInt(keyStringId);
+ }
+
+ /**
+ * The read the buffer and create the command
+ *
+ * @param buffer buffer
+ * @param operations the created command is added to the list
+ */
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ int id = buffer.readInt();
+ int mapId = buffer.readInt();
+ int stringId = buffer.readInt();
+ operations.add(new DataMapLookup(id, mapId, stringId));
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
+ .description("A float and its associated id")
+ .field(INT, "id", "id of float")
+ .field(INT, "dataMapId", "32-bit float value")
+ .field(INT, "value", "32-bit float value");
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ String str = context.getText(mStringId);
+ DataMap data = context.getDataMap(mDataMapId);
+ int pos = data.getPos(str);
+ byte type = data.getType(pos);
+ int dataId = data.getId(pos);
+ switch (type) {
+ case DataMapIds.TYPE_STRING:
+ context.loadText(mId, context.getText(dataId));
+ break;
+ case DataMapIds.TYPE_INT:
+ context.loadInteger(mId, context.getInteger(dataId));
+ break;
+ case DataMapIds.TYPE_FLOAT:
+ context.loadFloat(mId, context.getFloat(dataId));
+ break;
+ case DataMapIds.TYPE_LONG:
+ LongConstant lc = (LongConstant) context.getObject(dataId);
+ context.loadInteger(mId, (int) lc.getValue());
+ break;
+ case DataMapIds.TYPE_BOOLEAN:
+ BooleanConstant bc = (BooleanConstant) context.getObject(dataId);
+ context.loadInteger(mId, bc.getValue() ? 1 : 0);
+ break;
+ }
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java
index d377229..e078307 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java
@@ -15,13 +15,12 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
@@ -40,6 +39,7 @@
/**
* Writes out the operation to the buffer
+ *
* @param buffer the buffer to write to
* @param v1 The left side of the Oval
* @param v2 The top of the Oval
@@ -48,13 +48,8 @@
* @param v5 Starting angle (in degrees) where the arc begins
* @param v6 Sweep angle (in degrees) measured clockwise
*/
- public static void apply(WireBuffer buffer,
- float v1,
- float v2,
- float v3,
- float v4,
- float v5,
- float v6) {
+ public static void apply(
+ WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6) {
buffer.start(OP_CODE);
buffer.writeFloat(v1);
buffer.writeFloat(v2);
@@ -64,47 +59,37 @@
buffer.writeFloat(v6);
}
- protected void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3,
- float v4,
- float v5,
- float v6) {
+ @Override
+ protected void write(
+ WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6) {
apply(buffer, v1, v2, v3, v4, v5, v6);
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
- .description("Draw the specified arc"
- + "which will be scaled to fit inside the specified oval")
- .field(FLOAT, "left",
- "The left side of the Oval")
- .field(FLOAT, "top",
- "The top of the Oval")
- .field(FLOAT, "right",
- "The right side of the Oval")
- .field(FLOAT, "bottom",
- "The bottom of the Oval")
- .field(FLOAT, "startAngle",
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
+ .description(
+ "Draw the specified arc"
+ + "which will be scaled to fit inside the specified oval")
+ .field(DocumentedOperation.FLOAT, "left", "The left side of the Oval")
+ .field(DocumentedOperation.FLOAT, "top", "The top of the Oval")
+ .field(DocumentedOperation.FLOAT, "right", "The right side of the Oval")
+ .field(DocumentedOperation.FLOAT, "bottom", "The bottom of the Oval")
+ .field(
+ DocumentedOperation.FLOAT,
+ "startAngle",
"Starting angle (in degrees) where the arc begins")
- .field(FLOAT, "sweepAngle",
+ .field(
+ DocumentedOperation.FLOAT,
+ "sweepAngle",
"Sweep angle (in degrees) measured clockwise");
}
-
- public DrawArc(float v1,
- float v2,
- float v3,
- float v4,
- float v5,
- float v6) {
+ public DrawArc(float v1, float v2, float v3, float v4, float v5, float v6) {
super(v1, v2, v3, v4, v5, v6);
mName = "DrawArc";
}
+ @Override
public void paint(PaintContext context) {
context.drawArc(mV1, mV2, mV3, mV4, mV5, mV6);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
index 97eb76b..c678cc4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
@@ -25,11 +25,8 @@
import java.util.List;
-/**
- * Base class for commands that take 3 float
- */
-public abstract class DrawBase2 extends PaintOperation
- implements VariableSupport {
+/** Base class for commands that take 3 float */
+public abstract class DrawBase2 extends PaintOperation implements VariableSupport {
protected String mName = "DrawRectBase";
float mV1;
float mV2;
@@ -45,10 +42,8 @@
@Override
public void updateVariables(RemoteContext context) {
- mV1 = (Float.isNaN(mValue1))
- ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
- mV2 = (Float.isNaN(mValue2))
- ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
+ mV1 = Float.isNaN(mValue1) ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
+ mV2 = Float.isNaN(mValue2) ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
}
@Override
@@ -61,7 +56,6 @@
}
}
-
@Override
public void write(WireBuffer buffer) {
write(buffer, mV1, mV2);
@@ -70,8 +64,7 @@
protected abstract void write(WireBuffer buffer, float v1, float v2);
protected interface Maker {
- DrawBase2 create(float v1,
- float v2);
+ DrawBase2 create(float v1, float v2);
}
@Override
@@ -79,7 +72,6 @@
return mName + " " + floatToString(mV1) + " " + floatToString(mV2);
}
-
public static void read(Maker maker, WireBuffer buffer, List<Operation> operations) {
float v1 = buffer.readFloat();
float v2 = buffer.readFloat();
@@ -99,7 +91,6 @@
return null;
}
-
/**
* Writes out the operation to the buffer
*
@@ -108,13 +99,9 @@
* @param x1
* @param y1
*/
- protected static void write(WireBuffer buffer,
- int opCode,
- float x1,
- float y1) {
+ protected static void write(WireBuffer buffer, int opCode, float x1, float y1) {
buffer.start(opCode);
buffer.writeFloat(x1);
buffer.writeFloat(y1);
-
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
index 2d1d3eb..e1108e9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
@@ -25,11 +25,8 @@
import java.util.List;
-/**
- * Base class for commands that take 3 float
- */
-public abstract class DrawBase3 extends PaintOperation
- implements VariableSupport {
+/** Base class for commands that take 3 float */
+public abstract class DrawBase3 extends PaintOperation implements VariableSupport {
protected String mName = "DrawRectBase";
float mV1;
@@ -39,10 +36,7 @@
float mValue2;
float mValue3;
- public DrawBase3(
- float v1,
- float v2,
- float v3) {
+ public DrawBase3(float v1, float v2, float v3) {
mValue1 = v1;
mValue2 = v2;
mValue3 = v3;
@@ -54,12 +48,9 @@
@Override
public void updateVariables(RemoteContext context) {
- mV1 = (Utils.isVariable(mValue1))
- ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
- mV2 = (Utils.isVariable(mValue2))
- ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
- mV3 = (Utils.isVariable(mValue3))
- ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3;
+ mV1 = Utils.isVariable(mValue1) ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
+ mV2 = Utils.isVariable(mValue2) ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
+ mV3 = Utils.isVariable(mValue3) ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3;
}
@Override
@@ -80,21 +71,21 @@
write(buffer, mV1, mV2, mV3);
}
- protected abstract void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3);
+ protected abstract void write(WireBuffer buffer, float v1, float v2, float v3);
interface Maker {
- DrawBase3 create(float v1,
- float v2,
- float v3);
+ DrawBase3 create(float v1, float v2, float v3);
}
@Override
public String toString() {
- return mName + " " + floatToString(mV1) + " " + floatToString(mV2)
- + " " + floatToString(mV3);
+ return mName
+ + " "
+ + floatToString(mV1)
+ + " "
+ + floatToString(mV2)
+ + " "
+ + floatToString(mV3);
}
public static void read(Maker maker, WireBuffer buffer, List<Operation> operations) {
@@ -106,17 +97,14 @@
}
/**
- * Construct and Operation from the 3 variables.
- * This must be overridden by subclasses
+ * Construct and Operation from the 3 variables. This must be overridden by subclasses
*
* @param x1
* @param y1
* @param x2
* @return
*/
- public Operation construct(float x1,
- float y1,
- float x2) {
+ public Operation construct(float x1, float y1, float x2) {
return null;
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java
index 943c5a4..09f0df9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java
@@ -25,11 +25,8 @@
import java.util.List;
-/**
- * Base class for draw commands that take 4 floats
- */
-public abstract class DrawBase4 extends PaintOperation
- implements VariableSupport {
+/** Base class for draw commands that take 4 floats */
+public abstract class DrawBase4 extends PaintOperation implements VariableSupport {
protected String mName = "DrawRectBase";
protected float mX1;
protected float mY1;
@@ -40,11 +37,7 @@
float mX2Value;
float mY2Value;
- public DrawBase4(
- float x1,
- float y1,
- float x2,
- float y2) {
+ public DrawBase4(float x1, float y1, float x2, float y2) {
mX1Value = x1;
mY1Value = y1;
mX2Value = x2;
@@ -58,14 +51,10 @@
@Override
public void updateVariables(RemoteContext context) {
- mX1 = (Float.isNaN(mX1Value))
- ? context.getFloat(Utils.idFromNan(mX1Value)) : mX1Value;
- mY1 = (Float.isNaN(mY1Value))
- ? context.getFloat(Utils.idFromNan(mY1Value)) : mY1Value;
- mX2 = (Float.isNaN(mX2Value))
- ? context.getFloat(Utils.idFromNan(mX2Value)) : mX2Value;
- mY2 = (Float.isNaN(mY2Value))
- ? context.getFloat(Utils.idFromNan(mY2Value)) : mY2Value;
+ mX1 = Float.isNaN(mX1Value) ? context.getFloat(Utils.idFromNan(mX1Value)) : mX1Value;
+ mY1 = Float.isNaN(mY1Value) ? context.getFloat(Utils.idFromNan(mY1Value)) : mY1Value;
+ mX2 = Float.isNaN(mX2Value) ? context.getFloat(Utils.idFromNan(mX2Value)) : mX2Value;
+ mY2 = Float.isNaN(mY2Value) ? context.getFloat(Utils.idFromNan(mY2Value)) : mY2Value;
}
@Override
@@ -89,23 +78,23 @@
write(buffer, mX1, mY1, mX2, mY2);
}
- protected abstract void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3,
- float v4);
+ protected abstract void write(WireBuffer buffer, float v1, float v2, float v3, float v4);
protected interface Maker {
- DrawBase4 create(float v1,
- float v2,
- float v3,
- float v4);
+ DrawBase4 create(float v1, float v2, float v3, float v4);
}
@Override
public String toString() {
- return mName + " " + floatToString(mX1Value, mX1) + " " + floatToString(mY1Value, mY1)
- + " " + floatToString(mX2Value, mX2) + " " + floatToString(mY2Value, mY2);
+ return mName
+ + " "
+ + floatToString(mX1Value, mX1)
+ + " "
+ + floatToString(mY1Value, mY1)
+ + " "
+ + floatToString(mX2Value, mX2)
+ + " "
+ + floatToString(mY2Value, mY2);
}
public static void read(Maker maker, WireBuffer buffer, List<Operation> operations) {
@@ -127,14 +116,10 @@
* @param y2
* @return
*/
- public Operation construct(float x1,
- float y1,
- float x2,
- float y2) {
+ public Operation construct(float x1, float y1, float x2, float y2) {
return null;
}
-
/**
* Writes out the operation to the buffer
*
@@ -145,12 +130,8 @@
* @param x2
* @param y2
*/
- protected static void write(WireBuffer buffer,
- int opCode,
- float x1,
- float y1,
- float x2,
- float y2) {
+ protected static void write(
+ WireBuffer buffer, int opCode, float x1, float y1, float x2, float y2) {
buffer.start(opCode);
buffer.writeFloat(x1);
buffer.writeFloat(y1);
@@ -158,4 +139,3 @@
buffer.writeFloat(y2);
}
}
-
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java
index 767a36d..e071d5f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java
@@ -15,8 +15,6 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.PaintOperation;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -25,11 +23,8 @@
import java.util.List;
-/**
- * Base class for draw commands the take 6 floats
- */
-public abstract class DrawBase6 extends PaintOperation
- implements VariableSupport {
+/** Base class for draw commands the take 6 floats */
+public abstract class DrawBase6 extends PaintOperation implements VariableSupport {
protected String mName = "DrawRectBase";
float mV1;
float mV2;
@@ -44,13 +39,7 @@
float mValue5;
float mValue6;
- public DrawBase6(
- float v1,
- float v2,
- float v3,
- float v4,
- float v5,
- float v6) {
+ public DrawBase6(float v1, float v2, float v3, float v4, float v5, float v6) {
mValue1 = v1;
mValue2 = v2;
mValue3 = v3;
@@ -68,18 +57,12 @@
@Override
public void updateVariables(RemoteContext context) {
- mV1 = (Float.isNaN(mValue1))
- ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
- mV2 = (Float.isNaN(mValue2))
- ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
- mV3 = (Float.isNaN(mValue3))
- ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3;
- mV4 = (Float.isNaN(mValue4))
- ? context.getFloat(Utils.idFromNan(mValue4)) : mValue4;
- mV5 = (Float.isNaN(mValue5))
- ? context.getFloat(Utils.idFromNan(mValue5)) : mValue5;
- mV6 = (Float.isNaN(mValue6))
- ? context.getFloat(Utils.idFromNan(mValue6)) : mValue6;
+ mV1 = Float.isNaN(mValue1) ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
+ mV2 = Float.isNaN(mValue2) ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
+ mV3 = Float.isNaN(mValue3) ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3;
+ mV4 = Float.isNaN(mValue4) ? context.getFloat(Utils.idFromNan(mValue4)) : mValue4;
+ mV5 = Float.isNaN(mValue5) ? context.getFloat(Utils.idFromNan(mValue5)) : mValue5;
+ mV6 = Float.isNaN(mValue6) ? context.getFloat(Utils.idFromNan(mValue6)) : mValue6;
}
@Override
@@ -109,27 +92,24 @@
write(buffer, mV1, mV2, mV3, mV4, mV5, mV6);
}
- protected abstract void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3,
- float v4,
- float v5,
- float v6);
+ protected abstract void write(
+ WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6);
@Override
public String toString() {
- return mName + " " + floatToString(mV1) + " " + floatToString(mV2)
- + " " + floatToString(mV3) + " " + floatToString(mV4);
+ return mName
+ + " "
+ + Utils.floatToString(mV1)
+ + " "
+ + Utils.floatToString(mV2)
+ + " "
+ + Utils.floatToString(mV3)
+ + " "
+ + Utils.floatToString(mV4);
}
interface Maker {
- DrawBase6 create(float v1,
- float v2,
- float v3,
- float v4,
- float v5,
- float v6);
+ DrawBase6 create(float v1, float v2, float v3, float v4, float v5, float v6);
}
public static void read(Maker build, WireBuffer buffer, List<Operation> operations) {
@@ -155,18 +135,11 @@
* @param v6
* @return
*/
- public Operation construct(float v1,
- float v2,
- float v3,
- float v4,
- float v5,
- float v6) {
+ public Operation construct(float v1, float v2, float v3, float v4, float v5, float v6) {
return null;
}
-
public static String name() {
return "DrawBase6";
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
index 2748f4c..0b43fd2 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -44,12 +44,7 @@
int mDescriptionId = 0;
public DrawBitmap(
- int imageId,
- float left,
- float top,
- float right,
- float bottom,
- int descriptionId) {
+ int imageId, float left, float top, float right, float bottom, int descriptionId) {
mLeft = left;
mTop = top;
mRight = right;
@@ -60,14 +55,10 @@
@Override
public void updateVariables(RemoteContext context) {
- mOutputLeft = (Float.isNaN(mLeft))
- ? context.getFloat(Utils.idFromNan(mLeft)) : mLeft;
- mOutputTop = (Float.isNaN(mTop))
- ? context.getFloat(Utils.idFromNan(mTop)) : mTop;
- mOutputRight = (Float.isNaN(mRight))
- ? context.getFloat(Utils.idFromNan(mRight)) : mRight;
- mOutputBottom = (Float.isNaN(mBottom))
- ? context.getFloat(Utils.idFromNan(mBottom)) : mBottom;
+ mOutputLeft = Float.isNaN(mLeft) ? context.getFloat(Utils.idFromNan(mLeft)) : mLeft;
+ mOutputTop = Float.isNaN(mTop) ? context.getFloat(Utils.idFromNan(mTop)) : mTop;
+ mOutputRight = Float.isNaN(mRight) ? context.getFloat(Utils.idFromNan(mRight)) : mRight;
+ mOutputBottom = Float.isNaN(mBottom) ? context.getFloat(Utils.idFromNan(mBottom)) : mBottom;
}
@Override
@@ -93,8 +84,17 @@
@Override
public String toString() {
- return "DrawBitmap (desc=" + mDescriptionId + ")" + mLeft + " " + mTop
- + " " + mRight + " " + mBottom + ";";
+ return "DrawBitmap (desc="
+ + mDescriptionId
+ + ")"
+ + mLeft
+ + " "
+ + mTop
+ + " "
+ + mRight
+ + " "
+ + mBottom
+ + ";";
}
public static void read(WireBuffer buffer, List<Operation> operations) {
@@ -117,13 +117,14 @@
return OP_CODE;
}
- public static void apply(WireBuffer buffer,
- int id,
- float left,
- float top,
- float right,
- float bottom,
- int descriptionId) {
+ public static void apply(
+ WireBuffer buffer,
+ int id,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ int descriptionId) {
buffer.start(Operations.DRAW_BITMAP);
buffer.writeInt(id);
buffer.writeFloat(left);
@@ -134,26 +135,18 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Draw Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw a bitmap")
.field(INT, "id", "id of float")
- .field(FLOAT, "left",
- "The left side of the image")
- .field(FLOAT, "top",
- "The top of the image")
- .field(FLOAT, "right",
- "The right side of the image")
- .field(FLOAT, "bottom",
- "The bottom of the image")
+ .field(FLOAT, "left", "The left side of the image")
+ .field(FLOAT, "top", "The top of the image")
+ .field(FLOAT, "right", "The right side of the image")
+ .field(FLOAT, "bottom", "The bottom of the image")
.field(INT, "descriptionId", "id of string");
}
+ @Override
public void paint(PaintContext context) {
- context.drawBitmap(mId, mOutputLeft,
- mOutputTop,
- mOutputRight,
- mOutputBottom);
+ context.drawBitmap(mId, mOutputLeft, mOutputTop, mOutputRight, mOutputBottom);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java
index 561d527..fc74827 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java
@@ -15,20 +15,17 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.PaintOperation;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Operation to draw a given cached bitmap
- */
+/** Operation to draw a given cached bitmap */
public class DrawBitmapInt extends PaintOperation {
private static final int OP_CODE = Operations.DRAW_BITMAP_INT;
private static final String CLASS_NAME = "DrawBitmapInt";
@@ -43,16 +40,17 @@
int mDstBottom;
int mContentDescId = 0;
- public DrawBitmapInt(int imageId,
- int srcLeft,
- int srcTop,
- int srcRight,
- int srcBottom,
- int dstLeft,
- int dstTop,
- int dstRight,
- int dstBottom,
- int cdId) {
+ public DrawBitmapInt(
+ int imageId,
+ int srcLeft,
+ int srcTop,
+ int srcRight,
+ int srcBottom,
+ int dstLeft,
+ int dstTop,
+ int dstRight,
+ int dstBottom,
+ int cdId) {
this.mImageId = imageId;
this.mSrcLeft = srcLeft;
this.mSrcTop = srcTop;
@@ -67,18 +65,44 @@
@Override
public void write(WireBuffer buffer) {
- apply(buffer, mImageId, mSrcLeft, mSrcTop, mSrcRight, mSrcBottom,
- mDstLeft, mDstTop, mDstRight, mDstBottom, mContentDescId);
+ apply(
+ buffer,
+ mImageId,
+ mSrcLeft,
+ mSrcTop,
+ mSrcRight,
+ mSrcBottom,
+ mDstLeft,
+ mDstTop,
+ mDstRight,
+ mDstBottom,
+ mContentDescId);
}
@Override
public String toString() {
- return "DRAW_BITMAP_INT " + mImageId + " on " + mSrcLeft + " " + mSrcTop
- + " " + mSrcRight + " " + mSrcBottom + " "
- + "- " + mDstLeft + " " + mDstTop + " " + mDstRight + " " + mDstBottom + ";";
+ return "DRAW_BITMAP_INT "
+ + mImageId
+ + " on "
+ + mSrcLeft
+ + " "
+ + mSrcTop
+ + " "
+ + mSrcRight
+ + " "
+ + mSrcBottom
+ + " "
+ + "- "
+ + mDstLeft
+ + " "
+ + mDstTop
+ + " "
+ + mDstRight
+ + " "
+ + mDstBottom
+ + ";";
}
-
public static String name() {
return CLASS_NAME;
}
@@ -87,10 +111,18 @@
return OP_CODE;
}
- public static void apply(WireBuffer buffer, int imageId,
- int srcLeft, int srcTop, int srcRight, int srcBottom,
- int dstLeft, int dstTop, int dstRight, int dstBottom,
- int cdId) {
+ public static void apply(
+ WireBuffer buffer,
+ int imageId,
+ int srcLeft,
+ int srcTop,
+ int srcRight,
+ int srcBottom,
+ int dstLeft,
+ int dstTop,
+ int dstRight,
+ int dstBottom,
+ int cdId) {
buffer.start(Operations.DRAW_BITMAP_INT);
buffer.writeInt(imageId);
buffer.writeInt(srcLeft);
@@ -115,41 +147,41 @@
int dstRight = buffer.readInt();
int dstBottom = buffer.readInt();
int cdId = buffer.readInt();
- DrawBitmapInt op = new DrawBitmapInt(imageId, sLeft, srcTop, srcRight, srcBottom,
- dstLeft, dstTop, dstRight, dstBottom, cdId);
+ DrawBitmapInt op =
+ new DrawBitmapInt(
+ imageId, sLeft, srcTop, srcRight, srcBottom, dstLeft, dstTop, dstRight,
+ dstBottom, cdId);
operations.add(op);
}
-
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Draw Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw a bitmap using integer coordinates")
- .field(INT, "id", "id of bitmap")
- .field(INT, "srcLeft",
- "The left side of the image")
- .field(INT, "srcTop",
- "The top of the image")
- .field(INT, "srcRight",
- "The right side of the image")
- .field(INT, "srcBottom",
- "The bottom of the image")
- .field(INT, "dstLeft",
- "The left side of the image")
- .field(INT, "dstTop",
- "The top of the image")
- .field(INT, "dstRight",
- "The right side of the image")
- .field(INT, "dstBottom",
- "The bottom of the image")
- .field(INT, "cdId", "id of string");
+ .field(DocumentedOperation.INT, "id", "id of bitmap")
+ .field(DocumentedOperation.INT, "srcLeft", "The left side of the image")
+ .field(DocumentedOperation.INT, "srcTop", "The top of the image")
+ .field(DocumentedOperation.INT, "srcRight", "The right side of the image")
+ .field(DocumentedOperation.INT, "srcBottom", "The bottom of the image")
+ .field(DocumentedOperation.INT, "dstLeft", "The left side of the image")
+ .field(DocumentedOperation.INT, "dstTop", "The top of the image")
+ .field(DocumentedOperation.INT, "dstRight", "The right side of the image")
+ .field(DocumentedOperation.INT, "dstBottom", "The bottom of the image")
+ .field(DocumentedOperation.INT, "cdId", "id of string");
}
@Override
public void paint(PaintContext context) {
- context.drawBitmap(mImageId, mSrcLeft, mSrcTop, mSrcRight, mSrcBottom,
- mDstLeft, mDstTop, mDstRight, mDstBottom, mContentDescId);
+ context.drawBitmap(
+ mImageId,
+ mSrcLeft,
+ mSrcTop,
+ mSrcRight,
+ mSrcBottom,
+ mDstLeft,
+ mDstTop,
+ mDstRight,
+ mDstBottom,
+ mContentDescId);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java
new file mode 100644
index 0000000..22742c6
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+import com.android.internal.widget.remotecompose.core.operations.utilities.ImageScaling;
+
+import java.util.List;
+
+/** Operation to draw a given cached bitmap */
+public class DrawBitmapScaled extends PaintOperation implements VariableSupport {
+ private static final int OP_CODE = Operations.DRAW_BITMAP_SCALED;
+ private static final String CLASS_NAME = "DrawBitmapScaled";
+ int mImageId;
+ float mSrcLeft, mOutSrcLeft;
+ float mSrcTop, mOutSrcTop;
+ float mSrcRight, mOutSrcRight;
+ float mSrcBottom, mOutSrcBottom;
+ float mDstLeft, mOutDstLeft;
+ float mDstTop, mOutDstTop;
+ float mDstRight, mOutDstRight;
+ float mDstBottom, mOutDstBottom;
+ int mContentDescId;
+ float mScaleFactor, mOutScaleFactor;
+ int mScaleType;
+
+ ImageScaling mScaling = new ImageScaling();
+ public static final int SCALE_NONE = ImageScaling.SCALE_NONE;
+ public static final int SCALE_INSIDE = ImageScaling.SCALE_INSIDE;
+ public static final int SCALE_FILL_WIDTH = ImageScaling.SCALE_FILL_WIDTH;
+ public static final int SCALE_FILL_HEIGHT = ImageScaling.SCALE_FILL_HEIGHT;
+ public static final int SCALE_FIT = ImageScaling.SCALE_FIT;
+ public static final int SCALE_CROP = ImageScaling.SCALE_CROP;
+ public static final int SCALE_FILL_BOUNDS = ImageScaling.SCALE_FILL_BOUNDS;
+ public static final int SCALE_FIXED_SCALE = ImageScaling.SCALE_FIXED_SCALE;
+
+ public DrawBitmapScaled(
+ int imageId,
+ float srcLeft,
+ float srcTop,
+ float srcRight,
+ float srcBottom,
+ float dstLeft,
+ float dstTop,
+ float dstRight,
+ float dstBottom,
+ int type,
+ float scale,
+ int cdId) {
+ this.mImageId = imageId;
+ mOutSrcLeft = mSrcLeft = srcLeft;
+ mOutSrcTop = mSrcTop = srcTop;
+ mOutSrcRight = mSrcRight = srcRight;
+ mOutSrcBottom = mSrcBottom = srcBottom;
+ mOutDstLeft = mDstLeft = dstLeft;
+ mOutDstTop = mDstTop = dstTop;
+ mOutDstRight = mDstRight = dstRight;
+ mOutDstBottom = mDstBottom = dstBottom;
+ mScaleType = type;
+ mOutScaleFactor = mScaleFactor = scale;
+ this.mContentDescId = cdId;
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ mOutSrcLeft =
+ Float.isNaN(mSrcLeft) ? context.getFloat(Utils.idFromNan(mSrcLeft)) : mSrcLeft;
+ mOutSrcTop = Float.isNaN(mSrcTop) ? context.getFloat(Utils.idFromNan(mSrcTop)) : mSrcTop;
+ mOutSrcRight =
+ Float.isNaN(mSrcRight) ? context.getFloat(Utils.idFromNan(mSrcRight)) : mSrcRight;
+ mOutSrcBottom =
+ Float.isNaN(mSrcBottom)
+ ? context.getFloat(Utils.idFromNan(mSrcBottom))
+ : mSrcBottom;
+ mOutDstLeft =
+ Float.isNaN(mDstLeft) ? context.getFloat(Utils.idFromNan(mDstLeft)) : mDstLeft;
+ mOutDstTop = Float.isNaN(mDstTop) ? context.getFloat(Utils.idFromNan(mDstTop)) : mDstTop;
+ mOutDstRight =
+ Float.isNaN(mDstRight) ? context.getFloat(Utils.idFromNan(mDstRight)) : mDstRight;
+ mOutDstBottom =
+ Float.isNaN(mDstBottom)
+ ? context.getFloat(Utils.idFromNan(mDstBottom))
+ : mDstBottom;
+ mOutScaleFactor =
+ Float.isNaN(mScaleFactor)
+ ? context.getFloat(Utils.idFromNan(mScaleFactor))
+ : mScaleFactor;
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ register(context, mSrcLeft);
+ register(context, mSrcTop);
+ register(context, mSrcRight);
+ register(context, mSrcBottom);
+ register(context, mDstLeft);
+ register(context, mDstTop);
+ register(context, mDstRight);
+ register(context, mDstBottom);
+ register(context, mScaleFactor);
+ }
+
+ private void register(RemoteContext context, float value) {
+ if (Float.isNaN(value)) {
+ context.listensTo(Utils.idFromNan(value), this);
+ }
+ }
+
+ static String str(float v) {
+ String s = " " + (int) v;
+ return s.substring(s.length() - 3);
+ }
+
+ void print(String str, float left, float top, float right, float bottom) {
+ String s = str;
+ s += str(left) + ", " + str(top) + ", " + str(right) + ", " + str(bottom) + ", ";
+ s += " [" + str(right - left) + " x " + str(bottom - top) + "]";
+ System.out.println(s);
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ apply(
+ buffer,
+ mImageId,
+ mSrcLeft,
+ mSrcTop,
+ mSrcRight,
+ mSrcBottom,
+ mDstLeft,
+ mDstTop,
+ mDstRight,
+ mDstBottom,
+ mScaleType,
+ mScaleFactor,
+ mContentDescId);
+ }
+
+ @Override
+ public String toString() {
+ return "DrawBitmapScaled "
+ + mImageId
+ + " ["
+ + Utils.floatToString(mSrcLeft, mOutSrcLeft)
+ + " "
+ + Utils.floatToString(mSrcTop, mOutSrcTop)
+ + " "
+ + Utils.floatToString(mSrcRight, mOutSrcRight)
+ + " "
+ + Utils.floatToString(mSrcBottom, mOutSrcBottom)
+ + "] "
+ + "- ["
+ + Utils.floatToString(mDstLeft, mOutDstLeft)
+ + " "
+ + Utils.floatToString(mDstTop, mOutDstTop)
+ + " "
+ + Utils.floatToString(mDstRight, mOutDstRight)
+ + " "
+ + Utils.floatToString(mDstBottom, mOutDstBottom)
+ + "] "
+ + " "
+ + mScaleType
+ + " "
+ + Utils.floatToString(mScaleFactor, mOutScaleFactor);
+ }
+
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ public static int id() {
+ return OP_CODE;
+ }
+
+ public static void apply(
+ WireBuffer buffer,
+ int imageId,
+ float srcLeft,
+ float srcTop,
+ float srcRight,
+ float srcBottom,
+ float dstLeft,
+ float dstTop,
+ float dstRight,
+ float dstBottom,
+ int scaleType,
+ float scaleFactor,
+ int cdId) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(imageId);
+
+ buffer.writeFloat(srcLeft);
+ buffer.writeFloat(srcTop);
+ buffer.writeFloat(srcRight);
+ buffer.writeFloat(srcBottom);
+
+ buffer.writeFloat(dstLeft);
+ buffer.writeFloat(dstTop);
+ buffer.writeFloat(dstRight);
+ buffer.writeFloat(dstBottom);
+
+ buffer.writeInt(scaleType);
+ buffer.writeFloat(scaleFactor);
+ buffer.writeInt(cdId);
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ int imageId = buffer.readInt();
+
+ float sLeft = buffer.readFloat();
+ float srcTop = buffer.readFloat();
+ float srcRight = buffer.readFloat();
+ float srcBottom = buffer.readFloat();
+
+ float dstLeft = buffer.readFloat();
+ float dstTop = buffer.readFloat();
+ float dstRight = buffer.readFloat();
+ float dstBottom = buffer.readFloat();
+ int scaleType = buffer.readInt();
+ float scaleFactor = buffer.readFloat();
+ int cdId = buffer.readInt();
+ DrawBitmapScaled op =
+ new DrawBitmapScaled(
+ imageId,
+ sLeft,
+ srcTop,
+ srcRight,
+ srcBottom,
+ dstLeft,
+ dstTop,
+ dstRight,
+ dstBottom,
+ scaleType,
+ scaleFactor,
+ cdId);
+
+ operations.add(op);
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
+ .description("Draw a bitmap using integer coordinates")
+ .field(DocumentedOperation.INT, "id", "id of bitmap")
+ .field(DocumentedOperation.FLOAT, "srcLeft", "The left side of the image")
+ .field(DocumentedOperation.FLOAT, "srcTop", "The top of the image")
+ .field(DocumentedOperation.FLOAT, "srcRight", "The right side of the image")
+ .field(DocumentedOperation.FLOAT, "srcBottom", "The bottom of the output")
+ .field(DocumentedOperation.FLOAT, "dstLeft", "The left side of the output")
+ .field(DocumentedOperation.FLOAT, "dstTop", "The top of the output")
+ .field(DocumentedOperation.FLOAT, "dstRight", "The right side of the output")
+ .field(DocumentedOperation.INT, "type", "type of auto scaling")
+ .field(DocumentedOperation.INT, "scaleFactor", "for allowed")
+ .field(DocumentedOperation.INT, "cdId", "id of string");
+ }
+
+ // private String typeToString(int type) {
+ // String[] typeString = {
+ // "none",
+ // "inside",
+ // "fill_width",
+ // "fill_height",
+ // "fit",
+ // "crop",
+ // "fill_bounds",
+ // "fixed_scale"
+ // };
+ // return typeString[type];
+ // }
+
+ @Override
+ public void paint(PaintContext context) {
+ mScaling.setup(
+ mOutSrcLeft,
+ mOutSrcTop,
+ mOutSrcRight,
+ mOutSrcBottom,
+ mOutDstLeft,
+ mOutDstTop,
+ mOutDstRight,
+ mOutDstBottom,
+ mScaleType,
+ mOutScaleFactor);
+ context.save();
+ context.clipRect(mOutDstLeft, mOutDstTop, mOutDstRight, mOutDstBottom);
+ context.drawBitmap(
+ mImageId,
+ (int) mOutSrcLeft,
+ (int) mOutSrcTop,
+ (int) mOutSrcRight,
+ (int) mOutSrcBottom,
+ (int) mScaling.mFinalDstLeft,
+ (int) mScaling.mFinalDstTop,
+ (int) mScaling.mFinalDstRight,
+ (int) mScaling.mFinalDstBottom,
+ mContentDescId);
+ context.restore();
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java
index e39a191..04f095a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java
@@ -1,12 +1,26 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
@@ -28,29 +42,25 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw a Circle")
- .field(FLOAT, "centerX",
+ .field(
+ DocumentedOperation.FLOAT,
+ "centerX",
"The x-coordinate of the center of the circle to be drawn")
- .field(FLOAT, "centerY",
+ .field(
+ DocumentedOperation.FLOAT,
+ "centerY",
"The y-coordinate of the center of the circle to be drawn")
- .field(FLOAT, "radius",
- "The radius of the circle to be drawn");
+ .field(DocumentedOperation.FLOAT, "radius", "The radius of the circle to be drawn");
}
- protected void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3) {
+ @Override
+ protected void write(WireBuffer buffer, float v1, float v2, float v3) {
apply(buffer, v1, v2, v3);
}
- public DrawCircle(
- float left,
- float top,
- float right) {
+ public DrawCircle(float left, float top, float right) {
super(left, top, right);
mName = CLASS_NAME;
}
@@ -68,10 +78,7 @@
* @param y1
* @param x2
*/
- public static void apply(WireBuffer buffer,
- float x1,
- float y1,
- float x2) {
+ public static void apply(WireBuffer buffer, float x1, float y1, float x2) {
buffer.start(OP_CODE);
buffer.writeFloat(x1);
buffer.writeFloat(y1);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java
index a7276b5..dacbb03 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java
@@ -15,14 +15,13 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.SerializableToString;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
import java.util.List;
@@ -36,7 +35,6 @@
read(m, buffer, operations);
}
-
public static int id() {
return OP_CODE;
}
@@ -46,33 +44,32 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw a line segment")
- .field(FLOAT, "startX",
+ .field(
+ DocumentedOperation.FLOAT,
+ "startX",
"The x-coordinate of the start point of the line")
- .field(FLOAT, "startY",
+ .field(
+ DocumentedOperation.FLOAT,
+ "startY",
"The y-coordinate of the start point of the line")
- .field(FLOAT, "endX",
+ .field(
+ DocumentedOperation.FLOAT,
+ "endX",
"The x-coordinate of the end point of the line")
- .field(FLOAT, "endY",
+ .field(
+ DocumentedOperation.FLOAT,
+ "endY",
"The y-coordinate of the end point of the line");
}
- protected void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3,
- float v4) {
+ @Override
+ protected void write(WireBuffer buffer, float v1, float v2, float v3, float v4) {
apply(buffer, v1, v2, v3, v4);
}
- public DrawLine(
- float left,
- float top,
- float right,
- float bottom) {
+ public DrawLine(float left, float top, float right, float bottom) {
super(left, top, right, bottom);
mName = "DrawLine";
}
@@ -91,14 +88,11 @@
* @param x2 end x of the line
* @param y2 end y of the line
*/
- public static void apply(WireBuffer buffer,
- float x1,
- float y1,
- float x2,
- float y2) {
+ public static void apply(WireBuffer buffer, float x1, float y1, float x2, float y2) {
write(buffer, OP_CODE, x1, y1, x2, y2);
}
+ @Override
public void serializeToString(int indent, StringSerializer serializer) {
String x1 = "" + mX1;
if (Float.isNaN(mX1Value)) {
@@ -116,8 +110,6 @@
if (Float.isNaN(mY2Value)) {
y2 = "[" + Utils.idFromNan(mY2Value) + " = " + mY2 + "]";
}
- serializer.append(indent, CLASS_NAME
- + "(" + x1 + ", " + y1 + ", " + x2 + ", " + y2 + ")"
- );
+ serializer.append(indent, CLASS_NAME + "(" + x1 + ", " + y1 + ", " + x2 + ", " + y2 + ")");
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java
index 01761ef..5d498e8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java
@@ -15,13 +15,12 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
@@ -43,25 +42,16 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw the specified oval")
- .field(FLOAT, "left",
- "The left side of the oval")
- .field(FLOAT, "top",
- "The top of the oval")
- .field(FLOAT, "right",
- "The right side of the oval")
- .field(FLOAT, "bottom",
- "The bottom of the oval");
+ .field(DocumentedOperation.FLOAT, "left", "The left side of the oval")
+ .field(DocumentedOperation.FLOAT, "top", "The top of the oval")
+ .field(DocumentedOperation.FLOAT, "right", "The right side of the oval")
+ .field(DocumentedOperation.FLOAT, "bottom", "The bottom of the oval");
}
- protected void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3,
- float v4) {
+ @Override
+ protected void write(WireBuffer buffer, float v1, float v2, float v3, float v4) {
apply(buffer, v1, v2, v3, v4);
}
@@ -70,11 +60,7 @@
apply(buffer, mX1, mY1, mX2, mY2);
}
- public DrawOval(
- float left,
- float top,
- float right,
- float bottom) {
+ public DrawOval(float left, float top, float right, float bottom) {
super(left, top, right, bottom);
mName = CLASS_NAME;
}
@@ -83,6 +69,7 @@
public void paint(PaintContext context) {
context.drawOval(mX1, mY1, mX2, mY2);
}
+
/**
* Writes out the DrawOval to the buffer
*
@@ -92,11 +79,7 @@
* @param x2 end x of the DrawOval
* @param y2 end y of the DrawOval
*/
- public static void apply(WireBuffer buffer,
- float x1,
- float y1,
- float x2,
- float y2) {
+ public static void apply(WireBuffer buffer, float x1, float y1, float x2, float y2) {
write(buffer, OP_CODE, x1, y1, x2, y2);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
index bc2c53a..ccbf3d9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
@@ -15,14 +15,13 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.PaintOperation;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
@@ -54,12 +53,10 @@
operations.add(op);
}
-
public static String name() {
return CLASS_NAME;
}
-
public static int id() {
return Operations.DRAW_PATH;
}
@@ -70,14 +67,11 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Draw Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw a bitmap using integer coordinates")
- .field(INT, "id", "id of path");
+ .field(DocumentedOperation.INT, "id", "id of path");
}
-
@Override
public void paint(PaintContext context) {
context.drawPath(mId, mStart, mEnd);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java
index ad17fe7..644011b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java
@@ -15,19 +15,16 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Draw a Rectangle
- */
+/** Draw a Rectangle */
public class DrawRect extends DrawBase4 {
private static final int OP_CODE = Operations.DRAW_RECT;
private static final String CLASS_NAME = "DrawRect";
@@ -37,7 +34,6 @@
read(m, buffer, operations);
}
-
public static int id() {
return OP_CODE;
}
@@ -47,34 +43,20 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw the specified rectangle")
- .field(FLOAT, "left",
- "The left side of the rectangle")
- .field(FLOAT, "top",
- "The top of the rectangle")
- .field(FLOAT, "right",
- "The right side of the rectangle")
- .field(FLOAT, "bottom",
- "The bottom of the rectangle");
+ .field(DocumentedOperation.FLOAT, "left", "The left side of the rectangle")
+ .field(DocumentedOperation.FLOAT, "top", "The top of the rectangle")
+ .field(DocumentedOperation.FLOAT, "right", "The right side of the rectangle")
+ .field(DocumentedOperation.FLOAT, "bottom", "The bottom of the rectangle");
}
-
- protected void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3,
- float v4) {
+ @Override
+ protected void write(WireBuffer buffer, float v1, float v2, float v3, float v4) {
apply(buffer, v1, v2, v3, v4);
}
- public DrawRect(
- float left,
- float top,
- float right,
- float bottom) {
+ public DrawRect(float left, float top, float right, float bottom) {
super(left, top, right, bottom);
mName = CLASS_NAME;
}
@@ -88,16 +70,12 @@
* Writes out the DrawRect to the buffer
*
* @param buffer buffer to write to
- * @param x1 left x of rect
- * @param y1 top y of the rect
- * @param x2 right x of the rect
- * @param y2 bottom y of the rect
+ * @param x1 left x of rect
+ * @param y1 top y of the rect
+ * @param x2 right x of the rect
+ * @param y2 bottom y of the rect
*/
- public static void apply(WireBuffer buffer,
- float x1,
- float y1,
- float x2,
- float y2) {
+ public static void apply(WireBuffer buffer, float x1, float y1, float x2, float y2) {
write(buffer, OP_CODE, x1, y1, x2, y2);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
index 908e03a..64a3b28 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
@@ -15,24 +15,20 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Draw a rounded rectangle
- */
+/** Draw a rounded rectangle */
public class DrawRoundRect extends DrawBase6 {
private static final int OP_CODE = Operations.DRAW_ROUND_RECT;
private static final String CLASS_NAME = "DrawRoundRect";
-
public static void read(WireBuffer buffer, List<Operation> operations) {
Maker m = DrawRoundRect::new;
read(m, buffer, operations);
@@ -53,13 +49,8 @@
* @param v5 The x-radius of the oval used to round the corners
* @param v6 The y-radius of the oval used to round the corners
*/
- public static void apply(WireBuffer buffer,
- float v1,
- float v2,
- float v3,
- float v4,
- float v5,
- float v6) {
+ public static void apply(
+ WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6) {
buffer.start(OP_CODE);
buffer.writeFloat(v1);
buffer.writeFloat(v2);
@@ -69,50 +60,36 @@
buffer.writeFloat(v6);
}
- protected void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3,
- float v4,
- float v5,
- float v6) {
+ @Override
+ protected void write(
+ WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6) {
apply(buffer, v1, v2, v3, v4, v5, v6);
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw the specified round-rect")
- .field(FLOAT, "left",
- "The left side of the rect")
- .field(FLOAT, "top",
- "The top of the rect")
- .field(FLOAT, "right",
- "The right side of the rect")
- .field(FLOAT, "bottom",
- "The bottom of the rect")
- .field(FLOAT, "rx",
+ .field(DocumentedOperation.FLOAT, "left", "The left side of the rect")
+ .field(DocumentedOperation.FLOAT, "top", "The top of the rect")
+ .field(DocumentedOperation.FLOAT, "right", "The right side of the rect")
+ .field(DocumentedOperation.FLOAT, "bottom", "The bottom of the rect")
+ .field(
+ DocumentedOperation.FLOAT,
+ "rx",
"The x-radius of the oval used to round the corners")
- .field(FLOAT, "sweepAngle",
+ .field(
+ DocumentedOperation.FLOAT,
+ "sweepAngle",
"The y-radius of the oval used to round the corners");
}
-
- public DrawRoundRect(float v1,
- float v2,
- float v3,
- float v4,
- float v5,
- float v6) {
+ public DrawRoundRect(float v1, float v2, float v3, float v4, float v5, float v6) {
super(v1, v2, v3, v4, v5, v6);
mName = CLASS_NAME;
}
@Override
public void paint(PaintContext context) {
- context.drawRoundRect(mV1, mV2, mV3, mV4, mV5, mV6
- );
+ context.drawRoundRect(mV1, mV2, mV3, mV4, mV5, mV6);
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java
new file mode 100644
index 0000000..3cb1916
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java
@@ -0,0 +1,96 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+
+import java.util.List;
+
+public class DrawSector extends DrawBase6 {
+ public static final int OP_CODE = Operations.DRAW_SECTOR;
+ private static final String CLASS_NAME = "DrawSector";
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ Maker m = DrawSector::new;
+ read(m, buffer, operations);
+ }
+
+ public static int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ *
+ * @param buffer the buffer to write to
+ * @param v1 The left side of the Oval
+ * @param v2 The top of the Oval
+ * @param v3 The right side of the Oval
+ * @param v4 The bottom of the Oval
+ * @param v5 Starting angle (in degrees) where the arc begins
+ * @param v6 Sweep angle (in degrees) measured clockwise
+ */
+ public static void apply(
+ WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6) {
+ buffer.start(OP_CODE);
+ buffer.writeFloat(v1);
+ buffer.writeFloat(v2);
+ buffer.writeFloat(v3);
+ buffer.writeFloat(v4);
+ buffer.writeFloat(v5);
+ buffer.writeFloat(v6);
+ }
+
+ @Override
+ protected void write(
+ WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6) {
+ apply(buffer, v1, v2, v3, v4, v5, v6);
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
+ .description(
+ "Draw the specified sector (pie shape)"
+ + "which will be scaled to fit inside the specified oval")
+ .field(DocumentedOperation.FLOAT, "left", "The left side of the Oval")
+ .field(DocumentedOperation.FLOAT, "top", "The top of the Oval")
+ .field(DocumentedOperation.FLOAT, "right", "The right side of the Oval")
+ .field(DocumentedOperation.FLOAT, "bottom", "The bottom of the Oval")
+ .field(
+ DocumentedOperation.FLOAT,
+ "startAngle",
+ "Starting angle (in degrees) where the arc begins")
+ .field(
+ DocumentedOperation.FLOAT,
+ "sweepAngle",
+ "Sweep angle (in degrees) measured clockwise");
+ }
+
+ public DrawSector(float v1, float v2, float v3, float v4, float v5, float v6) {
+ super(v1, v2, v3, v4, v5, v6);
+ mName = "DrawSector";
+ }
+
+ @Override
+ public void paint(PaintContext context) {
+ context.drawSector(mV1, mV2, mV3, mV4, mV5, mV6);
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java
index 60dddc2..bcb7852 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java
@@ -15,9 +15,6 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.BOOLEAN;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
import com.android.internal.widget.remotecompose.core.Operation;
@@ -28,12 +25,11 @@
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Draw Text
- */
+/** Draw Text */
public class DrawText extends PaintOperation implements VariableSupport {
private static final int OP_CODE = Operations.DRAW_TEXT_RUN;
private static final String CLASS_NAME = "DrawText";
@@ -48,14 +44,15 @@
float mOutY = 0f;
boolean mRtl = false;
- public DrawText(int textID,
- int start,
- int end,
- int contextStart,
- int contextEnd,
- float x,
- float y,
- boolean rtl) {
+ public DrawText(
+ int textID,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean rtl) {
mTextID = textID;
mStart = start;
mEnd = end;
@@ -68,10 +65,8 @@
@Override
public void updateVariables(RemoteContext context) {
- mOutX = (Float.isNaN(mX))
- ? context.getFloat(Utils.idFromNan(mX)) : mX;
- mOutY = (Float.isNaN(mY))
- ? context.getFloat(Utils.idFromNan(mY)) : mY;
+ mOutX = Float.isNaN(mX) ? context.getFloat(Utils.idFromNan(mX)) : mX;
+ mOutY = Float.isNaN(mY) ? context.getFloat(Utils.idFromNan(mY)) : mY;
}
@Override
@@ -87,13 +82,20 @@
@Override
public void write(WireBuffer buffer) {
apply(buffer, mTextID, mStart, mEnd, mContextStart, mContextEnd, mX, mY, mRtl);
-
}
@Override
public String toString() {
- return "DrawTextRun [" + mTextID + "] " + mStart + ", " + mEnd + ", "
- + floatToString(mX, mOutX) + ", " + floatToString(mY, mOutY);
+ return "DrawTextRun ["
+ + mTextID
+ + "] "
+ + mStart
+ + ", "
+ + mEnd
+ + ", "
+ + floatToString(mX, mOutX)
+ + ", "
+ + floatToString(mY, mOutY);
}
public static void read(WireBuffer buffer, List<Operation> operations) {
@@ -114,7 +116,6 @@
return CLASS_NAME;
}
-
public static int id() {
return OP_CODE;
}
@@ -122,25 +123,26 @@
/**
* Writes out the operation to the buffer
*
- * @param buffer write the command to the buffer
- * @param textID id of the text
- * @param start Start position
- * @param end end position
+ * @param buffer write the command to the buffer
+ * @param textID id of the text
+ * @param start Start position
+ * @param end end position
* @param contextStart start of the context
- * @param contextEnd end of the context
- * @param x position of where to draw
- * @param y position of where to draw
- * @param rtl is it Right to Left text
+ * @param contextEnd end of the context
+ * @param x position of where to draw
+ * @param y position of where to draw
+ * @param rtl is it Right to Left text
*/
- public static void apply(WireBuffer buffer,
- int textID,
- int start,
- int end,
- int contextStart,
- int contextEnd,
- float x,
- float y,
- boolean rtl) {
+ public static void apply(
+ WireBuffer buffer,
+ int textID,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean rtl) {
buffer.start(Operations.DRAW_TEXT_RUN);
buffer.writeInt(textID);
buffer.writeInt(start);
@@ -152,33 +154,30 @@
buffer.writeBoolean(rtl);
}
-
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Draw Operations",
- id(),
- CLASS_NAME)
+ doc.operation("Draw Operations", id(), CLASS_NAME)
.description("Draw a run of text, all in a single direction")
- .field(INT, "textId", "id of bitmap")
- .field(INT, "start",
+ .field(DocumentedOperation.INT, "textId", "id of bitmap")
+ .field(
+ DocumentedOperation.INT,
+ "start",
"The start of the text to render. -1=end of string")
- .field(INT, "end",
- "The end of the text to render")
- .field(INT, "contextStart",
+ .field(DocumentedOperation.INT, "end", "The end of the text to render")
+ .field(
+ DocumentedOperation.INT,
+ "contextStart",
"the index of the start of the shaping context")
- .field(INT, "contextEnd",
+ .field(
+ DocumentedOperation.INT,
+ "contextEnd",
"the index of the end of the shaping context")
- .field(FLOAT, "x",
- "The x position at which to draw the text")
- .field(FLOAT, "y",
- "The y position at which to draw the text")
- .field(BOOLEAN, "RTL",
- "Whether the run is in RTL direction");
+ .field(DocumentedOperation.FLOAT, "x", "The x position at which to draw the text")
+ .field(DocumentedOperation.FLOAT, "y", "The y position at which to draw the text")
+ .field(DocumentedOperation.BOOLEAN, "RTL", "Whether the run is in RTL direction");
}
-
@Override
public void paint(PaintContext context) {
- context.drawTextRun(mTextID, mStart, mEnd, mContextStart,
- mContextEnd, mOutX, mOutY, mRtl);
+ context.drawTextRun(mTextID, mStart, mEnd, mContextStart, mContextEnd, mOutX, mOutY, mRtl);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java
index 242bc25..95a8766 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java
@@ -15,9 +15,6 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -26,12 +23,11 @@
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Draw Text in Anchored to a point
- */
+/** Draw Text in Anchored to a point */
public class DrawTextAnchored extends PaintOperation implements VariableSupport {
private static final int OP_CODE = Operations.DRAW_TEXT_ANCHOR;
private static final String CLASS_NAME = "DrawTextAnchored";
@@ -49,12 +45,7 @@
public static final int ANCHOR_TEXT_RTL = 1;
public static final int ANCHOR_MONOSPACE_MEASURE = 2;
- public DrawTextAnchored(int textID,
- float x,
- float y,
- float panX,
- float panY,
- int flags) {
+ public DrawTextAnchored(int textID, float x, float y, float panX, float panY, int flags) {
mTextID = textID;
mX = x;
mY = y;
@@ -67,14 +58,10 @@
@Override
public void updateVariables(RemoteContext context) {
- mOutX = (Float.isNaN(mX))
- ? context.getFloat(Utils.idFromNan(mX)) : mX;
- mOutY = (Float.isNaN(mY))
- ? context.getFloat(Utils.idFromNan(mY)) : mY;
- mOutPanX = (Float.isNaN(mPanX))
- ? context.getFloat(Utils.idFromNan(mPanX)) : mPanX;
- mOutPanY = (Float.isNaN(mPanY))
- ? context.getFloat(Utils.idFromNan(mPanY)) : mPanY;
+ mOutX = Float.isNaN(mX) ? context.getFloat(Utils.idFromNan(mX)) : mX;
+ mOutY = Float.isNaN(mY) ? context.getFloat(Utils.idFromNan(mY)) : mY;
+ mOutPanX = Float.isNaN(mPanX) ? context.getFloat(Utils.idFromNan(mPanX)) : mPanX;
+ mOutPanY = Float.isNaN(mPanY) ? context.getFloat(Utils.idFromNan(mPanY)) : mPanY;
}
@Override
@@ -95,18 +82,22 @@
@Override
public void write(WireBuffer buffer) {
- apply(buffer, mTextID, mX,
- mY,
- mPanX,
- mPanY,
- mFlags);
+ apply(buffer, mTextID, mX, mY, mPanX, mPanY, mFlags);
}
@Override
public String toString() {
- return "DrawTextAnchored [" + mTextID + "] " + floatToStr(mX) + ", "
- + floatToStr(mY) + ", "
- + floatToStr(mPanX) + ", " + floatToStr(mPanY) + ", "
+ return "DrawTextAnchored ["
+ + mTextID
+ + "] "
+ + floatToStr(mX)
+ + ", "
+ + floatToStr(mY)
+ + ", "
+ + floatToStr(mPanX)
+ + ", "
+ + floatToStr(mPanY)
+ + ", "
+ Integer.toBinaryString(mFlags);
}
@@ -125,10 +116,7 @@
float panY = buffer.readFloat();
int flags = buffer.readInt();
- DrawTextAnchored op = new DrawTextAnchored(textID,
- x, y,
- panX, panY,
- flags);
+ DrawTextAnchored op = new DrawTextAnchored(textID, x, y, panX, panY, flags);
operations.add(op);
}
@@ -152,13 +140,8 @@
* @param panY The pan from top(-1) to bottom(1) 0 being centered
* @param flags Change the behaviour
*/
- public static void apply(WireBuffer buffer,
- int textID,
- float x,
- float y,
- float panX,
- float panY,
- int flags) {
+ public static void apply(
+ WireBuffer buffer, int textID, float x, float y, float panX, float panY, int flags) {
buffer.start(OP_CODE);
buffer.writeInt(textID);
buffer.writeFloat(x);
@@ -169,25 +152,22 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Draw Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw text centered about an anchor point")
- .field(INT, "textId", "id of bitmap")
- .field(FLOAT, "x",
- "The x-position of the anchor point")
- .field(FLOAT, "y",
- "The y-position of the anchor point")
- .field(FLOAT, "panX",
+ .field(DocumentedOperation.INT, "textId", "id of bitmap")
+ .field(DocumentedOperation.FLOAT, "x", "The x-position of the anchor point")
+ .field(DocumentedOperation.FLOAT, "y", "The y-position of the anchor point")
+ .field(
+ DocumentedOperation.FLOAT,
+ "panX",
"The pan from left(-1) to right(1) 0 being centered")
- .field(FLOAT, "panY",
+ .field(
+ DocumentedOperation.FLOAT,
+ "panY",
"The pan from top(-1) to bottom(1) 0 being centered")
- .field(INT, "flags",
- "Change the behaviour");
-
+ .field(DocumentedOperation.INT, "flags", "Change the behaviour");
}
-
float[] mBounds = new float[4];
private float getHorizontalOffset() {
@@ -196,8 +176,7 @@
float textWidth = scale * (mBounds[2] - mBounds[0]);
float boxWidth = 0;
- return (boxWidth - textWidth) * (1 + mOutPanX) / 2.f
- - (scale * mBounds[0]);
+ return (boxWidth - textWidth) * (1 + mOutPanX) / 2.f - (scale * mBounds[0]);
}
private float getVerticalOffset() {
@@ -205,18 +184,18 @@
float scale = 1.0f;
float boxHeight = 0;
float textHeight = scale * (mBounds[3] - mBounds[1]);
- return (boxHeight - textHeight) * (1 - mOutPanY) / 2
- - (scale * mBounds[1]);
+ return (boxHeight - textHeight) * (1 - mOutPanY) / 2 - (scale * mBounds[1]);
}
@Override
public void paint(PaintContext context) {
- int flags = ((mFlags & ANCHOR_MONOSPACE_MEASURE) != 0)
- ? PaintContext.TEXT_MEASURE_MONOSPACE_WIDTH : 0;
+ int flags =
+ ((mFlags & ANCHOR_MONOSPACE_MEASURE) != 0)
+ ? PaintContext.TEXT_MEASURE_MONOSPACE_WIDTH
+ : 0;
context.getTextBounds(mTextID, 0, -1, flags, mBounds);
float x = mOutX + getHorizontalOffset();
- float y = (Float.isNaN(mOutPanY)) ? mOutY : mOutY + getVerticalOffset();
- context.drawTextRun(mTextID, 0, -1, 0, 1, x, y,
- (mFlags & ANCHOR_TEXT_RTL) == 1);
+ float y = Float.isNaN(mOutPanY) ? mOutY : mOutY + getVerticalOffset();
+ context.drawTextRun(mTextID, 0, -1, 0, 1, x, y, (mFlags & ANCHOR_TEXT_RTL) == 1);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
index d69362b..aefd6f3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
@@ -15,10 +15,6 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -27,13 +23,12 @@
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Draw text along a path.
- */
-public class DrawTextOnPath extends PaintOperation implements VariableSupport {
+/** Draw text along a path. */
+public class DrawTextOnPath extends PaintOperation implements VariableSupport {
private static final int OP_CODE = Operations.DRAW_TEXT_ON_PATH;
private static final String CLASS_NAME = "DrawTextOnPath";
int mPathId;
@@ -46,18 +41,16 @@
public DrawTextOnPath(int textId, int pathId, float hOffset, float vOffset) {
mPathId = pathId;
mTextId = textId;
- mOutHOffset = mHOffset = vOffset;
- mOutVOffset = mVOffset = hOffset;
+ mOutHOffset = mHOffset = hOffset;
+ mOutVOffset = mVOffset = vOffset;
}
-
@Override
public void updateVariables(RemoteContext context) {
- mOutHOffset = (Float.isNaN(mHOffset))
- ? context.getFloat(Utils.idFromNan(mHOffset)) : mHOffset;
- mOutVOffset = (Float.isNaN(mVOffset))
- ? context.getFloat(Utils.idFromNan(mVOffset)) : mVOffset;
-
+ mOutHOffset =
+ Float.isNaN(mHOffset) ? context.getFloat(Utils.idFromNan(mHOffset)) : mHOffset;
+ mOutVOffset =
+ Float.isNaN(mVOffset) ? context.getFloat(Utils.idFromNan(mVOffset)) : mVOffset;
}
@Override
@@ -77,16 +70,21 @@
@Override
public String toString() {
- return "DrawTextOnPath [" + mTextId + "] [" + mPathId + "] "
- + floatToString(mHOffset, mOutHOffset) + ", "
- + floatToString(mVOffset, mOutVOffset);
+ return "DrawTextOnPath ["
+ + mTextId
+ + "] ["
+ + mPathId
+ + "] "
+ + Utils.floatToString(mHOffset, mOutHOffset)
+ + ", "
+ + Utils.floatToString(mVOffset, mOutVOffset);
}
public static void read(WireBuffer buffer, List<Operation> operations) {
int textId = buffer.readInt();
int pathId = buffer.readInt();
- float hOffset = buffer.readFloat();
float vOffset = buffer.readFloat();
+ float hOffset = buffer.readFloat();
DrawTextOnPath op = new DrawTextOnPath(textId, pathId, hOffset, vOffset);
operations.add(op);
}
@@ -95,33 +93,26 @@
return "DrawTextOnPath";
}
-
public static int id() {
return Operations.DRAW_TEXT_ON_PATH;
}
- public static void apply(WireBuffer buffer, int textId, int pathId,
- float hOffset, float vOffset) {
+ public static void apply(
+ WireBuffer buffer, int textId, int pathId, float hOffset, float vOffset) {
buffer.start(OP_CODE);
buffer.writeInt(textId);
buffer.writeInt(pathId);
- buffer.writeFloat(hOffset);
buffer.writeFloat(vOffset);
+ buffer.writeFloat(hOffset);
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Draw Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw text along path object")
- .field(INT, "textId",
- "id of the text")
- .field(INT, "pathId",
- "id of the path")
- .field(FLOAT, "xOffset",
- "x Shift of the text")
- .field(FLOAT, "yOffset",
- "y Shift of the text");
+ .field(DocumentedOperation.INT, "textId", "id of the text")
+ .field(DocumentedOperation.INT, "pathId", "id of the path")
+ .field(DocumentedOperation.FLOAT, "xOffset", "x Shift of the text")
+ .field(DocumentedOperation.FLOAT, "yOffset", "y Shift of the text");
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
index 3547263..b6d45d9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
@@ -15,8 +15,6 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
import com.android.internal.widget.remotecompose.core.Operation;
@@ -27,6 +25,7 @@
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
@@ -42,12 +41,7 @@
int mPath1Id;
int mPath2Id;
- public DrawTweenPath(
- int path1Id,
- int path2Id,
- float tween,
- float start,
- float stop) {
+ public DrawTweenPath(int path1Id, int path2Id, float tween, float start, float stop) {
mOutTween = mTween = tween;
mOutStart = mStart = start;
mOutStop = mStop = stop;
@@ -57,12 +51,9 @@
@Override
public void updateVariables(RemoteContext context) {
- mOutTween = (Float.isNaN(mTween))
- ? context.getFloat(Utils.idFromNan(mTween)) : mTween;
- mOutStart = (Float.isNaN(mStart))
- ? context.getFloat(Utils.idFromNan(mStart)) : mStart;
- mOutStop = (Float.isNaN(mStop))
- ? context.getFloat(Utils.idFromNan(mStop)) : mStop;
+ mOutTween = Float.isNaN(mTween) ? context.getFloat(Utils.idFromNan(mTween)) : mTween;
+ mOutStart = Float.isNaN(mStart) ? context.getFloat(Utils.idFromNan(mStart)) : mStart;
+ mOutStop = Float.isNaN(mStop) ? context.getFloat(Utils.idFromNan(mStop)) : mStop;
}
@Override
@@ -80,49 +71,44 @@
@Override
public void write(WireBuffer buffer) {
- apply(buffer, mPath1Id,
- mPath2Id,
- mTween,
- mStart,
- mStop);
+ apply(buffer, mPath1Id, mPath2Id, mTween, mStart, mStop);
}
@Override
public String toString() {
- return "DrawTweenPath " + mPath1Id + " " + mPath2Id
- + " " + floatToString(mTween, mOutTween) + " "
- + floatToString(mStart, mOutStart) + " "
- + "- " + floatToString(mStop, mOutStop);
+ return "DrawTweenPath "
+ + mPath1Id
+ + " "
+ + mPath2Id
+ + " "
+ + floatToString(mTween, mOutTween)
+ + " "
+ + floatToString(mStart, mOutStart)
+ + " "
+ + "- "
+ + floatToString(mStop, mOutStop);
}
-
public static void read(WireBuffer buffer, List<Operation> operations) {
int path1Id = buffer.readInt();
int path2Id = buffer.readInt();
float tween = buffer.readFloat();
float start = buffer.readFloat();
float stop = buffer.readFloat();
- DrawTweenPath op = new DrawTweenPath(path1Id, path2Id,
- tween, start, stop);
+ DrawTweenPath op = new DrawTweenPath(path1Id, path2Id, tween, start, stop);
operations.add(op);
}
-
public static String name() {
return "DrawTweenPath";
}
-
public static int id() {
return Operations.DRAW_TWEEN_PATH;
}
- public static void apply(WireBuffer buffer,
- int path1Id,
- int path2Id,
- float tween,
- float start,
- float stop) {
+ public static void apply(
+ WireBuffer buffer, int path1Id, int path2Id, float tween, float start, float stop) {
buffer.start(OP_CODE);
buffer.writeInt(path1Id);
buffer.writeInt(path2Id);
@@ -131,33 +117,18 @@
buffer.writeFloat(stop);
}
-
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Draw Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw text along path object")
- .field(INT, "pathId1",
- "id of path 1")
- .field(INT, "pathId2",
- "id of path 2")
- .field(FLOAT, "tween",
- "interpolate between the two paths")
- .field(FLOAT, "start",
- "trim the start of the path")
- .field(FLOAT, "yOffset",
- "trim the end of the path");
-
+ .field(DocumentedOperation.INT, "pathId1", "id of path 1")
+ .field(DocumentedOperation.INT, "pathId2", "id of path 2")
+ .field(DocumentedOperation.FLOAT, "tween", "interpolate between the two paths")
+ .field(DocumentedOperation.FLOAT, "start", "trim the start of the path")
+ .field(DocumentedOperation.FLOAT, "yOffset", "trim the end of the path");
}
-
@Override
public void paint(PaintContext context) {
- context.drawTweenPath(mPath1Id,
- mPath2Id,
- mOutTween,
- mOutStart,
- mOutStop);
+ context.drawTweenPath(mPath1Id, mPath2Id, mOutTween, mOutStart, mOutStop);
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
index 31b8ff6..765e150 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
@@ -15,21 +15,19 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Operation to deal with Text data
- */
-public class FloatConstant implements Operation {
+/** Operation to deal with Text data */
+public class FloatConstant implements com.android.internal.widget.remotecompose.core.Operation {
private static final int OP_CODE = Operations.DATA_FLOAT;
private static final String CLASS_NAME = "FloatConstant";
public int mTextId;
@@ -54,7 +52,6 @@
return CLASS_NAME;
}
-
public static int id() {
return OP_CODE;
}
@@ -63,8 +60,8 @@
* Writes out the operation to the buffer
*
* @param buffer write command to this buffer
- * @param id the id
- * @param value the value of the float
+ * @param id the id
+ * @param value the value of the float
*/
public static void apply(WireBuffer buffer, int id, float value) {
buffer.start(OP_CODE);
@@ -80,14 +77,10 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Expressions Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("A float and its associated id")
- .field(INT, "id", "id of float")
- .field(FLOAT, "value",
- "32-bit float value");
-
+ .field(DocumentedOperation.INT, "id", "id of float")
+ .field(FLOAT, "value", "32-bit float value");
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java
index e3df1eb..d717933 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java
@@ -15,10 +15,10 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT_ARRAY;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.SHORT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.SHORT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -26,19 +26,17 @@
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import com.android.internal.widget.remotecompose.core.operations.utilities.AnimatedFloatExpression;
import com.android.internal.widget.remotecompose.core.operations.utilities.NanMap;
import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation;
-import java.util.Arrays;
import java.util.List;
/**
- * Operation to deal with AnimatedFloats
- * This is designed to be an optimized calculation for things like
- * injecting the width of the component int draw rect
- * As well as supporting generalized animation floats.
- * The floats represent a RPN style calculator
+ * Operation to deal with AnimatedFloats This is designed to be an optimized calculation for things
+ * like injecting the width of the component int draw rect As well as supporting generalized
+ * animation floats. The floats represent a RPN style calculator
*/
public class FloatExpression implements Operation, VariableSupport {
private static final int OP_CODE = Operations.ANIMATED_FLOAT;
@@ -49,6 +47,7 @@
public FloatAnimation mFloatAnimation;
public float[] mPreCalcValue;
private float mLastChange = Float.NaN;
+ private float mLastCalculatedValue = Float.NaN;
AnimatedFloatExpression mExp = new AnimatedFloatExpression();
public static final int MAX_EXPRESSION_SIZE = 32;
@@ -70,12 +69,12 @@
boolean value_changed = false;
for (int i = 0; i < mSrcValue.length; i++) {
float v = mSrcValue[i];
- if (Float.isNaN(v) && !AnimatedFloatExpression.isMathOperator(v)
+ if (Float.isNaN(v)
+ && !AnimatedFloatExpression.isMathOperator(v)
&& !NanMap.isDataVariable(v)) {
float newValue = context.getFloat(Utils.idFromNan(v));
if (mFloatAnimation != null) {
if (mPreCalcValue[i] != newValue) {
- mLastChange = context.getAnimationTime();
value_changed = true;
mPreCalcValue[i] = newValue;
}
@@ -86,8 +85,18 @@
mPreCalcValue[i] = mSrcValue[i];
}
}
+ float v = mLastCalculatedValue;
+ if (value_changed) { // inputs changed check if output changed
+ v = mExp.eval(mPreCalcValue, mPreCalcValue.length);
+ if (v != mLastCalculatedValue) {
+ mLastChange = context.getAnimationTime();
+ mLastCalculatedValue = v;
+ } else {
+ value_changed = false;
+ }
+ }
+
if (value_changed && mFloatAnimation != null) {
- float v = mExp.eval(Arrays.copyOf(mPreCalcValue, mPreCalcValue.length));
if (Float.isNaN(mFloatAnimation.getTargetValue())) {
mFloatAnimation.setInitialValue(v);
} else {
@@ -100,7 +109,8 @@
@Override
public void registerListening(RemoteContext context) {
for (float v : mSrcValue) {
- if (Float.isNaN(v) && !AnimatedFloatExpression.isMathOperator(v)
+ if (Float.isNaN(v)
+ && !AnimatedFloatExpression.isMathOperator(v)
&& !NanMap.isDataVariable(v)) {
context.listensTo(Utils.idFromNan(v), this);
}
@@ -118,8 +128,9 @@
float f = mFloatAnimation.get(t - mLastChange);
context.loadFloat(mId, f);
} else {
- context.loadFloat(mId, mExp.eval(context.getCollectionsAccess(),
- Arrays.copyOf(mPreCalcValue, mPreCalcValue.length)));
+ context.loadFloat(
+ mId,
+ mExp.eval(context.getCollectionsAccess(), mPreCalcValue, mPreCalcValue.length));
}
}
@@ -137,11 +148,17 @@
}
}
if (mPreCalcValue == null) {
- return "FloatExpression[" + mId + "] = ("
- + AnimatedFloatExpression.toString(mSrcValue, labels) + ")";
+ return "FloatExpression["
+ + mId
+ + "] = ("
+ + AnimatedFloatExpression.toString(mSrcValue, labels)
+ + ")";
}
- return "FloatExpression[" + mId + "] = ("
- + AnimatedFloatExpression.toString(mPreCalcValue, labels) + ")";
+ return "FloatExpression["
+ + mId
+ + "] = ("
+ + AnimatedFloatExpression.toString(mPreCalcValue, labels)
+ + ")";
}
public static String name() {
@@ -155,9 +172,9 @@
/**
* Writes out the operation to the buffer
*
- * @param buffer The buffer to write to
- * @param id the id of the resulting float
- * @param value the float expression array
+ * @param buffer The buffer to write to
+ * @param id the id of the resulting float
+ * @param value the float expression array
* @param animation the animation expression array
*/
public static void apply(WireBuffer buffer, int id, float[] value, float[] animation) {
@@ -178,7 +195,6 @@
buffer.writeFloat(v);
}
}
-
}
public static void read(WireBuffer buffer, List<Operation> operations) {
@@ -207,16 +223,20 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Expressions Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("A Float expression")
- .field(INT, "id", "The id of the Color")
+ .field(DocumentedOperation.INT, "id", "The id of the Color")
.field(SHORT, "expression_length", "expression length")
.field(SHORT, "animation_length", "animation description length")
- .field(FLOAT_ARRAY, "expression", "expression_length",
+ .field(
+ FLOAT_ARRAY,
+ "expression",
+ "expression_length",
"Sequence of Floats representing and expression")
- .field(FLOAT_ARRAY, "AnimationSpec", "animation_length",
+ .field(
+ FLOAT_ARRAY,
+ "AnimationSpec",
+ "animation_length",
"Sequence of Floats representing animation curve")
.field(FLOAT, "duration", "> time in sec")
.field(INT, "bits", "> WRAP|INITALVALUE | TYPE ")
@@ -229,5 +249,4 @@
public String deepToString(String indent) {
return indent + toString();
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java
index 099bce88..4f8516f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.LONG;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.LONG;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -29,9 +29,9 @@
/**
* Describe some basic information for a RemoteCompose document
- * <p>
- * It encodes the version of the document (following semantic versioning) as well
- * as the dimensions of the document in pixels.
+ *
+ * <p>It encodes the version of the document (following semantic versioning) as well as the
+ * dimensions of the document in pixels.
*/
public class Header implements RemoteComposeOperation {
private static final int OP_CODE = Operations.HEADER;
@@ -50,21 +50,26 @@
float mDensity;
long mCapabilities;
-
/**
- * It encodes the version of the document (following semantic versioning) as well
- * as the dimensions of the document in pixels.
+ * It encodes the version of the document (following semantic versioning) as well as the
+ * dimensions of the document in pixels.
*
* @param majorVersion the major version of the RemoteCompose document API
* @param minorVersion the minor version of the RemoteCompose document API
* @param patchVersion the patch version of the RemoteCompose document API
- * @param width the width of the RemoteCompose document
- * @param height the height of the RemoteCompose document
- * @param density the density at which the document was originally created
+ * @param width the width of the RemoteCompose document
+ * @param height the height of the RemoteCompose document
+ * @param density the density at which the document was originally created
* @param capabilities bitmask field storing needed capabilities (unused for now)
*/
- public Header(int majorVersion, int minorVersion, int patchVersion,
- int width, int height, float density, long capabilities) {
+ public Header(
+ int majorVersion,
+ int minorVersion,
+ int patchVersion,
+ int width,
+ int height,
+ float density,
+ long capabilities) {
this.mMajorVersion = majorVersion;
this.mMinorVersion = minorVersion;
this.mPatchVersion = patchVersion;
@@ -81,9 +86,19 @@
@Override
public String toString() {
- return "HEADER v" + mMajorVersion + "."
- + mMinorVersion + "." + mPatchVersion + ", "
- + mWidth + " x " + mHeight + " [" + mCapabilities + "]";
+ return "HEADER v"
+ + mMajorVersion
+ + "."
+ + mMinorVersion
+ + "."
+ + mPatchVersion
+ + ", "
+ + mWidth
+ + " x "
+ + mHeight
+ + " ["
+ + mCapabilities
+ + "]";
}
@Override
@@ -104,8 +119,8 @@
return OP_CODE;
}
- public static void apply(WireBuffer buffer, int width, int height,
- float density, long capabilities) {
+ public static void apply(
+ WireBuffer buffer, int width, int height, float density, long capabilities) {
buffer.start(OP_CODE);
buffer.writeInt(MAJOR_VERSION); // major version number of the protocol
buffer.writeInt(MINOR_VERSION); // minor version number of the protocol
@@ -125,15 +140,23 @@
// float density = buffer.readFloat();
float density = 1f;
long capabilities = buffer.readLong();
- Header header = new Header(majorVersion, minorVersion, patchVersion,
- width, height, density, capabilities);
+ Header header =
+ new Header(
+ majorVersion,
+ minorVersion,
+ patchVersion,
+ width,
+ height,
+ density,
+ capabilities);
operations.add(header);
}
public static void documentation(DocumentationBuilder doc) {
doc.operation("Protocol Operations", OP_CODE, CLASS_NAME)
- .description("Document metadata, containing the version,"
- + " original size & density, capabilities mask")
+ .description(
+ "Document metadata, containing the version,"
+ + " original size & density, capabilities mask")
.field(INT, "MAJOR_VERSION", "Major version")
.field(INT, "MINOR_VERSION", "Minor version")
.field(INT, "PATCH_VERSION", "Patch version")
@@ -142,5 +165,4 @@
// .field(FLOAT, "DENSITY", "Major version")
.field(LONG, "CAPABILITIES", "Major version");
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java
index 11730f3..c9a8508 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT_ARRAY;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,17 +24,16 @@
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import com.android.internal.widget.remotecompose.core.operations.utilities.IntegerExpressionEvaluator;
import java.util.Arrays;
import java.util.List;
/**
- * Operation to deal with AnimatedFloats
- * This is designed to be an optimized calculation for things like
- * injecting the width of the component int draw rect
- * As well as supporting generalized animation floats.
- * The floats represent a RPN style calculator
+ * Operation to deal with AnimatedFloats This is designed to be an optimized calculation for things
+ * like injecting the width of the component int draw rect As well as supporting generalized
+ * animation floats. The floats represent a RPN style calculator
*/
public class IntegerExpression implements Operation, VariableSupport {
private static final int OP_CODE = Operations.INTEGER_EXPRESSION;
@@ -45,7 +44,7 @@
public int[] mSrcValue;
public int[] mPreCalcValue;
private float mLastChange = Float.NaN;
- public static final int MAX_STRING_SIZE = 4000;
+ public static final int MAX_SIZE = 320;
IntegerExpressionEvaluator mExp = new IntegerExpressionEvaluator();
public IntegerExpression(int id, int mask, int[] value) {
@@ -70,7 +69,6 @@
}
}
-
@Override
public void registerListening(RemoteContext context) {
for (int i = 0; i < mSrcValue.length; i++) {
@@ -91,6 +89,21 @@
context.loadInteger(mId, v);
}
+ /**
+ * Evaluate the expression
+ *
+ * @param context current context
+ * @return the resulting value
+ */
+ public int evaluate(RemoteContext context) {
+ updateVariables(context);
+ float t = context.getAnimationTime();
+ if (Float.isNaN(mLastChange)) {
+ mLastChange = t;
+ }
+ return mExp.eval(mPreMask, Arrays.copyOf(mPreCalcValue, mPreCalcValue.length));
+ }
+
@Override
public void write(WireBuffer buffer) {
apply(buffer, mId, mMask, mSrcValue);
@@ -99,6 +112,9 @@
@Override
public String toString() {
StringBuilder s = new StringBuilder();
+ if (mPreCalcValue == null) {
+ return "";
+ }
for (int i = 0; i < mPreCalcValue.length; i++) {
if (i != 0) {
s.append(" ");
@@ -128,9 +144,9 @@
* Writes out the operation to the buffer
*
* @param buffer buffer to write to
- * @param id the id of the integer
- * @param mask the mask bits of ints & operators or variables
- * @param value array of integers to be evaluated
+ * @param id the id of the integer
+ * @param mask the mask bits of ints & operators or variables
+ * @param value array of integers to be evaluated
*/
public static void apply(WireBuffer buffer, int id, int mask, int[] value) {
buffer.start(OP_CODE);
@@ -146,7 +162,9 @@
int id = buffer.readInt();
int mask = buffer.readInt();
int len = buffer.readInt();
-
+ if (len > MAX_SIZE) {
+ throw new RuntimeException("buffer corrupt integer expression " + len);
+ }
int[] values = new int[len];
for (int i = 0; i < values.length; i++) {
values[i] = buffer.readInt();
@@ -156,17 +174,12 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Data Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Expression that computes an integer")
- .field(INT, "id", "id of integer")
- .field(INT, "mask",
- "bits representing operators or other id's")
- .field(INT, "length",
- "length of array")
- .field(INT_ARRAY, "values", "length",
- "Array of ints");
+ .field(DocumentedOperation.INT, "id", "id of integer")
+ .field(INT, "mask", "bits representing operators or other id's")
+ .field(INT, "length", "length of array")
+ .field(INT_ARRAY, "values", "length", "Array of ints");
}
@Override
@@ -177,8 +190,8 @@
/**
* given the "i" position in the mask is this an ID
*
- * @param mask 32 bit mask used for defining numbers vs other
- * @param i the bit in question
+ * @param mask 32 bit mask used for defining numbers vs other
+ * @param i the bit in question
* @param value the value
* @return true if this is an ID
*/
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
index f3f9a51..04f8a50 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
@@ -27,15 +27,14 @@
public class MatrixRestore extends PaintOperation {
private static final int OP_CODE = Operations.MATRIX_RESTORE;
private static final String CLASS_NAME = "MatrixRestore";
- public MatrixRestore() {
- }
+
+ public MatrixRestore() {}
@Override
public void write(WireBuffer buffer) {
apply(buffer);
}
-
public static void read(WireBuffer buffer, List<Operation> operations) {
MatrixRestore op = new MatrixRestore();
operations.add(op);
@@ -46,12 +45,10 @@
return "MatrixRestore";
}
-
public static String name() {
return CLASS_NAME;
}
-
public static int id() {
return OP_CODE;
}
@@ -61,13 +58,10 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Restore the matrix and clip");
}
-
@Override
public void paint(PaintContext context) {
context.matrixRestore();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java
index 9cc82fc..df10f32 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java
@@ -15,13 +15,12 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
@@ -30,12 +29,13 @@
private static final String CLASS_NAME = "MatrixRotate";
public static void read(WireBuffer buffer, List<Operation> operations) {
- Maker m = new Maker() {
- @Override
- public DrawBase3 create(float v1, float v2, float v3) {
- return new MatrixRotate(v1, v2, v3);
- }
- };
+ Maker m =
+ new Maker() {
+ @Override
+ public DrawBase3 create(float v1, float v2, float v3) {
+ return new MatrixRotate(v1, v2, v3);
+ }
+ };
read(m, buffer, operations);
}
@@ -48,20 +48,15 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("apply rotation to matrix")
- .field(FLOAT, "rotate", "Angle to rotate")
- .field(FLOAT, "pivotX", "X Pivot point")
- .field(FLOAT, "pivotY", "Y Pivot point");
+ .field(DocumentedOperation.FLOAT, "rotate", "Angle to rotate")
+ .field(DocumentedOperation.FLOAT, "pivotX", "X Pivot point")
+ .field(DocumentedOperation.FLOAT, "pivotY", "Y Pivot point");
}
-
- protected void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3) {
+ @Override
+ protected void write(WireBuffer buffer, float v1, float v2, float v3) {
apply(buffer, v1, v2, v3);
}
@@ -83,10 +78,7 @@
* @param y1 X Pivot point
* @param x2 Y Pivot point
*/
- public static void apply(WireBuffer buffer,
- float x1,
- float y1,
- float x2) {
+ public static void apply(WireBuffer buffer, float x1, float y1, float x2) {
buffer.start(OP_CODE);
buffer.writeFloat(x1);
buffer.writeFloat(y1);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java
index a47ed6b..67612c7 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java
@@ -47,7 +47,6 @@
return CLASS_NAME;
}
-
public static int id() {
return OP_CODE;
}
@@ -57,9 +56,7 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Save the matrix and clip to a stack");
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java
index 769e798..26c898a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java
@@ -15,13 +15,12 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
@@ -42,28 +41,18 @@
return CLASS_NAME;
}
-
- protected void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3,
- float v4) {
+ @Override
+ protected void write(WireBuffer buffer, float v1, float v2, float v3, float v4) {
apply(buffer, v1, v2, v3, v4);
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw the specified Oval")
- .field(FLOAT, "scaleX",
- "The amount to scale in X")
- .field(FLOAT, "scaleY",
- "The amount to scale in Y")
- .field(FLOAT, "pivotX",
- "The x-coordinate for the pivot point")
- .field(FLOAT, "pivotY",
- "The y-coordinate for the pivot point");
+ .field(DocumentedOperation.FLOAT, "scaleX", "The amount to scale in X")
+ .field(DocumentedOperation.FLOAT, "scaleY", "The amount to scale in Y")
+ .field(DocumentedOperation.FLOAT, "pivotX", "The x-coordinate for the pivot point")
+ .field(DocumentedOperation.FLOAT, "pivotY", "The y-coordinate for the pivot point");
}
public MatrixScale(float scaleX, float scaleY, float centerX, float centerY) {
@@ -80,16 +69,12 @@
* Writes out the DrawOval to the buffer
*
* @param buffer buffer to write to
- * @param x1 start x of DrawOval
- * @param y1 start y of the DrawOval
- * @param x2 end x of the DrawOval
- * @param y2 end y of the DrawOval
+ * @param x1 start x of DrawOval
+ * @param y1 start y of the DrawOval
+ * @param x2 end x of the DrawOval
+ * @param y2 end y of the DrawOval
*/
- public static void apply(WireBuffer buffer,
- float x1,
- float y1,
- float x2,
- float y2) {
+ public static void apply(WireBuffer buffer, float x1, float y1, float x2, float y2) {
write(buffer, OP_CODE, x1, y1, x2, y2);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java
index 34f71b4..d641178 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -29,7 +29,6 @@
public static final int OP_CODE = Operations.MATRIX_SKEW;
public static final String CLASS_NAME = "MatrixSkew";
-
public static void read(WireBuffer buffer, List<Operation> operations) {
Maker m = MatrixSkew::new;
read(m, buffer, operations);
@@ -43,25 +42,18 @@
return CLASS_NAME;
}
-
- protected void write(WireBuffer buffer,
- float v1,
- float v2) {
+ @Override
+ protected void write(WireBuffer buffer, float v1, float v2) {
apply(buffer, v1, v2);
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Current matrix with the specified skew.")
- .field(FLOAT, "skewX",
- "The amount to skew in X")
- .field(FLOAT, "skewY",
- "The amount to skew in Y");
+ .field(FLOAT, "skewX", "The amount to skew in X")
+ .field(FLOAT, "skewY", "The amount to skew in Y");
}
-
public MatrixSkew(float skewX, float skewY) {
super(skewX, skewY);
mName = CLASS_NAME;
@@ -76,13 +68,10 @@
* Writes out the DrawOval to the buffer
*
* @param buffer buffer to write to
- * @param x1 start x of DrawOval
- * @param y1 start y of the DrawOval
+ * @param x1 start x of DrawOval
+ * @param y1 start y of the DrawOval
*/
- public static void apply(WireBuffer buffer,
- float x1,
- float y1
- ) {
+ public static void apply(WireBuffer buffer, float x1, float y1) {
write(buffer, OP_CODE, x1, y1);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java
index 8561343..e008292 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java
@@ -15,13 +15,12 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
@@ -42,22 +41,16 @@
return CLASS_NAME;
}
- protected void write(WireBuffer buffer,
- float v1,
- float v2) {
+ @Override
+ protected void write(WireBuffer buffer, float v1, float v2) {
apply(buffer, v1, v2);
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- "MatrixTranslate")
+ doc.operation("Canvas Operations", OP_CODE, "MatrixTranslate")
.description("Preconcat the current matrix with the specified translation")
- .field(FLOAT, "dx",
- "The distance to translate in X")
- .field(FLOAT, "dy",
- "The distance to translate in Y");
-
+ .field(DocumentedOperation.FLOAT, "dx", "The distance to translate in X")
+ .field(DocumentedOperation.FLOAT, "dy", "The distance to translate in Y");
}
public MatrixTranslate(float translateX, float translateY) {
@@ -74,12 +67,10 @@
* Writes out the DrawOval to the buffer
*
* @param buffer buffer to write to
- * @param x1 start x of DrawOval
- * @param y1 start y of the DrawOval
+ * @param x1 start x of DrawOval
+ * @param y1 start y of the DrawOval
*/
- public static void apply(WireBuffer buffer,
- float x1,
- float y1) {
+ public static void apply(WireBuffer buffer, float x1, float y1) {
write(buffer, OP_CODE, x1, y1);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java
index 2cf83cd..fa6e271 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java
@@ -15,20 +15,19 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.UTF8;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.UTF8;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Operation to deal with Text data
- */
+/** Operation to deal with Text data */
public class NamedVariable implements Operation {
private static final int OP_CODE = Operations.NAMED_VARIABLE;
private static final String CLASS_NAME = "NamedVariable";
@@ -39,6 +38,7 @@
public static final int COLOR_TYPE = 2;
public static final int FLOAT_TYPE = 1;
public static final int STRING_TYPE = 0;
+ public static final int IMAGE_TYPE = 3;
public NamedVariable(int varId, int varType, String name) {
this.mVarId = varId;
@@ -53,8 +53,12 @@
@Override
public String toString() {
- return "VariableName[" + mVarId + "] = \""
- + Utils.trimString(mVarName, 10) + "\" type=" + mVarType;
+ return "VariableName["
+ + mVarId
+ + "] = \""
+ + Utils.trimString(mVarName, 10)
+ + "\" type="
+ + mVarType;
}
public static String name() {
@@ -80,7 +84,7 @@
buffer.writeUTF8(text);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(WireBuffer buffer, List<Operation> operations) {
int varId = buffer.readInt();
int varType = buffer.readInt();
String text = buffer.readUTF8(MAX_STRING_SIZE);
@@ -88,11 +92,9 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Data Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Add a string name for an ID")
- .field(INT, "varId", "id to label")
+ .field(DocumentedOperation.INT, "varId", "id to label")
.field(INT, "varType", "The type of variable")
.field(UTF8, "name", "String");
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
index ae7a892..095a010 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT_ARRAY;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -36,8 +36,7 @@
public PaintBundle mPaintData = new PaintBundle();
public static final int MAX_STRING_SIZE = 4000;
- public PaintData() {
- }
+ public PaintData() {}
@Override
public void updateVariables(RemoteContext context) {
@@ -59,7 +58,6 @@
return "PaintData " + "\"" + mPaintData + "\"";
}
-
public static String name() {
return CLASS_NAME;
}
@@ -80,13 +78,10 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Data Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Encode a Paint ")
.field(INT, "length", "id string")
- .field(INT_ARRAY, "paint", "length",
- "path encoded as floats");
+ .field(INT_ARRAY, "paint", "length", "path encoded as floats");
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
index 91352d9..13d5a49 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT_ARRAY;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,6 +24,7 @@
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.Arrays;
import java.util.List;
@@ -46,8 +47,7 @@
for (int i = 0; i < mFloatPath.length; i++) {
float v = mFloatPath[i];
if (Utils.isVariable(v)) {
- mOutputPath[i] = (Float.isNaN(v))
- ? context.getFloat(Utils.idFromNan(v)) : v;
+ mOutputPath[i] = Float.isNaN(v) ? context.getFloat(Utils.idFromNan(v)) : v;
} else {
mOutputPath[i] = v;
}
@@ -79,30 +79,15 @@
}
/**
- * public float[] getFloatPath(PaintContext context) {
- * float[] ret = mRetFloats; // Assume retFloats is declared elsewhere
- * if (ret == null) {
- * return mFloatPath; // Assume floatPath is declared elsewhere
- * }
- * float[] localRef = mRef; // Assume ref is of type Float[]
- * if (localRef == null) {
- * for (int i = 0; i < mFloatPath.length; i++) {
- * ret[i] = mFloatPath[i];
- * }
- * } else {
- * for (int i = 0; i < mFloatPath.length; i++) {
- * float lr = localRef[i];
- * if (Float.isNaN(lr)) {
- * ret[i] = Utils.getActualValue(lr);
- * } else {
- * ret[i] = mFloatPath[i];
- * }
- * }
- * }
- * return ret;
- * }
+ * public float[] getFloatPath(PaintContext context) { float[] ret = mRetFloats; // Assume
+ * retFloats is declared elsewhere if (ret == null) { return mFloatPath; // Assume floatPath is
+ * declared elsewhere } float[] localRef = mRef; // Assume ref is of type Float[] if (localRef
+ * == null) { for (int i = 0; i < mFloatPath.length; i++) { ret[i] = mFloatPath[i]; } } else {
+ * for (int i = 0; i < mFloatPath.length; i++) { float lr = localRef[i]; if (Float.isNaN(lr)) {
+ * ret[i] = Utils.getActualValue(lr); } else { ret[i] = mFloatPath[i]; } } } return ret; }
*/
public static final int MOVE = 10;
+
public static final int LINE = 11;
public static final int QUADRATIC = 12;
public static final int CONIC = 13;
@@ -145,17 +130,13 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Data Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Encode a Path ")
- .field(INT, "id", "id string")
+ .field(DocumentedOperation.INT, "id", "id string")
.field(INT, "length", "id string")
- .field(FLOAT_ARRAY, "pathData", "length",
- "path encoded as floats");
+ .field(FLOAT_ARRAY, "pathData", "length", "path encoded as floats");
}
-
public static String pathString(float[] path) {
if (path == null) {
return "null";
@@ -208,5 +189,4 @@
public void apply(RemoteContext context) {
context.loadPathData(mInstanceId, mOutputPath);
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
index 33f997f..4a8f532 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
@@ -15,22 +15,21 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteComposeOperation;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
/**
* Describe some basic information for a RemoteCompose document
- * <p>
- * It encodes the version of the document (following semantic versioning) as well
- * as the dimensions of the document in pixels.
+ *
+ * <p>It encodes the version of the document (following semantic versioning) as well as the
+ * dimensions of the document in pixels.
*/
public class RootContentBehavior implements RemoteComposeOperation {
private static final int OP_CODE = Operations.ROOT_CONTENT_BEHAVIOR;
@@ -67,8 +66,8 @@
public static final int ALIGNMENT_START = 16;
public static final int ALIGNMENT_HORIZONTAL_CENTER = 32;
public static final int ALIGNMENT_END = 64;
- public static final int ALIGNMENT_CENTER = ALIGNMENT_HORIZONTAL_CENTER
- + ALIGNMENT_VERTICAL_CENTER;
+ public static final int ALIGNMENT_CENTER =
+ ALIGNMENT_HORIZONTAL_CENTER + ALIGNMENT_VERTICAL_CENTER;
///////////////////////////////////////////////////////////////////////////////////////////////
// Layout mode
@@ -98,21 +97,14 @@
/**
* Sets the way the player handles the content
*
- * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
+ * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
* @param alignment set the alignment of the content (TOP|CENTER|BOTTOM|START|END)
- * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
- * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes
- * the LAYOUT modes are:
- * - LAYOUT_MATCH_PARENT
- * - LAYOUT_WRAP_CONTENT
- * or adding an horizontal mode and a vertical mode:
- * - LAYOUT_HORIZONTAL_MATCH_PARENT
- * - LAYOUT_HORIZONTAL_WRAP_CONTENT
- * - LAYOUT_HORIZONTAL_FIXED
- * - LAYOUT_VERTICAL_MATCH_PARENT
- * - LAYOUT_VERTICAL_WRAP_CONTENT
- * - LAYOUT_VERTICAL_FIXED
- * The LAYOUT_*_FIXED modes will use the intrinsic document size
+ * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
+ * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes the LAYOUT modes are:
+ * - LAYOUT_MATCH_PARENT - LAYOUT_WRAP_CONTENT or adding an horizontal mode and a vertical
+ * mode: - LAYOUT_HORIZONTAL_MATCH_PARENT - LAYOUT_HORIZONTAL_WRAP_CONTENT -
+ * LAYOUT_HORIZONTAL_FIXED - LAYOUT_VERTICAL_MATCH_PARENT - LAYOUT_VERTICAL_WRAP_CONTENT -
+ * LAYOUT_VERTICAL_FIXED The LAYOUT_*_FIXED modes will use the intrinsic document size
*/
public RootContentBehavior(int scroll, int alignment, int sizing, int mode) {
switch (scroll) {
@@ -121,41 +113,43 @@
case SCROLL_VERTICAL:
mScroll = scroll;
break;
- default: {
+ default:
System.out.println(TAG + "incorrect scroll value " + scroll);
- }
}
if (alignment == ALIGNMENT_CENTER) {
mAlignment = alignment;
} else {
int horizontalContentAlignment = alignment & 0xF0;
int verticalContentAlignment = alignment & 0xF;
- boolean validHorizontalAlignment = horizontalContentAlignment == ALIGNMENT_START
- || horizontalContentAlignment == ALIGNMENT_HORIZONTAL_CENTER
- || horizontalContentAlignment == ALIGNMENT_END;
- boolean validVerticalAlignment = verticalContentAlignment == ALIGNMENT_TOP
- || verticalContentAlignment == ALIGNMENT_VERTICAL_CENTER
- || verticalContentAlignment == ALIGNMENT_BOTTOM;
+ boolean validHorizontalAlignment =
+ horizontalContentAlignment == ALIGNMENT_START
+ || horizontalContentAlignment == ALIGNMENT_HORIZONTAL_CENTER
+ || horizontalContentAlignment == ALIGNMENT_END;
+ boolean validVerticalAlignment =
+ verticalContentAlignment == ALIGNMENT_TOP
+ || verticalContentAlignment == ALIGNMENT_VERTICAL_CENTER
+ || verticalContentAlignment == ALIGNMENT_BOTTOM;
if (validHorizontalAlignment && validVerticalAlignment) {
mAlignment = alignment;
} else {
- System.out.println(TAG + "incorrect alignment "
- + " h: " + horizontalContentAlignment
- + " v: " + verticalContentAlignment);
+ System.out.println(
+ TAG
+ + "incorrect alignment "
+ + " h: "
+ + horizontalContentAlignment
+ + " v: "
+ + verticalContentAlignment);
}
}
switch (sizing) {
- case SIZING_LAYOUT: {
+ case SIZING_LAYOUT:
System.out.println(TAG + "sizing_layout is not yet supported");
- }
- break;
- case SIZING_SCALE: {
+ break;
+ case SIZING_SCALE:
mSizing = sizing;
- }
- break;
- default: {
+ break;
+ default:
System.out.println(TAG + "incorrect sizing value " + sizing);
- }
}
if (mSizing == SIZING_LAYOUT) {
if (mode != NONE) {
@@ -171,9 +165,8 @@
case SCALE_FILL_BOUNDS:
mMode = mode;
break;
- default: {
+ default:
System.out.println(TAG + "incorrect mode for scale sizing, mode: " + mode);
- }
}
}
}
@@ -185,8 +178,12 @@
@Override
public String toString() {
- return "ROOT_CONTENT_BEHAVIOR scroll: " + mScroll
- + " sizing: " + mSizing + " mode: " + mMode;
+ return "ROOT_CONTENT_BEHAVIOR scroll: "
+ + mScroll
+ + " sizing: "
+ + mSizing
+ + " mode: "
+ + mMode;
}
@Override
@@ -199,12 +196,10 @@
return toString();
}
-
public static String name() {
return CLASS_NAME;
}
-
public static int id() {
return OP_CODE;
}
@@ -217,7 +212,6 @@
buffer.writeInt(mode);
}
-
public static void read(WireBuffer buffer, List<Operation> operations) {
int scroll = buffer.readInt();
int alignment = buffer.readInt();
@@ -228,30 +222,27 @@
operations.add(rootContentBehavior);
}
-
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Protocol Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Protocol Operations", OP_CODE, CLASS_NAME)
.description("Describes the behaviour of the root")
- .field(INT, "scroll", "scroll")
+ .field(DocumentedOperation.INT, "scroll", "scroll")
.possibleValues("SCROLL_HORIZONTAL", SCROLL_HORIZONTAL)
.possibleValues("SCROLL_VERTICAL", SCROLL_VERTICAL)
- .field(INT, "alignment", "alignment")
+ .field(DocumentedOperation.INT, "alignment", "alignment")
.possibleValues("ALIGNMENT_TOP", ALIGNMENT_TOP)
.possibleValues("ALIGNMENT_VERTICAL_CENTER", ALIGNMENT_VERTICAL_CENTER)
.possibleValues("ALIGNMENT_BOTTOM", ALIGNMENT_BOTTOM)
.possibleValues("ALIGNMENT_START", ALIGNMENT_START)
.possibleValues("ALIGNMENT_START", ALIGNMENT_START)
.possibleValues("ALIGNMENT_END", ALIGNMENT_END)
- .field(INT, "sizing", "sizing")
+ .field(DocumentedOperation.INT, "sizing", "sizing")
.possibleValues("SCALE_INSIDE", SCALE_INSIDE)
.possibleValues("SCALE_FIT", SCALE_FIT)
.possibleValues("SCALE_FILL_WIDTH", SCALE_FILL_WIDTH)
.possibleValues("SCALE_FILL_HEIGHT", SCALE_FILL_HEIGHT)
.possibleValues("SCALE_CROP", SCALE_CROP)
.possibleValues("SCALE_FILL_BOUNDS", SCALE_FILL_BOUNDS)
- .field(INT, "mode", "mode")
+ .field(DocumentedOperation.INT, "mode", "mode")
.possibleValues("LAYOUT_HORIZONTAL_MATCH_PARENT", LAYOUT_HORIZONTAL_MATCH_PARENT)
.possibleValues("LAYOUT_HORIZONTAL_WRAP_CONTENT", LAYOUT_HORIZONTAL_WRAP_CONTENT)
.possibleValues("LAYOUT_HORIZONTAL_FIXED", LAYOUT_HORIZONTAL_FIXED)
@@ -260,6 +251,5 @@
.possibleValues("LAYOUT_VERTICAL_FIXED", LAYOUT_VERTICAL_FIXED)
.possibleValues("LAYOUT_MATCH_PARENT", LAYOUT_MATCH_PARENT)
.possibleValues("LAYOUT_WRAP_CONTENT", LAYOUT_WRAP_CONTENT);
-
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java
index e1533ee..bff9029 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java
@@ -15,20 +15,17 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteComposeOperation;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Describe a content description for the document
- */
+/** Describe a content description for the document */
public class RootContentDescription implements RemoteComposeOperation {
private static final int OP_CODE = Operations.ROOT_CONTENT_DESCRIPTION;
private static final String CLASS_NAME = "RootContentDescription";
@@ -82,13 +79,9 @@
operations.add(header);
}
-
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Protocol Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Protocol Operations", OP_CODE, CLASS_NAME)
.description("Content description of root")
- .field(INT, "id", "id of Int");
-
+ .field(DocumentedOperation.INT, "id", "id of Int");
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java
index c4dde6e..7ec7879 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java
@@ -15,12 +15,12 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.BYTE;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT_ARRAY;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT_ARRAY;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.SHORT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.UTF8;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.BYTE;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.SHORT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.UTF8;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -28,15 +28,15 @@
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
/**
- * Operation to deal with bitmap data
- * On getting an Image during a draw call the bitmap is compressed and saved
- * in playback the image is decompressed
+ * Operation to deal with bitmap data On getting an Image during a draw call the bitmap is
+ * compressed and saved in playback the image is decompressed
*/
public class ShaderData implements Operation, VariableSupport {
private static final int OP_CODE = Operations.DATA_SHADER;
@@ -48,11 +48,12 @@
HashMap<String, int[]> mUniformIntMap = null;
HashMap<String, Integer> mUniformBitmapMap = null;
- public ShaderData(int shaderID,
- int shaderTextId,
- HashMap<String, float[]> floatMap,
- HashMap<String, int[]> intMap,
- HashMap<String, Integer> bitmapMap) {
+ public ShaderData(
+ int shaderID,
+ int shaderTextId,
+ HashMap<String, float[]> floatMap,
+ HashMap<String, int[]> intMap,
+ HashMap<String, Integer> bitmapMap) {
mShaderID = shaderID;
mShaderTextId = shaderTextId;
if (floatMap != null) {
@@ -77,7 +78,6 @@
mUniformBitmapMap.put(name, bitmapMap.get(name));
}
}
-
}
public int getShaderTextId() {
@@ -107,7 +107,7 @@
/**
* get the name of all know uniform integers
*
- * @return Name of all integer uniforms
+ * @return Name of all integer uniforms
*/
public String[] getUniformIntegerNames() {
if (mUniformIntMap == null) return new String[0];
@@ -146,8 +146,13 @@
@Override
public void write(WireBuffer buffer) {
- apply(buffer, mShaderID, mShaderTextId,
- mUniformFloatMap, mUniformIntMap, mUniformBitmapMap);
+ apply(
+ buffer,
+ mShaderID,
+ mShaderTextId,
+ mUniformFloatMap,
+ mUniformIntMap,
+ mUniformBitmapMap);
}
@Override
@@ -202,10 +207,13 @@
* @param intMap the map of int uniforms
* @param bitmapMap the map of bitmap uniforms
*/
- public static void apply(WireBuffer buffer, int shaderID, int shaderTextId,
- HashMap<String, float[]> floatMap,
- HashMap<String, int[]> intMap,
- HashMap<String, Integer> bitmapMap) {
+ public static void apply(
+ WireBuffer buffer,
+ int shaderID,
+ int shaderTextId,
+ HashMap<String, float[]> floatMap,
+ HashMap<String, int[]> intMap,
+ HashMap<String, Integer> bitmapMap) {
buffer.start(OP_CODE);
buffer.writeInt(shaderID);
@@ -248,7 +256,6 @@
}
}
-
public static void read(WireBuffer buffer, List<Operation> operations) {
int shaderID = buffer.readInt();
int shaderTextId = buffer.readInt();
@@ -298,16 +305,13 @@
bitmapMap.put(name, val);
}
}
- operations.add(new ShaderData(shaderID, shaderTextId,
- floatMap, intMap, bitmapMap));
+ operations.add(new ShaderData(shaderID, shaderTextId, floatMap, intMap, bitmapMap));
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Data Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Shader")
- .field(INT, "shaderID", "id of shader")
+ .field(DocumentedOperation.INT, "shaderID", "id of shader")
.field(BYTE, " floatSize", "number of float uniforms")
.field(BYTE, " intSize", "number of int uniform")
.field(SHORT, " intSize", "number of int uniform")
@@ -319,7 +323,6 @@
.field(INT_ARRAY, "VALUE", "int uniform (max 4)")
.field(UTF8, "bitmapName", "name of bitmap")
.field(INT, "VALUE", "id of bitmap");
-
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
index b49cb76..6383249 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
@@ -15,8 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.UTF8;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.UTF8;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,13 +23,12 @@
import com.android.internal.widget.remotecompose.core.SerializableToString;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
import java.util.List;
-/**
- * Operation to deal with Text data
- */
+/** Operation to deal with Text data */
public class TextData implements Operation, SerializableToString {
private static final int OP_CODE = Operations.DATA_TEXT;
private static final String CLASS_NAME = "TextData";
@@ -50,15 +48,13 @@
@Override
public String toString() {
- return "TextData[" + mTextId + "] = \""
- + Utils.trimString(mText, 10) + "\"";
+ return "TextData[" + mTextId + "] = \"" + Utils.trimString(mText, 10) + "\"";
}
public static String name() {
return CLASS_NAME;
}
-
public static int id() {
return OP_CODE;
}
@@ -69,7 +65,6 @@
buffer.writeUTF8(text);
}
-
public static void read(WireBuffer buffer, List<Operation> operations) {
int textId = buffer.readInt();
@@ -77,18 +72,13 @@
operations.add(new TextData(textId, text));
}
-
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Data Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Encode a string ")
- .field(INT, "id", "id string")
- .field(UTF8, "text",
- "encode text as a string");
+ .field(DocumentedOperation.INT, "id", "id string")
+ .field(UTF8, "text", "encode text as a string");
}
-
@Override
public void apply(RemoteContext context) {
context.loadText(mTextId, mText);
@@ -101,8 +91,7 @@
@Override
public void serializeToString(int indent, StringSerializer serializer) {
- serializer.append(indent, getSerializedName() + "<" + mTextId
- + "> = \"" + mText + "\"");
+ serializer.append(indent, getSerializedName() + "<" + mTextId + "> = \"" + mText + "\"");
}
private String getSerializedName() {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java
index 8f235dc..0d966d1 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.SHORT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.SHORT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,14 +24,15 @@
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringUtils;
import java.util.List;
/**
- * Operation convert floats to text
- * This command is structured [command][textID][before,after][flags]
- * before and after define number of digits before and after the decimal point
+ * Operation convert floats to text This command is structured
+ * [command][textID][before,after][flags] before and after define number of digits before and after
+ * the decimal point
*/
public class TextFromFloat implements Operation, VariableSupport {
private static final int OP_CODE = Operations.TEXT_FROM_FLOAT;
@@ -49,12 +50,12 @@
public static final int PAD_AFTER_SPACE = 0; // pad past point with space
public static final int PAD_AFTER_NONE = 1; // do not pad past last digit
public static final int PAD_AFTER_ZERO = 3; // pad with 0 past last digit
- public static final int PAD_PRE_SPACE = 0; // pad before number with spaces
- public static final int PAD_PRE_NONE = 4; // pad before number with 0s
- public static final int PAD_PRE_ZERO = 12; // do not pad before number
+ public static final int PAD_PRE_SPACE = 0; // pad before number with spaces
+ public static final int PAD_PRE_NONE = 4; // pad before number with 0s
+ public static final int PAD_PRE_ZERO = 12; // do not pad before number
- public TextFromFloat(int textId, float value, short digitsBefore,
- short digitsAfter, int flags) {
+ public TextFromFloat(
+ int textId, float value, short digitsBefore, short digitsAfter, int flags) {
this.mTextId = textId;
this.mValue = value;
this.mDigitsAfter = digitsAfter;
@@ -87,17 +88,23 @@
@Override
public void write(WireBuffer buffer) {
- apply(buffer, mTextId, mValue, mDigitsAfter, mDigitsBefore, mFlags);
+ apply(buffer, mTextId, mValue, mDigitsBefore, mDigitsAfter, mFlags);
}
@Override
public String toString() {
- return "TextFromFloat[" + mTextId + "] = "
- + Utils.floatToString(mValue) + " " + mDigitsBefore
- + "." + mDigitsAfter + " " + mFlags;
+ return "TextFromFloat["
+ + mTextId
+ + "] = "
+ + Utils.floatToString(mValue)
+ + " "
+ + mDigitsBefore
+ + "."
+ + mDigitsAfter
+ + " "
+ + mFlags;
}
-
@Override
public void updateVariables(RemoteContext context) {
if (Float.isNaN(mValue)) {
@@ -123,22 +130,25 @@
/**
* Writes out the operation to the buffer
*
- * @param buffer buffer to write to
- * @param textId the id of the output text
- * @param value the float value to be turned into strings
+ * @param buffer buffer to write to
+ * @param textId the id of the output text
+ * @param value the float value to be turned into strings
* @param digitsBefore the digits before the decimal point
- * @param digitsAfter the digits after the decimal point
- * @param flags flags that control if and how to fill the empty spots
+ * @param digitsAfter the digits after the decimal point
+ * @param flags flags that control if and how to fill the empty spots
*/
- public static void apply(WireBuffer buffer, int textId,
- float value, short digitsBefore,
- short digitsAfter, int flags) {
+ public static void apply(
+ WireBuffer buffer,
+ int textId,
+ float value,
+ short digitsBefore,
+ short digitsAfter,
+ int flags) {
buffer.start(OP_CODE);
buffer.writeInt(textId);
buffer.writeFloat(value);
buffer.writeInt((digitsBefore << 16) | digitsAfter);
buffer.writeInt(flags);
-
}
public static void read(WireBuffer buffer, List<Operation> operations) {
@@ -153,27 +163,19 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Expressions Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("Draw text along path object")
- .field(INT, "textId",
- "id of the text generated")
- .field(INT, "value",
- "Value to add")
- .field(SHORT, "prePoint",
- "digits before the decimal point")
- .field(SHORT, "pstPoint",
- "digit after the decimal point")
+ .field(DocumentedOperation.INT, "textId", "id of the text generated")
+ .field(INT, "value", "Value to add")
+ .field(SHORT, "prePoint", "digits before the decimal point")
+ .field(SHORT, "pstPoint", "digit after the decimal point")
.field(INT, "flags", "options on padding");
}
-
@Override
public void apply(RemoteContext context) {
float v = mOutValue;
- String s = StringUtils.floatToString(v, mDigitsBefore,
- mDigitsAfter, mPre, mAfter);
+ String s = StringUtils.floatToString(v, mDigitsBefore, mDigitsAfter, mPre, mAfter);
context.loadText(mTextId, s);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java
new file mode 100644
index 0000000..e148fb9
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+
+import java.util.List;
+
+/** Operation to measure the length of the text */
+public class TextLength implements Operation {
+ private static final int OP_CODE = Operations.TEXT_LENGTH;
+ private static final String CLASS_NAME = "TextLength";
+ public int mLengthId;
+ public int mTextId;
+
+ public TextLength(int lengthId, int textId) {
+ this.mLengthId = lengthId;
+ this.mTextId = textId;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ apply(buffer, mLengthId, mTextId);
+ }
+
+ @Override
+ public String toString() {
+ return CLASS_NAME + "[" + mLengthId + "] = " + mTextId;
+ }
+
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ public static int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ *
+ * @param buffer write command to this buffer
+ * @param lengthId the id to output
+ * @param textId the id of the text to measure
+ */
+ public static void apply(WireBuffer buffer, int lengthId, int textId) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(lengthId);
+ buffer.writeInt(textId);
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ int lengthId = buffer.readInt();
+ int textId = buffer.readInt();
+ operations.add(new TextLength(lengthId, textId));
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
+ .description("get the length of the text and store in float table")
+ .field(INT, "id", "id of float length")
+ .field(INT, "value", "index of text");
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ context.loadFloat(mLengthId, context.getText(mTextId).length());
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java
new file mode 100644
index 0000000..b04d698
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java
@@ -0,0 +1,127 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+
+import java.util.List;
+
+/**
+ * Operation convert floats to text This command is structured
+ * [command][textID][before,after][flags] before and after define number of digits before and after
+ * the decimal point
+ */
+public class TextLookup implements Operation, VariableSupport {
+ private static final int OP_CODE = Operations.TEXT_LOOKUP;
+ private static final String CLASS_NAME = "TextFromFloat";
+ public int mTextId;
+ public int mDataSetId;
+ public float mOutIndex, mIndex;
+
+ public static final int MAX_STRING_SIZE = 4000;
+
+ public TextLookup(int textId, int dataSetId, float index) {
+ this.mTextId = textId;
+ this.mDataSetId = dataSetId;
+ this.mOutIndex = this.mIndex = index;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ apply(buffer, mTextId, mDataSetId, mIndex);
+ }
+
+ @Override
+ public String toString() {
+ return "TextLookup["
+ + Utils.idString(mTextId)
+ + "] = "
+ + Utils.idString(mDataSetId)
+ + " "
+ + Utils.floatToString(mIndex);
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ if (Float.isNaN(mIndex)) {
+ mOutIndex = context.getFloat(Utils.idFromNan(mIndex));
+ }
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ if (Float.isNaN(mIndex)) {
+ context.listensTo(Utils.idFromNan(mIndex), this);
+ }
+ }
+
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ public static int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ *
+ * @param buffer buffer to write to
+ * @param textId the id of the output text
+ * @param dataSet float pointer to the array/list to turn int a string
+ * @param index index of element to return
+ */
+ public static void apply(WireBuffer buffer, int textId, int dataSet, float index) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(textId);
+ buffer.writeInt(dataSet);
+ buffer.writeFloat(index);
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ int textId = buffer.readInt();
+ int dataSetId = buffer.readInt();
+ float index = buffer.readFloat();
+ operations.add(new TextLookup(textId, dataSetId, index));
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
+ .description("Look an array and turn into a text object")
+ .field(INT, "textId", "id of the text generated")
+ .field(FLOAT, "dataSet", "float pointer to the array/list to turn int a string")
+ .field(FLOAT, "index", "index of element to return");
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ int id = context.getCollectionsAccess().getId(mDataSetId, (int) mOutIndex);
+ context.loadText(mTextId, context.getText(id));
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java
new file mode 100644
index 0000000..171bea2
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java
@@ -0,0 +1,120 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+
+import java.util.List;
+
+/** Operation convert int index of a list to text */
+public class TextLookupInt implements Operation, VariableSupport {
+ private static final int OP_CODE = Operations.TEXT_LOOKUP_INT;
+ private static final String CLASS_NAME = "TextFromINT";
+ public int mTextId;
+ public int mDataSetId;
+ public int mOutIndex;
+ public int mIndex;
+
+ public static final int MAX_STRING_SIZE = 4000;
+
+ public TextLookupInt(int textId, int dataSetId, int indexId) {
+ this.mTextId = textId;
+ this.mDataSetId = dataSetId;
+ this.mOutIndex = this.mIndex = indexId;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ apply(buffer, mTextId, mDataSetId, mIndex);
+ }
+
+ @Override
+ public String toString() {
+ return "TextLookupInt["
+ + Utils.idString(mTextId)
+ + "] = "
+ + Utils.idString(mDataSetId)
+ + " "
+ + mIndex;
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ mOutIndex = context.getInteger(mIndex);
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ context.listensTo(mIndex, this);
+ }
+
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ public static int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ *
+ * @param buffer buffer to write to
+ * @param textId the id of the output text
+ * @param dataSet float pointer to the array/list to turn int a string
+ * @param indexId index of element to return
+ */
+ public static void apply(WireBuffer buffer, int textId, int dataSet, int indexId) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(textId);
+ buffer.writeInt(dataSet);
+ buffer.writeInt(indexId);
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ int textId = buffer.readInt();
+ int dataSetId = buffer.readInt();
+ int indexId = buffer.readInt();
+ operations.add(new TextLookupInt(textId, dataSetId, indexId));
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
+ .description("Look up an array and turn into a text object")
+ .field(DocumentedOperation.INT, "textId", "id of the text generated")
+ .field(INT, "dataSetId", "id to the array/list to turn int a string")
+ .field(INT, "index", "index of the element to return");
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ int id = context.getCollectionsAccess().getId(mDataSetId, (int) mOutIndex);
+ context.loadText(mTextId, context.getText(id));
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java
new file mode 100644
index 0000000..0281d69
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java
@@ -0,0 +1,140 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.PaintContext.TEXT_MEASURE_FONT_HEIGHT;
+import static com.android.internal.widget.remotecompose.core.PaintContext.TEXT_MEASURE_MONOSPACE_WIDTH;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+
+import java.util.List;
+
+/** Operation to Measure Text data */
+public class TextMeasure extends PaintOperation {
+ private static final int OP_CODE = Operations.TEXT_MEASURE;
+ private static final String CLASS_NAME = "TextMeasure";
+ public int mId;
+ public int mTextId;
+ public int mType;
+
+ public static final int MEASURE_WIDTH = 0;
+ public static final int MEASURE_HEIGHT = 1;
+ public static final int MEASURE_LEFT = 2;
+ public static final int MEASURE_RIGHT = 3;
+ public static final int MEASURE_TOP = 4;
+ public static final int MEASURE_BOTTOM = 5;
+
+ /** a << 8 shifted {@link PaintContext#getTextBounds} */
+ public static final int MEASURE_MONOSPACE_FLAG = TEXT_MEASURE_MONOSPACE_WIDTH << 8;
+
+ public static final int MEASURE_MAX_HEIGHT_FLAG = TEXT_MEASURE_FONT_HEIGHT << 8;
+
+ public TextMeasure(int id, int textId, int type) {
+ this.mId = id;
+ this.mTextId = textId;
+ this.mType = type;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ apply(buffer, mId, mTextId, mType);
+ }
+
+ @Override
+ public String toString() {
+ return "FloatConstant[" + mId + "] = " + mTextId + " " + mType;
+ }
+
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ public static int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ *
+ * @param buffer write command to this buffer
+ * @param id the id
+ * @param textId the id
+ * @param type the value of the float
+ */
+ public static void apply(WireBuffer buffer, int id, int textId, int type) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(id);
+ buffer.writeInt(textId);
+ buffer.writeInt(type);
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ int id = buffer.readInt();
+ int textId = buffer.readInt();
+ int type = buffer.readInt();
+ operations.add(new TextMeasure(id, textId, type));
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
+ .description("A float and its associated id")
+ .field(INT, "id", "id of float result of the measure")
+ .field(INT, "textId", "id of text")
+ .field(INT, "type", "type: measure 0=width,1=height");
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+
+ float[] mBounds = new float[4];
+
+ @Override
+ public void paint(PaintContext context) {
+ int val = mType & 255;
+ int flags = mType >> 8;
+ context.getTextBounds(mTextId, 0, -1, flags, mBounds);
+ switch (val) {
+ case MEASURE_WIDTH:
+ context.getContext().loadFloat(mId, mBounds[2] - mBounds[0]);
+ break;
+ case MEASURE_HEIGHT:
+ context.getContext().loadFloat(mId, mBounds[3] - mBounds[1]);
+ break;
+ case MEASURE_LEFT:
+ context.getContext().loadFloat(mId, mBounds[0]);
+ break;
+ case MEASURE_TOP:
+ context.getContext().loadFloat(mId, mBounds[1]);
+ break;
+ case MEASURE_RIGHT:
+ context.getContext().loadFloat(mId, mBounds[2]);
+
+ break;
+ case MEASURE_BOTTOM:
+ context.getContext().loadFloat(mId, mBounds[3]);
+
+ break;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java
index dd78223..78cc674 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java
@@ -15,19 +15,18 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Operation to deal with Text data
- */
+/** Operation to deal with Text data */
public class TextMerge implements Operation {
private static final int OP_CODE = Operations.TEXT_MERGE;
private static final String CLASS_NAME = "TextMerge";
@@ -51,7 +50,6 @@
return "TextMerge[" + mTextId + "] = [" + mSrcId1 + " ] + [ " + mSrcId2 + "]";
}
-
public static String name() {
return CLASS_NAME;
}
@@ -84,16 +82,11 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Data Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Merge two string into one")
- .field(INT, "textId",
- "id of the text")
- .field(INT, "srcTextId1",
- "id of the path")
- .field(INT, "srcTextId1",
- "x Shift of the text");
+ .field(DocumentedOperation.INT, "textId", "id of the text")
+ .field(INT, "srcTextId1", "id of the path")
+ .field(INT, "srcTextId1", "x Shift of the text");
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java
index 52ae7fe..845f25d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -27,10 +27,9 @@
import java.util.List;
/**
- * Set a current theme, applied to the following operations in the document.
- * This can be used to "tag" the subsequent operations to a given theme. On playback,
- * we can then filter operations depending on the chosen theme.
- *
+ * Set a current theme, applied to the following operations in the document. This can be used to
+ * "tag" the subsequent operations to a given theme. On playback, we can then filter operations
+ * depending on the chosen theme.
*/
public class Theme implements RemoteComposeOperation {
private static final int OP_CODE = Operations.THEME;
@@ -43,10 +42,7 @@
/**
* we can then filter operations depending on the chosen theme.
*
- * @param theme the theme we are interested in:
- * - Theme.UNSPECIFIED
- * - Theme.DARK
- * - Theme.LIGHT
+ * @param theme the theme we are interested in: - Theme.UNSPECIFIED - Theme.DARK - Theme.LIGHT
*/
public Theme(int theme) {
this.mTheme = theme;
@@ -76,7 +72,6 @@
return CLASS_NAME;
}
-
public static int id() {
return OP_CODE;
}
@@ -86,7 +81,6 @@
buffer.writeInt(theme);
}
-
public static void read(WireBuffer buffer, List<Operation> operations) {
int theme = buffer.readInt();
operations.add(new Theme(theme));
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
index 6e858c7..8ebb40c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
@@ -15,9 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-/**
- * Utilities to be used across all core operations
- */
+/** Utilities to be used across all core operations */
public class Utils {
public static float asNan(int v) {
return Float.intBitsToFloat(v | -0x800000);
@@ -28,8 +26,16 @@
return b & 0x3FFFFF;
}
+ public static long idFromLong(long v) {
+ return v - 0x100000000L;
+ }
+
public static String idStringFromNan(float value) {
int b = Float.floatToRawIntBits(value) & 0x3FFFFF;
+ return idString(b);
+ }
+
+ public static String idString(int b) {
return (b > 0xFFFFF) ? "A_" + (b & 0xFFFFF) : "" + b;
}
@@ -38,8 +44,7 @@
}
/**
- * trim a string to n characters if needing to trim
- * end in "..."
+ * trim a string to n characters if needing to trim end in "..."
*
* @param str
* @param n
@@ -54,6 +59,7 @@
/**
* print the id and the value of a float
+ *
* @param idvalue
* @param value
* @return
@@ -70,6 +76,7 @@
/**
* Convert float to string but render nan id in brackets [n]
+ *
* @param value
* @return
*/
@@ -85,17 +92,25 @@
/**
* Debugging util to print a message and include the file/line it came from
+ *
* @param str
*/
public static void log(String str) {
StackTraceElement s = new Throwable().getStackTrace()[1];
- System.out.println("(" + s.getFileName()
- + ":" + s.getLineNumber() + "). "
- + s.getMethodName() + "() " + str);
+ System.out.println(
+ "("
+ + s.getFileName()
+ + ":"
+ + s.getLineNumber()
+ + "). "
+ + s.getMethodName()
+ + "() "
+ + str);
}
/**
* Debugging util to print the stack
+ *
* @param str
* @param n
*/
@@ -104,8 +119,8 @@
for (int i = 1; i < n + 1; i++) {
StackTraceElement s = st[i];
String space = new String(new char[i]).replace('\0', ' ');
- System.out.println(space + "(" + s.getFileName()
- + ":" + s.getLineNumber() + ")." + str);
+ System.out.println(
+ space + "(" + s.getFileName() + ":" + s.getLineNumber() + ")." + str);
}
}
@@ -136,8 +151,8 @@
}
/**
- * Interpolate two colors.
- * gamma corrected colors are interpolated in the form c1 * (1-t) + c2 * t
+ * Interpolate two colors. gamma corrected colors are interpolated in the form c1 * (1-t) + c2 *
+ * t
*
* @param c1
* @param c2
@@ -183,7 +198,6 @@
int outb = clamp((int) ((float) Math.pow(f_b, 1.0 / 2.2) * 255.0f));
int outa = clamp((int) (f_a * 255.0f));
-
return (outa << 24 | outr << 16 | outg << 8 | outb);
}
@@ -205,9 +219,9 @@
/**
* convert hue saturation and value to RGB
*
- * @param hue 0..1
+ * @param hue 0..1
* @param saturation 0..1 0=on the gray scale
- * @param value 0..1 0=black
+ * @param value 0..1 0=black
* @return
*/
public static int hsvToRgb(float hue, float saturation, float value) {
@@ -230,7 +244,6 @@
return 0XFF000000 | (t << 16) + (p << 8) + v;
case 5:
return 0XFF000000 | (v << 16) + (p << 8) + q;
-
}
return 0;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java
index 7588c794..bdc2a886 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java
@@ -20,11 +20,10 @@
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
-/**
- * Operations representing actions on the document
- */
+/** Operations representing actions on the document */
public interface ActionOperation extends Operation {
void serializeToString(int indent, StringSerializer serializer);
- void runAction(RemoteContext context, CoreDocument document,
- Component component, float x, float y);
+
+ void runAction(
+ RemoteContext context, CoreDocument document, Component component, float x, float y);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java
index fd35017..9d80d3c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,13 +24,17 @@
import java.util.List;
-/**
- * Represents the content of a CanvasLayout (i.e. contains the canvas commands)
- */
+/** Represents the content of a CanvasLayout (i.e. contains the canvas commands) */
public class CanvasContent extends Component implements ComponentStartOperation {
- public CanvasContent(int componentId, float x, float y,
- float width, float height, Component parent, int animationId) {
+ public CanvasContent(
+ int componentId,
+ float x,
+ float y,
+ float width,
+ float height,
+ Component parent,
+ int animationId) {
super(parent, componentId, animationId, x, y, width, height);
}
@@ -42,7 +46,8 @@
return Operations.LAYOUT_CANVAS_CONTENT;
}
- @Override protected String getSerializedName() {
+ @Override
+ protected String getSerializedName() {
return "CANVAS_CONTENT";
}
@@ -53,8 +58,7 @@
public static void read(WireBuffer buffer, List<Operation> operations) {
int componentId = buffer.readInt();
- operations.add(new CanvasContent(
- componentId, 0, 0, 0, 0, null, -1));
+ operations.add(new CanvasContent(componentId, 0, 0, 0, 0, null, -1));
}
public static void documentation(DocumentationBuilder doc) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierEnd.java
index f7c6ce2..fe726ac 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierEnd.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierEnd.java
@@ -57,14 +57,14 @@
buffer.start(id());
}
-
public static void read(WireBuffer buffer, List<Operation> operations) {
operations.add(new ClickModifierEnd());
}
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
- .description("End tag for click modifiers. This operation marks the end"
- + "of a click modifier");
+ .description(
+ "End tag for click modifiers. This operation marks the end"
+ + "of a click modifier");
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java
index d75f70b..d5ff07d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java
@@ -35,14 +35,11 @@
import java.util.ArrayList;
import java.util.List;
-/**
- * Represents a click modifier + actions
- */
+/** Represents a click modifier + actions */
public class ClickModifierOperation extends PaintOperation
implements ModifierOperation, DecoratorComponent {
private static final int OP_CODE = Operations.MODIFIER_CLICK;
-
long mAnimateRippleStart = 0;
float mAnimateRippleX = 0f;
float mAnimateRippleY = 0f;
@@ -60,6 +57,7 @@
mAnimateRippleX = x;
mAnimateRippleY = y;
}
+
public ArrayList<Operation> mList = new ArrayList<>();
public ArrayList<Operation> getList() {
@@ -107,14 +105,14 @@
context.savePaint();
mPaint.reset();
- FloatAnimation anim1 = new FloatAnimation(Easing.CUBIC_STANDARD, 1f,
- null, Float.NaN, Float.NaN);
+ FloatAnimation anim1 =
+ new FloatAnimation(Easing.CUBIC_STANDARD, 1f, null, Float.NaN, Float.NaN);
anim1.setInitialValue(0f);
anim1.setTargetValue(1f);
float tween = anim1.get(progress);
- FloatAnimation anim2 = new FloatAnimation(Easing.CUBIC_STANDARD, 0.5f,
- null, Float.NaN, Float.NaN);
+ FloatAnimation anim2 =
+ new FloatAnimation(Easing.CUBIC_STANDARD, 0.5f, null, Float.NaN, Float.NaN);
anim2.setInitialValue(0f);
anim2.setTargetValue(1f);
float tweenRadius = anim2.get(progress);
@@ -149,8 +147,11 @@
}
@Override
- public void onClick(RemoteContext context, CoreDocument document,
- Component component, float x, float y) {
+ public void onClick(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
+ if (!component.isVisible()) {
+ return;
+ }
locationInWindow[0] = 0f;
locationInWindow[1] = 0f;
component.getLocationInWindow(locationInWindow);
@@ -176,7 +177,8 @@
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", OP_CODE, name())
- .description("Click modifier. This operation contains"
- + " a list of action executed on click");
+ .description(
+ "Click modifier. This operation contains"
+ + " a list of action executed on click");
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
index fca0b13..96dffca 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
@@ -38,9 +38,7 @@
import java.util.ArrayList;
import java.util.HashSet;
-/**
- * Generic Component class
- */
+/** Generic Component class */
public class Component extends PaintOperation implements Measurable, SerializableToString {
private static final boolean DEBUG = false;
@@ -64,7 +62,6 @@
PaintBundle mPaint = new PaintBundle();
protected HashSet<ComponentValue> mComponentValues = new HashSet<>();
-
public ArrayList<Operation> getList() {
return mList;
}
@@ -120,27 +117,27 @@
*/
private void updateComponentValues(RemoteContext context) {
if (DEBUG) {
- System.out.println("UPDATE COMPONENT VALUES ("
- + mComponentValues.size()
- + ") FOR " + mComponentId);
+ System.out.println(
+ "UPDATE COMPONENT VALUES ("
+ + mComponentValues.size()
+ + ") FOR "
+ + mComponentId);
}
for (ComponentValue v : mComponentValues) {
switch (v.getType()) {
- case ComponentValue.WIDTH: {
+ case ComponentValue.WIDTH:
context.loadFloat(v.getValueId(), mWidth);
if (DEBUG) {
System.out.println("Updating WIDTH for " + mComponentId + " to " + mWidth);
}
- }
- break;
- case ComponentValue.HEIGHT: {
+ break;
+ case ComponentValue.HEIGHT:
context.loadFloat(v.getValueId(), mHeight);
if (DEBUG) {
- System.out.println("Updating HEIGHT for " + mComponentId
- + " to " + mHeight);
+ System.out.println(
+ "Updating HEIGHT for " + mComponentId + " to " + mHeight);
}
- }
- break;
+ break;
}
}
}
@@ -153,8 +150,14 @@
mAnimationId = id;
}
- public Component(Component parent, int componentId, int animationId,
- float x, float y, float width, float height) {
+ public Component(
+ Component parent,
+ int componentId,
+ int animationId,
+ float x,
+ float y,
+ float width,
+ float height) {
this.mComponentId = componentId;
this.mX = x;
this.mY = y;
@@ -164,15 +167,20 @@
this.mAnimationId = animationId;
}
- public Component(int componentId, float x, float y, float width, float height,
- Component parent) {
+ public Component(
+ int componentId, float x, float y, float width, float height, Component parent) {
this(parent, componentId, -1, x, y, width, height);
}
public Component(Component component) {
- this(component.mParent, component.mComponentId, component.mAnimationId,
- component.mX, component.mY, component.mWidth, component.mHeight
- );
+ this(
+ component.mParent,
+ component.mComponentId,
+ component.mAnimationId,
+ component.mX,
+ component.mY,
+ component.mWidth,
+ component.mHeight);
mList.addAll(component.mList);
finalizeCreation();
}
@@ -199,8 +207,8 @@
}
/**
- * This traverses the component tree and make sure to
- * update variables referencing the component dimensions as needed.
+ * This traverses the component tree and make sure to update variables referencing the component
+ * dimensions as needed.
*
* @param context the current context
*/
@@ -223,9 +231,9 @@
}
public enum Visibility {
+ GONE,
VISIBLE,
- INVISIBLE,
- GONE
+ INVISIBLE
}
public boolean isVisible() {
@@ -239,15 +247,43 @@
}
public void setVisibility(Visibility visibility) {
- if (visibility != mVisibility) {
+ if (visibility != mVisibility || visibility != mScheduledVisibility) {
mScheduledVisibility = visibility;
invalidateMeasure();
}
}
@Override
- public void measure(PaintContext context, float minWidth, float maxWidth,
- float minHeight, float maxHeight, MeasurePass measure) {
+ public boolean suitableForTransition(Operation o) {
+ if (!(o instanceof Component)) {
+ return false;
+ }
+ if (mList.size() != ((Component) o).mList.size()) {
+ return false;
+ }
+ for (int i = 0; i < mList.size(); i++) {
+ Operation o1 = mList.get(i);
+ Operation o2 = ((Component) o).mList.get(i);
+ if (o1 instanceof Component && o2 instanceof Component) {
+ if (!((Component) o1).suitableForTransition(o2)) {
+ return false;
+ }
+ }
+ if (o1 instanceof PaintOperation && !((PaintOperation) o1).suitableForTransition(o2)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void measure(
+ PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ MeasurePass measure) {
ComponentMeasure m = measure.get(this);
m.setW(mWidth);
m.setH(mHeight);
@@ -256,19 +292,34 @@
@Override
public void layout(RemoteContext context, MeasurePass measure) {
ComponentMeasure m = measure.get(this);
- if (!mFirstLayout && context.isAnimationEnabled()
+ if (!mFirstLayout
+ && context.isAnimationEnabled()
&& !(this instanceof LayoutComponentContent)) {
if (mAnimateMeasure == null) {
- ComponentMeasure origin = new ComponentMeasure(mComponentId,
- mX, mY, mWidth, mHeight, mVisibility);
- ComponentMeasure target = new ComponentMeasure(mComponentId,
- m.getX(), m.getY(), m.getW(), m.getH(), m.getVisibility());
- mAnimateMeasure = new AnimateMeasure(context.currentTime, this,
- origin, target,
- mAnimationSpec.getMotionDuration(), mAnimationSpec.getVisibilityDuration(),
- mAnimationSpec.getEnterAnimation(), mAnimationSpec.getExitAnimation(),
- mAnimationSpec.getMotionEasingType(),
- mAnimationSpec.getVisibilityEasingType());
+ ComponentMeasure origin =
+ new ComponentMeasure(mComponentId, mX, mY, mWidth, mHeight, mVisibility);
+ ComponentMeasure target =
+ new ComponentMeasure(
+ mComponentId,
+ m.getX(),
+ m.getY(),
+ m.getW(),
+ m.getH(),
+ m.getVisibility());
+ if (!target.same(origin)) {
+ mAnimateMeasure =
+ new AnimateMeasure(
+ context.currentTime,
+ this,
+ origin,
+ target,
+ mAnimationSpec.getMotionDuration(),
+ mAnimationSpec.getVisibilityDuration(),
+ mAnimationSpec.getEnterAnimation(),
+ mAnimationSpec.getExitAnimation(),
+ mAnimationSpec.getMotionEasingType(),
+ mAnimationSpec.getVisibilityEasingType());
+ }
} else {
mAnimateMeasure.updateTarget(m, context.currentTime);
}
@@ -323,19 +374,48 @@
@Override
public String toString() {
- return "COMPONENT(<" + mComponentId + "> " + getClass().getSimpleName()
- + ") [" + mX + "," + mY + " - " + mWidth + " x " + mHeight + "] " + textContent()
- + " Visibility (" + mVisibility + ") ";
+ return "COMPONENT(<"
+ + mComponentId
+ + "> "
+ + getClass().getSimpleName()
+ + ") ["
+ + mX
+ + ","
+ + mY
+ + " - "
+ + mWidth
+ + " x "
+ + mHeight
+ + "] "
+ + textContent()
+ + " Visibility ("
+ + mVisibility
+ + ") ";
}
protected String getSerializedName() {
return "COMPONENT";
}
+ @Override
public void serializeToString(int indent, StringSerializer serializer) {
- serializer.append(indent, getSerializedName() + " [" + mComponentId
- + ":" + mAnimationId + "] = "
- + "[" + mX + ", " + mY + ", " + mWidth + ", " + mHeight + "] "
+ serializer.append(
+ indent,
+ getSerializedName()
+ + " ["
+ + mComponentId
+ + ":"
+ + mAnimationId
+ + "] = "
+ + "["
+ + mX
+ + ", "
+ + mY
+ + ", "
+ + mWidth
+ + ", "
+ + mHeight
+ + "] "
+ mVisibility
// + " [" + mNeedsMeasure + ", " + mNeedsRepaint + "]"
);
@@ -346,9 +426,7 @@
// nothing
}
- /**
- * Returns the top-level RootLayoutComponent
- */
+ /** Returns the top-level RootLayoutComponent */
public RootLayoutComponent getRoot() throws Exception {
if (this instanceof RootLayoutComponent) {
return (RootLayoutComponent) this;
@@ -378,8 +456,8 @@
}
/**
- * Mark itself as needing to be remeasured, and walk back up the tree
- * to mark each parents as well.
+ * Mark itself as needing to be remeasured, and walk back up the tree to mark each parents as
+ * well.
*/
public void invalidateMeasure() {
needsRepaint();
@@ -411,7 +489,7 @@
public String textContent() {
StringBuilder builder = new StringBuilder();
- for (Operation op : mList) {
+ for (Operation ignored : mList) {
String letter = "";
// if (op instanceof DrawTextRun) {
// letter = "[" + ((DrawTextRun) op).text + "]";
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java
index 71decd7..c83ee487 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java
@@ -61,14 +61,14 @@
return 1 + 4 + 4 + 4;
}
-
public static void read(WireBuffer buffer, List<Operation> operations) {
operations.add(new ComponentEnd());
}
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
- .description("End tag for components / layouts. This operation marks the end"
- + "of a component");
+ .description(
+ "End tag for components / layouts. This operation marks the end"
+ + "of a component");
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java
index 32ef5ce..72cc9b6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -75,8 +75,19 @@
@Override
public String toString() {
- return "COMPONENT_START (type " + mType + " " + typeDescription(mType)
- + ") - (" + mX + ", " + mY + " - " + mWidth + " x " + mHeight + ")";
+ return "COMPONENT_START (type "
+ + mType
+ + " "
+ + typeDescription(mType)
+ + ") - ("
+ + mX
+ + ", "
+ + mY
+ + " - "
+ + mWidth
+ + " x "
+ + mHeight
+ + ")";
}
@Override
@@ -149,8 +160,8 @@
return Operations.COMPONENT_START;
}
- public static void apply(WireBuffer buffer, int type, int componentId,
- float width, float height) {
+ public static void apply(
+ WireBuffer buffer, int type, int componentId, float width, float height) {
buffer.start(Operations.COMPONENT_START);
buffer.writeInt(type);
buffer.writeInt(componentId);
@@ -172,8 +183,8 @@
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
- .description("Basic component encapsulating draw commands."
- + "This is not resizable.")
+ .description(
+ "Basic component encapsulating draw commands." + "This is not resizable.")
.field(INT, "TYPE", "Type of components")
.field(INT, "COMPONENT_ID", "unique id for this component")
.field(FLOAT, "WIDTH", "width of the component")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStartOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStartOperation.java
index 67964ef..abf2356a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStartOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStartOperation.java
@@ -17,5 +17,4 @@
import com.android.internal.widget.remotecompose.core.Operation;
-public interface ComponentStartOperation extends Operation {
-}
+public interface ComponentStartOperation extends Operation {}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/DecoratorComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/DecoratorComponent.java
index 71bf839..314650f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/DecoratorComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/DecoratorComponent.java
@@ -24,6 +24,7 @@
*/
public interface DecoratorComponent {
void layout(RemoteContext context, float width, float height);
- void onClick(RemoteContext context, CoreDocument document,
- Component component, float x, float y);
+
+ void onClick(
+ RemoteContext context, CoreDocument document, Component component, float x, float y);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
index f4c2131..8172502 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
@@ -32,9 +32,7 @@
import java.util.ArrayList;
-/**
- * Component with modifiers and children
- */
+/** Component with modifiers and children */
public class LayoutComponent extends Component {
protected WidthModifierOperation mWidthModifier = null;
@@ -54,8 +52,14 @@
protected ComponentModifiers mComponentModifiers = new ComponentModifiers();
protected ArrayList<Component> mChildrenComponents = new ArrayList<>();
- public LayoutComponent(Component parent, int componentId, int animationId,
- float x, float y, float width, float height) {
+ public LayoutComponent(
+ Component parent,
+ int componentId,
+ int animationId,
+ float x,
+ float y,
+ float width,
+ float height) {
super(parent, componentId, animationId, x, y, width, height);
}
@@ -91,7 +95,6 @@
return mPaddingBottom;
}
-
public WidthModifierOperation getWidthModifier() {
return mWidthModifier;
}
@@ -237,10 +240,7 @@
context.restore();
}
-
- /**
- * Traverse the modifiers to compute indicated dimension
- */
+ /** Traverse the modifiers to compute indicated dimension */
public float computeModifierDefinedWidth() {
float s = 0f;
float e = 0f;
@@ -283,9 +283,7 @@
return s + e;
}
- /**
- * Traverse the modifiers to compute indicated dimension
- */
+ /** Traverse the modifiers to compute indicated dimension */
public float computeModifierDefinedHeight() {
float t = 0f;
float b = 0f;
@@ -328,4 +326,7 @@
return t + b;
}
+ public ArrayList<Component> getChildrenComponents() {
+ return mChildrenComponents;
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java
index 5b3b54d..66fd053 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,13 +24,17 @@
import java.util.List;
-/**
- * Represents the content of a LayoutComponent (i.e. the children components)
- */
+/** Represents the content of a LayoutComponent (i.e. the children components) */
public class LayoutComponentContent extends Component implements ComponentStartOperation {
- public LayoutComponentContent(int componentId, float x, float y,
- float width, float height, Component parent, int animationId) {
+ public LayoutComponentContent(
+ int componentId,
+ float x,
+ float y,
+ float width,
+ float height,
+ Component parent,
+ int animationId) {
super(parent, componentId, animationId, x, y, width, height);
}
@@ -42,7 +46,8 @@
return Operations.LAYOUT_CONTENT;
}
- @Override protected String getSerializedName() {
+ @Override
+ protected String getSerializedName() {
return "CONTENT";
}
@@ -53,16 +58,16 @@
public static void read(WireBuffer buffer, List<Operation> operations) {
int componentId = buffer.readInt();
- operations.add(new LayoutComponentContent(
- componentId, 0, 0, 0, 0, null, -1));
+ operations.add(new LayoutComponentContent(componentId, 0, 0, 0, 0, null, -1));
}
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
.field(INT, "COMPONENT_ID", "unique id for this component")
- .description("Container for components. BoxLayout, RowLayout and ColumnLayout "
- + "expects a LayoutComponentContent as a child, encapsulating the "
- + "components that needs to be laid out.");
+ .description(
+ "Container for components. BoxLayout, RowLayout and ColumnLayout "
+ + "expects a LayoutComponentContent as a child, encapsulating the "
+ + "components that needs to be laid out.");
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java
new file mode 100644
index 0000000..3086d6a
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+
+import java.util.List;
+
+public class LoopEnd implements Operation {
+
+ @Override
+ public void write(WireBuffer buffer) {
+ apply(buffer);
+ }
+
+ @Override
+ public String toString() {
+ return "LOOP_END";
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ // nothing
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return (indent != null ? indent : "") + toString();
+ }
+
+ public static String name() {
+ return "LoopEnd";
+ }
+
+ public static int id() {
+ return Operations.LOOP_END;
+ }
+
+ public static void apply(WireBuffer buffer) {
+ buffer.start(id());
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ operations.add(new LoopEnd());
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Operations", id(), name()).description("End tag for loops");
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java
new file mode 100644
index 0000000..6910008
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java
@@ -0,0 +1,116 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations.layout;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Represents a loop of operations */
+public class LoopOperation extends PaintOperation {
+ private static final int OP_CODE = Operations.LOOP_START;
+
+ public ArrayList<Operation> mList = new ArrayList<>();
+
+ int mIndexVariableId;
+ float mUntil = 12;
+ float mFrom = 0;
+ float mStep = 1;
+
+ public LoopOperation(int count, int indexId) {
+ mUntil = count;
+ mIndexVariableId = indexId;
+ }
+
+ public LoopOperation(float count, float from, float step, int indexId) {
+ mUntil = count;
+ mFrom = from;
+ mStep = step;
+ mIndexVariableId = indexId;
+ }
+
+ public ArrayList<Operation> getList() {
+ return mList;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ apply(buffer, mUntil, mFrom, mStep, mIndexVariableId);
+ }
+
+ @Override
+ public String toString() {
+ return "LoopOperation";
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return (indent != null ? indent : "") + toString();
+ }
+
+ @Override
+ public void paint(PaintContext context) {
+ if (mIndexVariableId == 0) {
+ for (float i = mFrom; i < mUntil; i += mStep) {
+ for (Operation op : mList) {
+ op.apply(context.getContext());
+ }
+ }
+ } else {
+ for (float i = mFrom; i < mUntil; i += mStep) {
+ context.getContext().loadFloat(mIndexVariableId, i);
+ for (Operation op : mList) {
+ if (op instanceof VariableSupport) {
+ ((VariableSupport) op).updateVariables(context.getContext());
+ }
+ op.apply(context.getContext());
+ }
+ }
+ }
+ }
+
+ public static String name() {
+ return "Loop";
+ }
+
+ public static void apply(WireBuffer buffer, float count, float from, float step, int indexId) {
+ buffer.start(OP_CODE);
+ buffer.writeFloat(count);
+ buffer.writeFloat(from);
+ buffer.writeFloat(step);
+ buffer.writeInt(indexId);
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ float count = buffer.readFloat();
+ float from = buffer.readFloat();
+ float step = buffer.readFloat();
+ int indexId = buffer.readInt();
+ operations.add(new LoopOperation(count, from, step, indexId));
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Operations", OP_CODE, name())
+ .description("Loop. This operation execute" + " a list of action in a loop");
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java
index bf1a496..680bb0b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -32,38 +32,66 @@
import java.util.List;
-/**
- * Represents the root layout component. Entry point to the component tree layout/paint.
- */
+/** Represents the root layout component. Entry point to the component tree layout/paint. */
public class RootLayoutComponent extends Component implements ComponentStartOperation {
int mCurrentId = -1;
- public RootLayoutComponent(int componentId, float x, float y,
- float width, float height, Component parent, int animationId) {
+ public RootLayoutComponent(
+ int componentId,
+ float x,
+ float y,
+ float width,
+ float height,
+ Component parent,
+ int animationId) {
super(parent, componentId, animationId, x, y, width, height);
}
- public RootLayoutComponent(int componentId, float x, float y,
- float width, float height, Component parent) {
+ public RootLayoutComponent(
+ int componentId, float x, float y, float width, float height, Component parent) {
super(parent, componentId, -1, x, y, width, height);
}
@Override
public String toString() {
- return "ROOT " + mComponentId + " (" + mX + ", " + mY + " - "
- + mWidth + " x " + mHeight + ") " + mVisibility;
+ return "ROOT "
+ + mComponentId
+ + " ("
+ + mX
+ + ", "
+ + mY
+ + " - "
+ + mWidth
+ + " x "
+ + mHeight
+ + ") "
+ + mVisibility;
}
@Override
public void serializeToString(int indent, StringSerializer serializer) {
- serializer.append(indent, "ROOT [" + mComponentId + ":" + mAnimationId
- + "] = [" + mX + ", " + mY + ", " + mWidth + ", " + mHeight + "] " + mVisibility);
+ serializer.append(
+ indent,
+ "ROOT ["
+ + mComponentId
+ + ":"
+ + mAnimationId
+ + "] = ["
+ + mX
+ + ", "
+ + mY
+ + ", "
+ + mWidth
+ + ", "
+ + mHeight
+ + "] "
+ + mVisibility);
}
/**
- * Traverse the hierarchy and assign generated ids to component without ids.
- * Most components would already have ids assigned during the document creation, but this
- * allow us to take care of any components added during the inflation.
+ * Traverse the hierarchy and assign generated ids to component without ids. Most components
+ * would already have ids assigned during the document creation, but this allow us to take care
+ * of any components added during the inflation.
*
* @param lastId the last known generated id
*/
@@ -84,9 +112,7 @@
}
}
- /**
- * This will measure then layout the tree of components
- */
+ /** This will measure then layout the tree of components */
public void layout(RemoteContext context) {
if (!mNeedsMeasure) {
return;
@@ -100,8 +126,7 @@
for (Operation op : mList) {
if (op instanceof Measurable) {
Measurable m = (Measurable) op;
- m.measure(context.getPaintContext(),
- 0f, mWidth, 0f, mHeight, measurePass);
+ m.measure(context.getPaintContext(), 0f, mWidth, 0f, mHeight, measurePass);
m.layout(context, measurePass);
}
}
@@ -161,15 +186,16 @@
public static void read(WireBuffer buffer, List<Operation> operations) {
int componentId = buffer.readInt();
- operations.add(new RootLayoutComponent(
- componentId, 0, 0, 0, 0, null, -1));
+ operations.add(new RootLayoutComponent(componentId, 0, 0, 0, 0, null, -1));
}
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
.field(INT, "COMPONENT_ID", "unique id for this component")
- .description("Root element for a document. Other components / layout managers "
- + "are children in the component tree starting from this Root component.");
+ .description(
+ "Root element for a document. Other components / layout managers are"
+ + " children in the component tree starting from"
+ + "this Root component.");
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
index 1ada733..e450585 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
@@ -28,7 +28,7 @@
/**
* Basic interpolation manager between two ComponentMeasures
*
- * Handles position, size and visibility
+ * <p>Handles position, size and visibility
*/
public class AnimateMeasure {
long mStartTime = System.currentTimeMillis();
@@ -44,18 +44,24 @@
float mP = 0f;
float mVp = 0f;
- FloatAnimation mMotionEasing = new FloatAnimation(mMotionEasingType,
- mDuration / 1000f, null, 0f, Float.NaN);
- FloatAnimation mVisibilityEasing = new FloatAnimation(mVisibilityEasingType,
- mDurationVisibilityChange / 1000f,
- null, 0f, Float.NaN);
+ FloatAnimation mMotionEasing =
+ new FloatAnimation(mMotionEasingType, mDuration / 1000f, null, 0f, Float.NaN);
+ FloatAnimation mVisibilityEasing =
+ new FloatAnimation(
+ mVisibilityEasingType, mDurationVisibilityChange / 1000f, null, 0f, Float.NaN);
ParticleAnimation mParticleAnimation;
- public AnimateMeasure(long startTime, Component component, ComponentMeasure original,
- ComponentMeasure target, int duration, int durationVisibilityChange,
- AnimationSpec.ANIMATION enterAnimation,
- AnimationSpec.ANIMATION exitAnimation,
- int motionEasingType, int visibilityEasingType) {
+ public AnimateMeasure(
+ long startTime,
+ Component component,
+ ComponentMeasure original,
+ ComponentMeasure target,
+ int duration,
+ int durationVisibilityChange,
+ AnimationSpec.ANIMATION enterAnimation,
+ AnimationSpec.ANIMATION exitAnimation,
+ int motionEasingType,
+ int visibilityEasingType) {
this.mStartTime = startTime;
this.mComponent = component;
this.mOriginal = original;
@@ -64,18 +70,28 @@
this.mDurationVisibilityChange = durationVisibilityChange;
this.mEnterAnimation = enterAnimation;
this.mExitAnimation = exitAnimation;
+ this.mMotionEasingType = motionEasingType;
+ this.mVisibilityEasingType = visibilityEasingType;
+
+ float motionDuration = mDuration / 1000f;
+ float visibilityDuration = mDurationVisibilityChange / 1000f;
+
+ mMotionEasing = new FloatAnimation(mMotionEasingType, motionDuration, null, 0f, Float.NaN);
+ mVisibilityEasing =
+ new FloatAnimation(mVisibilityEasingType, visibilityDuration, null, 0f, Float.NaN);
mMotionEasing.setTargetValue(1f);
mVisibilityEasing.setTargetValue(1f);
+
component.mVisibility = target.getVisibility();
}
public void update(long currentTime) {
long elapsed = currentTime - mStartTime;
- mP = Math.min(elapsed / (float) mDuration, 1f);
- //mP = motionEasing.get(mP);
- mVp = Math.min(elapsed / (float) mDurationVisibilityChange, 1f);
- // mVp = mVisibilityEasing.get(mVp);
+ float motionProgress = elapsed / (float) mDuration;
+ float visibilityProgress = elapsed / (float) mDurationVisibilityChange;
+ mP = mMotionEasing.get(motionProgress);
+ mVp = mVisibilityEasing.get(visibilityProgress);
}
public PaintBundle paint = new PaintBundle();
@@ -117,8 +133,11 @@
paint.reset();
paint.setColor(0f, 0f, 0f, 1f - mVp);
context.applyPaint(paint);
- context.saveLayer(mComponent.getX(), mComponent.getY(),
- mComponent.getWidth(), mComponent.getHeight());
+ context.saveLayer(
+ mComponent.getX(),
+ mComponent.getY(),
+ mComponent.getWidth(),
+ mComponent.getHeight());
mComponent.paintingComponent(context);
context.restore();
context.restorePaint();
@@ -127,8 +146,11 @@
case SLIDE_LEFT:
context.save();
context.translate(-mVp * mComponent.getParent().getWidth(), 0f);
- context.saveLayer(mComponent.getX(), mComponent.getY(),
- mComponent.getWidth(), mComponent.getHeight());
+ context.saveLayer(
+ mComponent.getX(),
+ mComponent.getY(),
+ mComponent.getWidth(),
+ mComponent.getHeight());
mComponent.paintingComponent(context);
context.restore();
context.restore();
@@ -140,8 +162,11 @@
paint.setColor(0f, 0f, 0f, 1f);
context.applyPaint(paint);
context.translate(mVp * mComponent.getParent().getWidth(), 0f);
- context.saveLayer(mComponent.getX(), mComponent.getY(),
- mComponent.getWidth(), mComponent.getHeight());
+ context.saveLayer(
+ mComponent.getX(),
+ mComponent.getY(),
+ mComponent.getWidth(),
+ mComponent.getHeight());
mComponent.paintingComponent(context);
context.restore();
context.restorePaint();
@@ -149,20 +174,24 @@
break;
case SLIDE_TOP:
context.save();
- context.translate(0f,
- -mVp * mComponent.getParent().getHeight());
- context.saveLayer(mComponent.getX(), mComponent.getY(),
- mComponent.getWidth(), mComponent.getHeight());
+ context.translate(0f, -mVp * mComponent.getParent().getHeight());
+ context.saveLayer(
+ mComponent.getX(),
+ mComponent.getY(),
+ mComponent.getWidth(),
+ mComponent.getHeight());
mComponent.paintingComponent(context);
context.restore();
context.restore();
break;
case SLIDE_BOTTOM:
context.save();
- context.translate(0f,
- mVp * mComponent.getParent().getHeight());
- context.saveLayer(mComponent.getX(), mComponent.getY(),
- mComponent.getWidth(), mComponent.getHeight());
+ context.translate(0f, mVp * mComponent.getParent().getHeight());
+ context.saveLayer(
+ mComponent.getX(),
+ mComponent.getY(),
+ mComponent.getWidth(),
+ mComponent.getHeight());
mComponent.paintingComponent(context);
context.restore();
context.restore();
@@ -189,8 +218,11 @@
paint.reset();
paint.setColor(0f, 0f, 0f, mVp);
context.applyPaint(paint);
- context.saveLayer(mComponent.getX(), mComponent.getY(),
- mComponent.getWidth(), mComponent.getHeight());
+ context.saveLayer(
+ mComponent.getX(),
+ mComponent.getY(),
+ mComponent.getWidth(),
+ mComponent.getHeight());
mComponent.paintingComponent(context);
context.restore();
context.restorePaint();
@@ -202,8 +234,11 @@
paint.reset();
paint.setColor(0f, 0f, 0f, mVp);
context.applyPaint(paint);
- context.saveLayer(mComponent.getX(), mComponent.getY(),
- mComponent.getWidth(), mComponent.getHeight());
+ context.saveLayer(
+ mComponent.getX(),
+ mComponent.getY(),
+ mComponent.getWidth(),
+ mComponent.getHeight());
mComponent.paintingComponent(context);
context.restore();
context.restorePaint();
@@ -211,40 +246,48 @@
break;
case SLIDE_LEFT:
context.save();
- context.translate(
- (1f - mVp) * mComponent.getParent().getWidth(), 0f);
- context.saveLayer(mComponent.getX(), mComponent.getY(),
- mComponent.getWidth(), mComponent.getHeight());
+ context.translate((1f - mVp) * mComponent.getParent().getWidth(), 0f);
+ context.saveLayer(
+ mComponent.getX(),
+ mComponent.getY(),
+ mComponent.getWidth(),
+ mComponent.getHeight());
mComponent.paintingComponent(context);
context.restore();
context.restore();
break;
case SLIDE_RIGHT:
context.save();
- context.translate(
- -(1f - mVp) * mComponent.getParent().getWidth(), 0f);
- context.saveLayer(mComponent.getX(), mComponent.getY(),
- mComponent.getWidth(), mComponent.getHeight());
+ context.translate(-(1f - mVp) * mComponent.getParent().getWidth(), 0f);
+ context.saveLayer(
+ mComponent.getX(),
+ mComponent.getY(),
+ mComponent.getWidth(),
+ mComponent.getHeight());
mComponent.paintingComponent(context);
context.restore();
context.restore();
break;
case SLIDE_TOP:
context.save();
- context.translate(0f,
- (1f - mVp) * mComponent.getParent().getHeight());
- context.saveLayer(mComponent.getX(), mComponent.getY(),
- mComponent.getWidth(), mComponent.getHeight());
+ context.translate(0f, (1f - mVp) * mComponent.getParent().getHeight());
+ context.saveLayer(
+ mComponent.getX(),
+ mComponent.getY(),
+ mComponent.getWidth(),
+ mComponent.getHeight());
mComponent.paintingComponent(context);
context.restore();
context.restore();
break;
case SLIDE_BOTTOM:
context.save();
- context.translate(0f,
- -(1f - mVp) * mComponent.getParent().getHeight());
- context.saveLayer(mComponent.getX(), mComponent.getY(),
- mComponent.getWidth(), mComponent.getHeight());
+ context.translate(0f, -(1f - mVp) * mComponent.getParent().getHeight());
+ context.saveLayer(
+ mComponent.getX(),
+ mComponent.getY(),
+ mComponent.getWidth(),
+ mComponent.getHeight());
mComponent.paintingComponent(context);
context.restore();
context.restore();
@@ -300,11 +343,22 @@
mOriginal.setY(getY());
mOriginal.setW(getWidth());
mOriginal.setH(getHeight());
- mTarget.setX(measure.getX());
- mTarget.setY(measure.getY());
- mTarget.setW(measure.getW());
- mTarget.setH(measure.getH());
- mTarget.setVisibility(measure.getVisibility());
- mStartTime = currentTime;
+ float targetX = mTarget.getX();
+ float targetY = mTarget.getY();
+ float targetW = mTarget.getW();
+ float targetH = mTarget.getH();
+ Component.Visibility targetVisibility = mTarget.getVisibility();
+ if (targetX != measure.getX()
+ || targetY != measure.getY()
+ || targetW != measure.getW()
+ || targetH != measure.getH()
+ || targetVisibility != measure.getVisibility()) {
+ mTarget.setX(measure.getX());
+ mTarget.setY(measure.getY());
+ mTarget.setW(measure.getW());
+ mTarget.setH(measure.getH());
+ mTarget.setVisibility(measure.getVisibility());
+ mStartTime = currentTime;
+ }
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java
index 0f7db36..35533cb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.animation;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -26,9 +26,7 @@
import java.util.List;
-/**
- * Basic component animation spec
- */
+/** Basic component animation spec */
public class AnimationSpec implements Operation {
int mAnimationId = -1;
int mMotionDuration = 300;
@@ -38,9 +36,14 @@
ANIMATION mEnterAnimation = ANIMATION.FADE_IN;
ANIMATION mExitAnimation = ANIMATION.FADE_OUT;
- public AnimationSpec(int animationId, int motionDuration, int motionEasingType,
- int visibilityDuration, int visibilityEasingType,
- ANIMATION enterAnimation, ANIMATION exitAnimation) {
+ public AnimationSpec(
+ int animationId,
+ int motionDuration,
+ int motionEasingType,
+ int visibilityDuration,
+ int visibilityEasingType,
+ ANIMATION enterAnimation,
+ ANIMATION exitAnimation) {
this.mAnimationId = animationId;
this.mMotionDuration = motionDuration;
this.mMotionEasingType = motionEasingType;
@@ -51,9 +54,14 @@
}
public AnimationSpec() {
- this(-1, 300, GeneralEasing.CUBIC_STANDARD,
- 300, GeneralEasing.CUBIC_STANDARD,
- ANIMATION.FADE_IN, ANIMATION.FADE_OUT);
+ this(
+ -1,
+ 600,
+ GeneralEasing.CUBIC_STANDARD,
+ 500,
+ GeneralEasing.CUBIC_STANDARD,
+ ANIMATION.FADE_IN,
+ ANIMATION.FADE_OUT);
}
public int getAnimationId() {
@@ -102,8 +110,15 @@
@Override
public void write(WireBuffer buffer) {
- apply(buffer, mAnimationId, mMotionDuration, mMotionEasingType,
- mVisibilityDuration, mVisibilityEasingType, mEnterAnimation, mExitAnimation);
+ apply(
+ buffer,
+ mAnimationId,
+ mMotionDuration,
+ mMotionEasingType,
+ mVisibilityDuration,
+ mVisibilityEasingType,
+ mEnterAnimation,
+ mExitAnimation);
}
@Override
@@ -151,10 +166,15 @@
}
}
- public static void apply(WireBuffer buffer, int animationId, int motionDuration,
- int motionEasingType, int visibilityDuration,
- int visibilityEasingType, ANIMATION enterAnimation,
- ANIMATION exitAnimation) {
+ public static void apply(
+ WireBuffer buffer,
+ int animationId,
+ int motionDuration,
+ int motionEasingType,
+ int visibilityDuration,
+ int visibilityEasingType,
+ ANIMATION enterAnimation,
+ ANIMATION exitAnimation) {
buffer.start(Operations.ANIMATION_SPEC);
buffer.writeInt(animationId);
buffer.writeInt(motionDuration);
@@ -173,20 +193,25 @@
int visibilityEasingType = buffer.readInt();
ANIMATION enterAnimation = intToAnimation(buffer.readInt());
ANIMATION exitAnimation = intToAnimation(buffer.readInt());
- AnimationSpec op = new AnimationSpec(animationId, motionDuration, motionEasingType,
- visibilityDuration, visibilityEasingType, enterAnimation, exitAnimation);
+ AnimationSpec op =
+ new AnimationSpec(
+ animationId,
+ motionDuration,
+ motionEasingType,
+ visibilityDuration,
+ visibilityEasingType,
+ enterAnimation,
+ exitAnimation);
operations.add(op);
}
+
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Layout Operations",
- id(),
- name())
+ doc.operation("Layout Operations", id(), name())
.description("define the animation")
.field(INT, "animationId", "")
.field(INT, "motionDuration", "")
.field(INT, "motionEasingType", "")
.field(INT, "visibilityDuration", "")
.field(INT, "visibilityEasingType", "");
-
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java
index 5c5d056..686643f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java
@@ -27,9 +27,13 @@
HashMap<Integer, ArrayList<Particle>> mAllParticles = new HashMap<>();
PaintBundle mPaint = new PaintBundle();
- public void animate(PaintContext context, Component component,
- ComponentMeasure start, ComponentMeasure end,
- float progress) {
+
+ public void animate(
+ PaintContext context,
+ Component component,
+ ComponentMeasure start,
+ ComponentMeasure end,
+ float progress) {
ArrayList<Particle> particles = mAllParticles.get(component.getComponentId());
if (particles == null) {
particles = new ArrayList<Particle>();
@@ -37,9 +41,9 @@
float x = (float) Math.random();
float y = (float) Math.random();
float radius = (float) Math.random();
- float r = 250f;
- float g = 250f;
- float b = 250f;
+ float r = 220f;
+ float g = 220f;
+ float b = 220f;
particles.add(new Particle(x, y, radius, r, g, b));
}
mAllParticles.put(component.getComponentId(), particles);
@@ -49,12 +53,17 @@
for (int i = 0; i < particles.size(); i++) {
Particle particle = particles.get(i);
mPaint.reset();
- mPaint.setColor(particle.r, particle.g, particle.b,
- 200 * (1 - progress));
+ mPaint.setColor(
+ particle.r / 255f,
+ particle.g / 255f,
+ particle.b / 255f,
+ (200 * (1 - progress)) / 255f);
context.applyPaint(mPaint);
float dx = start.getX() + component.getWidth() * particle.x;
- float dy = start.getY() + component.getHeight() * particle.y
- + progress * 0.01f * component.getHeight();
+ float dy =
+ start.getY()
+ + component.getHeight() * particle.y
+ + progress * 0.01f * component.getHeight();
float dr = (component.getHeight() + 60) * 0.15f * particle.radius + (30 * progress);
context.drawCircle(dx, dy, dr);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java
index 88a49a66..047a968 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.managers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -30,9 +30,7 @@
import java.util.List;
-/**
- * Simple Box layout implementation
- */
+/** Simple Box layout implementation */
public class BoxLayout extends LayoutManager implements ComponentStartOperation {
public static final int START = 1;
@@ -41,37 +39,68 @@
public static final int TOP = 4;
public static final int BOTTOM = 5;
-
int mHorizontalPositioning;
int mVerticalPositioning;
- public BoxLayout(Component parent, int componentId, int animationId,
- float x, float y, float width, float height,
- int horizontalPositioning, int verticalPositioning) {
+ public BoxLayout(
+ Component parent,
+ int componentId,
+ int animationId,
+ float x,
+ float y,
+ float width,
+ float height,
+ int horizontalPositioning,
+ int verticalPositioning) {
super(parent, componentId, animationId, x, y, width, height);
mHorizontalPositioning = horizontalPositioning;
mVerticalPositioning = verticalPositioning;
}
- public BoxLayout(Component parent, int componentId, int animationId,
- int horizontalPositioning, int verticalPositioning) {
- this(parent, componentId, animationId, 0, 0, 0, 0,
- horizontalPositioning, verticalPositioning);
+ public BoxLayout(
+ Component parent,
+ int componentId,
+ int animationId,
+ int horizontalPositioning,
+ int verticalPositioning) {
+ this(
+ parent,
+ componentId,
+ animationId,
+ 0,
+ 0,
+ 0,
+ 0,
+ horizontalPositioning,
+ verticalPositioning);
}
@Override
public String toString() {
- return "BOX [" + mComponentId + ":" + mAnimationId + "] (" + mX + ", "
- + mY + " - " + mWidth + " x " + mHeight + ") " + mVisibility;
+ return "BOX ["
+ + mComponentId
+ + ":"
+ + mAnimationId
+ + "] ("
+ + mX
+ + ", "
+ + mY
+ + " - "
+ + mWidth
+ + " x "
+ + mHeight
+ + ") "
+ + mVisibility;
}
+ @Override
protected String getSerializedName() {
return "BOX";
}
@Override
- public void computeWrapSize(PaintContext context, float maxWidth, float maxHeight,
- MeasurePass measure, Size size) {
+ public void computeWrapSize(
+ PaintContext context, float maxWidth, float maxHeight, MeasurePass measure, Size size) {
for (Component c : mChildrenComponents) {
c.measure(context, 0f, maxWidth, 0f, maxHeight, measure);
ComponentMeasure m = measure.get(c);
@@ -84,16 +113,20 @@
}
@Override
- public void computeSize(PaintContext context, float minWidth, float maxWidth,
- float minHeight, float maxHeight, MeasurePass measure) {
+ public void computeSize(
+ PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ MeasurePass measure) {
for (Component child : mChildrenComponents) {
child.measure(context, minWidth, maxWidth, minHeight, maxHeight, measure);
}
}
@Override
- public void internalLayoutMeasure(PaintContext context,
- MeasurePass measure) {
+ public void internalLayoutMeasure(PaintContext context, MeasurePass measure) {
ComponentMeasure selfMeasure = measure.get(this);
float selfWidth = selfMeasure.getW() - mPaddingLeft - mPaddingRight;
float selfHeight = selfMeasure.getH() - mPaddingTop - mPaddingBottom;
@@ -136,8 +169,12 @@
return Operations.LAYOUT_BOX;
}
- public static void apply(WireBuffer buffer, int componentId, int animationId,
- int horizontalPositioning, int verticalPositioning) {
+ public static void apply(
+ WireBuffer buffer,
+ int componentId,
+ int animationId,
+ int horizontalPositioning,
+ int verticalPositioning) {
buffer.start(Operations.LAYOUT_BOX);
buffer.writeInt(componentId);
buffer.writeInt(animationId);
@@ -150,24 +187,32 @@
int animationId = buffer.readInt();
int horizontalPositioning = buffer.readInt();
int verticalPositioning = buffer.readInt();
- operations.add(new BoxLayout(null, componentId, animationId,
- horizontalPositioning, verticalPositioning));
+ operations.add(
+ new BoxLayout(
+ null,
+ componentId,
+ animationId,
+ horizontalPositioning,
+ verticalPositioning));
}
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
- .description("Box layout implementation.\n\n"
- + "Child components are laid out independently from one another,\n"
- + " and painted in their hierarchy order (first children drawn"
- + "before the latter). Horizontal and Vertical positioning"
- + "are supported.")
+ .description(
+ "Box layout implementation.\n\n"
+ + "Child components are laid out independently from one another,\n"
+ + " and painted in their hierarchy order (first children drawn"
+ + "before the latter). Horizontal and Vertical positioning"
+ + "are supported.")
.examplesDimension(150, 100)
.exampleImage("Top", "layout-BoxLayout-start-top.png")
.exampleImage("Center", "layout-BoxLayout-center-center.png")
.exampleImage("Bottom", "layout-BoxLayout-end-bottom.png")
.field(INT, "COMPONENT_ID", "unique id for this component")
- .field(INT, "ANIMATION_ID", "id used to match components,"
- + " for animation purposes")
+ .field(
+ INT,
+ "ANIMATION_ID",
+ "id used to match components," + " for animation purposes")
.field(INT, "HORIZONTAL_POSITIONING", "horizontal positioning value")
.possibleValues("START", BoxLayout.START)
.possibleValues("CENTER", BoxLayout.CENTER)
@@ -178,10 +223,8 @@
.possibleValues("BOTTOM", BoxLayout.BOTTOM);
}
-
@Override
public void write(WireBuffer buffer) {
- apply(buffer, mComponentId, mAnimationId,
- mHorizontalPositioning, mVerticalPositioning);
+ apply(buffer, mComponentId, mAnimationId, mHorizontalPositioning, mVerticalPositioning);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java
index bce7a77a..f799767 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.managers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -29,8 +29,14 @@
import java.util.List;
public class CanvasLayout extends BoxLayout {
- public CanvasLayout(Component parent, int componentId, int animationId,
- float x, float y, float width, float height) {
+ public CanvasLayout(
+ Component parent,
+ int componentId,
+ int animationId,
+ float x,
+ float y,
+ float width,
+ float height) {
super(parent, componentId, animationId, x, y, width, height, 0, 0);
}
@@ -40,10 +46,23 @@
@Override
public String toString() {
- return "CANVAS [" + mComponentId + ":" + mAnimationId + "] (" + mX + ", "
- + mY + " - " + mWidth + " x " + mHeight + ") " + mVisibility;
+ return "CANVAS ["
+ + mComponentId
+ + ":"
+ + mAnimationId
+ + "] ("
+ + mX
+ + ", "
+ + mY
+ + " - "
+ + mWidth
+ + " x "
+ + mHeight
+ + ") "
+ + mVisibility;
}
+ @Override
protected String getSerializedName() {
return "CANVAS";
}
@@ -72,13 +91,14 @@
doc.operation("Layout Operations", id(), name())
.description("Canvas implementation. Encapsulate draw operations.\n\n")
.field(INT, "COMPONENT_ID", "unique id for this component")
- .field(INT, "ANIMATION_ID", "id used to match components,"
- + " for animation purposes");
+ .field(
+ INT,
+ "ANIMATION_ID",
+ "id used to match components," + " for animation purposes");
}
@Override
- public void internalLayoutMeasure(PaintContext context,
- MeasurePass measure) {
+ public void internalLayoutMeasure(PaintContext context, MeasurePass measure) {
ComponentMeasure selfMeasure = measure.get(this);
float selfWidth = selfMeasure.getW() - mPaddingLeft - mPaddingRight;
float selfHeight = selfMeasure.getH() - mPaddingTop - mPaddingBottom;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
index 48d966e..402b784 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.managers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -34,8 +34,7 @@
import java.util.List;
/**
- * Simple Column layout implementation
- * - also supports weight and horizontal/vertical positioning
+ * Simple Column layout implementation - also supports weight and horizontal/vertical positioning
*/
public class ColumnLayout extends LayoutManager implements ComponentStartOperation {
public static final int START = 1;
@@ -51,69 +50,122 @@
int mVerticalPositioning;
float mSpacedBy = 0f;
- public ColumnLayout(Component parent, int componentId, int animationId,
- float x, float y, float width, float height,
- int horizontalPositioning, int verticalPositioning, float spacedBy) {
+ public ColumnLayout(
+ Component parent,
+ int componentId,
+ int animationId,
+ float x,
+ float y,
+ float width,
+ float height,
+ int horizontalPositioning,
+ int verticalPositioning,
+ float spacedBy) {
super(parent, componentId, animationId, x, y, width, height);
mHorizontalPositioning = horizontalPositioning;
mVerticalPositioning = verticalPositioning;
mSpacedBy = spacedBy;
}
- public ColumnLayout(Component parent, int componentId, int animationId,
- int horizontalPositioning, int verticalPositioning, float spacedBy) {
- this(parent, componentId, animationId, 0, 0, 0, 0,
- horizontalPositioning, verticalPositioning, spacedBy);
+ public ColumnLayout(
+ Component parent,
+ int componentId,
+ int animationId,
+ int horizontalPositioning,
+ int verticalPositioning,
+ float spacedBy) {
+ this(
+ parent,
+ componentId,
+ animationId,
+ 0,
+ 0,
+ 0,
+ 0,
+ horizontalPositioning,
+ verticalPositioning,
+ spacedBy);
}
@Override
public String toString() {
- return "COLUMN [" + mComponentId + ":" + mAnimationId + "] (" + mX + ", "
- + mY + " - " + mWidth + " x " + mHeight + ") " + mVisibility;
+ return "COLUMN ["
+ + mComponentId
+ + ":"
+ + mAnimationId
+ + "] ("
+ + mX
+ + ", "
+ + mY
+ + " - "
+ + mWidth
+ + " x "
+ + mHeight
+ + ") "
+ + mVisibility;
}
+ @Override
protected String getSerializedName() {
return "COLUMN";
}
@Override
- public void computeWrapSize(PaintContext context, float maxWidth, float maxHeight,
- MeasurePass measure, Size size) {
+ public void computeWrapSize(
+ PaintContext context, float maxWidth, float maxHeight, MeasurePass measure, Size size) {
DebugLog.s(() -> "COMPUTE WRAP SIZE in " + this + " (" + mComponentId + ")");
+ int visibleChildrens = 0;
for (Component c : mChildrenComponents) {
- c.measure(context, 0f, maxWidth,
- 0f, maxHeight, measure);
+ c.measure(context, 0f, maxWidth, 0f, maxHeight, measure);
ComponentMeasure m = measure.get(c);
- size.setWidth(Math.max(size.getWidth(), m.getW()));
- size.setHeight(size.getHeight() + m.getH());
+ if (m.getVisibility() != Visibility.GONE) {
+ size.setWidth(Math.max(size.getWidth(), m.getW()));
+ size.setHeight(size.getHeight() + m.getH());
+ visibleChildrens++;
+ }
}
if (!mChildrenComponents.isEmpty()) {
- size.setHeight(size.getHeight()
- + (mSpacedBy * (mChildrenComponents.size() - 1)));
+ size.setHeight(size.getHeight() + (mSpacedBy * (visibleChildrens - 1)));
}
DebugLog.e();
}
@Override
- public void computeSize(PaintContext context, float minWidth, float maxWidth,
- float minHeight, float maxHeight, MeasurePass measure) {
+ public void computeSize(
+ PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ MeasurePass measure) {
DebugLog.s(() -> "COMPUTE SIZE in " + this + " (" + mComponentId + ")");
float mh = maxHeight;
for (Component child : mChildrenComponents) {
child.measure(context, minWidth, maxWidth, minHeight, mh, measure);
ComponentMeasure m = measure.get(child);
- mh -= m.getH();
+ if (m.getVisibility() != Visibility.GONE) {
+ mh -= m.getH();
+ }
}
DebugLog.e();
}
@Override
- public void internalLayoutMeasure(PaintContext context,
- MeasurePass measure) {
+ public void internalLayoutMeasure(PaintContext context, MeasurePass measure) {
ComponentMeasure selfMeasure = measure.get(this);
- DebugLog.s(() -> "INTERNAL LAYOUT " + this + " (" + mComponentId + ") children: "
- + mChildrenComponents.size() + " size (" + selfMeasure.getW()
- + " x " + selfMeasure.getH() + ")");
+ DebugLog.s(
+ () ->
+ "INTERNAL LAYOUT "
+ + this
+ + " ("
+ + mComponentId
+ + ") children: "
+ + mChildrenComponents.size()
+ + " size ("
+ + selfMeasure.getW()
+ + " x "
+ + selfMeasure.getH()
+ + ")");
if (mChildrenComponents.isEmpty()) {
DebugLog.e();
return;
@@ -127,6 +179,9 @@
float totalWeights = 0f;
for (Component child : mChildrenComponents) {
ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
if (child instanceof LayoutComponent
&& ((LayoutComponent) child).getHeightModifier().hasWeight()) {
hasWeights = true;
@@ -141,21 +196,34 @@
if (child instanceof LayoutComponent
&& ((LayoutComponent) child).getHeightModifier().hasWeight()) {
ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
float weight = ((LayoutComponent) child).getHeightModifier().getValue();
childMeasure.setH((weight * availableSpace) / totalWeights);
- child.measure(context, childMeasure.getW(),
- childMeasure.getW(), childMeasure.getH(), childMeasure.getH(), measure);
+ child.measure(
+ context,
+ childMeasure.getW(),
+ childMeasure.getW(),
+ childMeasure.getH(),
+ childMeasure.getH(),
+ measure);
}
}
}
childrenHeight = 0f;
+ int visibleChildrens = 0;
for (Component child : mChildrenComponents) {
ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
childrenWidth = Math.max(childrenWidth, childMeasure.getW());
childrenHeight += childMeasure.getH();
+ visibleChildrens++;
}
- childrenHeight += mSpacedBy * (mChildrenComponents.size() - 1);
+ childrenHeight += mSpacedBy * (visibleChildrens - 1);
float tx = 0f;
float ty = 0f;
@@ -175,24 +243,33 @@
case SPACE_BETWEEN:
for (Component child : mChildrenComponents) {
ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
total += childMeasure.getH();
}
- verticalGap = (selfHeight - total) / (mChildrenComponents.size() - 1);
+ verticalGap = (selfHeight - total) / (visibleChildrens - 1);
break;
case SPACE_EVENLY:
for (Component child : mChildrenComponents) {
ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
total += childMeasure.getH();
}
- verticalGap = (selfHeight - total) / (mChildrenComponents.size() + 1);
+ verticalGap = (selfHeight - total) / (visibleChildrens + 1);
ty = verticalGap;
break;
case SPACE_AROUND:
for (Component child : mChildrenComponents) {
ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
total += childMeasure.getH();
}
- verticalGap = (selfHeight - total) / (mChildrenComponents.size());
+ verticalGap = (selfHeight - total) / visibleChildrens;
ty = verticalGap / 2f;
break;
}
@@ -211,6 +288,9 @@
}
childMeasure.setX(tx);
childMeasure.setY(ty);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
ty += childMeasure.getH();
if (mVerticalPositioning == SPACE_BETWEEN
|| mVerticalPositioning == SPACE_AROUND
@@ -230,8 +310,13 @@
return Operations.LAYOUT_COLUMN;
}
- public static void apply(WireBuffer buffer, int componentId, int animationId,
- int horizontalPositioning, int verticalPositioning, float spacedBy) {
+ public static void apply(
+ WireBuffer buffer,
+ int componentId,
+ int animationId,
+ int horizontalPositioning,
+ int verticalPositioning,
+ float spacedBy) {
buffer.start(Operations.LAYOUT_COLUMN);
buffer.writeInt(componentId);
buffer.writeInt(animationId);
@@ -246,15 +331,22 @@
int horizontalPositioning = buffer.readInt();
int verticalPositioning = buffer.readInt();
float spacedBy = buffer.readFloat();
- operations.add(new ColumnLayout(null, componentId, animationId,
- horizontalPositioning, verticalPositioning, spacedBy));
+ operations.add(
+ new ColumnLayout(
+ null,
+ componentId,
+ animationId,
+ horizontalPositioning,
+ verticalPositioning,
+ spacedBy));
}
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
- .description("Column layout implementation, positioning components one"
- + " after the other vertically.\n\n"
- + "It supports weight and horizontal/vertical positioning.")
+ .description(
+ "Column layout implementation, positioning components one"
+ + " after the other vertically.\n\n"
+ + "It supports weight and horizontal/vertical positioning.")
.examplesDimension(100, 400)
.exampleImage("Top", "layout-ColumnLayout-start-top.png")
.exampleImage("Center", "layout-ColumnLayout-start-center.png")
@@ -263,8 +355,10 @@
.exampleImage("SpaceAround", "layout-ColumnLayout-start-space-around.png")
.exampleImage("SpaceBetween", "layout-ColumnLayout-start-space-between.png")
.field(INT, "COMPONENT_ID", "unique id for this component")
- .field(INT, "ANIMATION_ID", "id used to match components,"
- + " for animation purposes")
+ .field(
+ INT,
+ "ANIMATION_ID",
+ "id used to match components," + " for animation purposes")
.field(INT, "HORIZONTAL_POSITIONING", "horizontal positioning value")
.possibleValues("START", ColumnLayout.START)
.possibleValues("CENTER", ColumnLayout.CENTER)
@@ -281,7 +375,12 @@
@Override
public void write(WireBuffer buffer) {
- apply(buffer, mComponentId, mAnimationId,
- mHorizontalPositioning, mVerticalPositioning, mSpacedBy);
+ apply(
+ buffer,
+ mComponentId,
+ mAnimationId,
+ mHorizontalPositioning,
+ mVerticalPositioning,
+ mSpacedBy);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
index 3a36617..308ed64 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
@@ -24,53 +24,58 @@
import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
-/**
- * Base class for layout managers -- resizable components.
- */
+/** Base class for layout managers -- resizable components. */
public abstract class LayoutManager extends LayoutComponent implements Measurable {
Size mCachedWrapSize = new Size(0f, 0f);
- public LayoutManager(Component parent, int componentId, int animationId,
- float x, float y, float width, float height) {
+ public LayoutManager(
+ Component parent,
+ int componentId,
+ int animationId,
+ float x,
+ float y,
+ float width,
+ float height) {
super(parent, componentId, animationId, x, y, width, height);
}
- /**
- * Implemented by subclasses to provide a layout/measure pass
- */
- public void internalLayoutMeasure(PaintContext context,
- MeasurePass measure) {
+ /** Implemented by subclasses to provide a layout/measure pass */
+ public void internalLayoutMeasure(PaintContext context, MeasurePass measure) {
// nothing here
}
- /**
- * Subclasses can implement this to provide wrap sizing
- */
- public void computeWrapSize(PaintContext context, float maxWidth, float maxHeight,
- MeasurePass measure, Size size) {
+ /** Subclasses can implement this to provide wrap sizing */
+ public void computeWrapSize(
+ PaintContext context, float maxWidth, float maxHeight, MeasurePass measure, Size size) {
// nothing here
}
- /**
- * Subclasses can implement this when not in wrap sizing
- */
- public void computeSize(PaintContext context, float minWidth, float maxWidth,
- float minHeight, float maxHeight, MeasurePass measure) {
+ /** Subclasses can implement this when not in wrap sizing */
+ public void computeSize(
+ PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ MeasurePass measure) {
// nothing here
}
- /**
- * Base implementation of the measure resolution
- */
+ /** Base implementation of the measure resolution */
@Override
- public void measure(PaintContext context, float minWidth, float maxWidth,
- float minHeight, float maxHeight, MeasurePass measure) {
+ public void measure(
+ PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ MeasurePass measure) {
boolean hasWrap = true;
- float measuredWidth = Math.min(maxWidth,
- computeModifierDefinedWidth() - mMarginLeft - mMarginRight);
- float measuredHeight = Math.min(maxHeight,
- computeModifierDefinedHeight() - mMarginTop - mMarginBottom);
+ float measuredWidth =
+ Math.min(maxWidth, computeModifierDefinedWidth() - mMarginLeft - mMarginRight);
+ float measuredHeight =
+ Math.min(maxHeight, computeModifierDefinedHeight() - mMarginTop - mMarginBottom);
float insetMaxWidth = maxWidth - mMarginLeft - mMarginRight;
float insetMaxHeight = maxHeight - mMarginTop - mMarginBottom;
if (mWidthModifier.isWrap() || mHeightModifier.isWrap()) {
@@ -129,9 +134,7 @@
internalLayoutMeasure(context, measure);
}
- /**
- * basic layout of internal components
- */
+ /** basic layout of internal components */
@Override
public void layout(RemoteContext context, MeasurePass measure) {
super.layout(context, measure);
@@ -143,4 +146,18 @@
}
this.mNeedsMeasure = false;
}
+
+ /**
+ * Only layout self, not children
+ *
+ * @param context
+ * @param measure
+ */
+ public void selfLayout(RemoteContext context, MeasurePass measure) {
+ super.layout(context, measure);
+ ComponentMeasure self = measure.get(this);
+
+ mComponentModifiers.layout(context, self.getW(), self.getH());
+ this.mNeedsMeasure = false;
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
index 5e452f3..b29a05c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.managers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -33,10 +33,7 @@
import java.util.List;
-/**
- * Simple Row layout implementation
- * - also supports weight and horizontal/vertical positioning
- */
+/** Simple Row layout implementation - also supports weight and horizontal/vertical positioning */
public class RowLayout extends LayoutManager implements ComponentStartOperation {
public static final int START = 1;
public static final int CENTER = 2;
@@ -51,68 +48,122 @@
int mVerticalPositioning;
float mSpacedBy = 0f;
- public RowLayout(Component parent, int componentId, int animationId,
- float x, float y, float width, float height,
- int horizontalPositioning, int verticalPositioning, float spacedBy) {
+ public RowLayout(
+ Component parent,
+ int componentId,
+ int animationId,
+ float x,
+ float y,
+ float width,
+ float height,
+ int horizontalPositioning,
+ int verticalPositioning,
+ float spacedBy) {
super(parent, componentId, animationId, x, y, width, height);
mHorizontalPositioning = horizontalPositioning;
mVerticalPositioning = verticalPositioning;
mSpacedBy = spacedBy;
}
- public RowLayout(Component parent, int componentId, int animationId,
- int horizontalPositioning, int verticalPositioning, float spacedBy) {
- this(parent, componentId, animationId, 0, 0, 0, 0,
- horizontalPositioning, verticalPositioning, spacedBy);
+ public RowLayout(
+ Component parent,
+ int componentId,
+ int animationId,
+ int horizontalPositioning,
+ int verticalPositioning,
+ float spacedBy) {
+ this(
+ parent,
+ componentId,
+ animationId,
+ 0,
+ 0,
+ 0,
+ 0,
+ horizontalPositioning,
+ verticalPositioning,
+ spacedBy);
}
@Override
public String toString() {
- return "ROW [" + mComponentId + ":" + mAnimationId + "] (" + mX + ", "
- + mY + " - " + mWidth + " x " + mHeight + ") " + mVisibility;
+ return "ROW ["
+ + mComponentId
+ + ":"
+ + mAnimationId
+ + "] ("
+ + mX
+ + ", "
+ + mY
+ + " - "
+ + mWidth
+ + " x "
+ + mHeight
+ + ") "
+ + mVisibility;
}
+ @Override
protected String getSerializedName() {
return "ROW";
}
@Override
- public void computeWrapSize(PaintContext context, float maxWidth, float maxHeight,
- MeasurePass measure, Size size) {
+ public void computeWrapSize(
+ PaintContext context, float maxWidth, float maxHeight, MeasurePass measure, Size size) {
DebugLog.s(() -> "COMPUTE WRAP SIZE in " + this + " (" + mComponentId + ")");
+ // int visibleChildrens = 0;
for (Component c : mChildrenComponents) {
c.measure(context, 0f, maxWidth, 0f, maxHeight, measure);
ComponentMeasure m = measure.get(c);
- size.setWidth(size.getWidth() + m.getW());
- size.setHeight(Math.max(size.getHeight(), m.getH()));
+ if (m.getVisibility() != Visibility.GONE) {
+ size.setWidth(size.getWidth() + m.getW());
+ size.setHeight(Math.max(size.getHeight(), m.getH()));
+ // visibleChildrens++;
+ }
}
if (!mChildrenComponents.isEmpty()) {
- size.setWidth(size.getWidth()
- + (mSpacedBy * (mChildrenComponents.size() - 1)));
+ size.setWidth(size.getWidth() + (mSpacedBy * (mChildrenComponents.size() - 1)));
}
DebugLog.e();
}
@Override
- public void computeSize(PaintContext context, float minWidth, float maxWidth,
- float minHeight, float maxHeight, MeasurePass measure) {
+ public void computeSize(
+ PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ MeasurePass measure) {
DebugLog.s(() -> "COMPUTE SIZE in " + this + " (" + mComponentId + ")");
float mw = maxWidth;
for (Component child : mChildrenComponents) {
child.measure(context, minWidth, mw, minHeight, maxHeight, measure);
ComponentMeasure m = measure.get(child);
- mw -= m.getW();
+ if (m.getVisibility() != Visibility.GONE) {
+ mw -= m.getW();
+ }
}
DebugLog.e();
}
@Override
- public void internalLayoutMeasure(PaintContext context,
- MeasurePass measure) {
+ public void internalLayoutMeasure(PaintContext context, MeasurePass measure) {
ComponentMeasure selfMeasure = measure.get(this);
- DebugLog.s(() -> "INTERNAL LAYOUT " + this + " (" + mComponentId + ") children: "
- + mChildrenComponents.size() + " size (" + selfMeasure.getW()
- + " x " + selfMeasure.getH() + ")");
+ DebugLog.s(
+ () ->
+ "INTERNAL LAYOUT "
+ + this
+ + " ("
+ + mComponentId
+ + ") children: "
+ + mChildrenComponents.size()
+ + " size ("
+ + selfMeasure.getW()
+ + " x "
+ + selfMeasure.getH()
+ + ")");
if (mChildrenComponents.isEmpty()) {
DebugLog.e();
return;
@@ -126,6 +177,9 @@
float totalWeights = 0f;
for (Component child : mChildrenComponents) {
ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
if (child instanceof LayoutComponent
&& ((LayoutComponent) child).getWidthModifier().hasWeight()) {
hasWeights = true;
@@ -143,21 +197,34 @@
if (child instanceof LayoutComponent
&& ((LayoutComponent) child).getWidthModifier().hasWeight()) {
ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
float weight = ((LayoutComponent) child).getWidthModifier().getValue();
childMeasure.setW((weight * availableSpace) / totalWeights);
- child.measure(context, childMeasure.getW(),
- childMeasure.getW(), childMeasure.getH(), childMeasure.getH(), measure);
+ child.measure(
+ context,
+ childMeasure.getW(),
+ childMeasure.getW(),
+ childMeasure.getH(),
+ childMeasure.getH(),
+ measure);
}
}
}
childrenWidth = 0f;
+ int visibleChildrens = 0;
for (Component child : mChildrenComponents) {
ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
childrenWidth += childMeasure.getW();
childrenHeight = Math.max(childrenHeight, childMeasure.getH());
+ visibleChildrens++;
}
- childrenWidth += mSpacedBy * (mChildrenComponents.size() - 1);
+ childrenWidth += mSpacedBy * (visibleChildrens - 1);
float tx = 0f;
float ty = 0f;
@@ -178,24 +245,33 @@
case SPACE_BETWEEN:
for (Component child : mChildrenComponents) {
ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
total += childMeasure.getW();
}
- horizontalGap = (selfWidth - total) / (mChildrenComponents.size() - 1);
+ horizontalGap = (selfWidth - total) / (visibleChildrens - 1);
break;
case SPACE_EVENLY:
for (Component child : mChildrenComponents) {
ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
total += childMeasure.getW();
}
- horizontalGap = (selfWidth - total) / (mChildrenComponents.size() + 1);
+ horizontalGap = (selfWidth - total) / (visibleChildrens + 1);
tx = horizontalGap;
break;
case SPACE_AROUND:
for (Component child : mChildrenComponents) {
ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
total += childMeasure.getW();
}
- horizontalGap = (selfWidth - total) / (mChildrenComponents.size());
+ horizontalGap = (selfWidth - total) / visibleChildrens;
tx = horizontalGap / 2f;
break;
}
@@ -215,6 +291,9 @@
}
childMeasure.setX(tx);
childMeasure.setY(ty);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
tx += childMeasure.getW();
if (mHorizontalPositioning == SPACE_BETWEEN
|| mHorizontalPositioning == SPACE_AROUND
@@ -234,8 +313,13 @@
return Operations.LAYOUT_ROW;
}
- public static void apply(WireBuffer buffer, int componentId, int animationId,
- int horizontalPositioning, int verticalPositioning, float spacedBy) {
+ public static void apply(
+ WireBuffer buffer,
+ int componentId,
+ int animationId,
+ int horizontalPositioning,
+ int verticalPositioning,
+ float spacedBy) {
buffer.start(Operations.LAYOUT_ROW);
buffer.writeInt(componentId);
buffer.writeInt(animationId);
@@ -250,15 +334,22 @@
int horizontalPositioning = buffer.readInt();
int verticalPositioning = buffer.readInt();
float spacedBy = buffer.readFloat();
- operations.add(new RowLayout(null, componentId, animationId,
- horizontalPositioning, verticalPositioning, spacedBy));
+ operations.add(
+ new RowLayout(
+ null,
+ componentId,
+ animationId,
+ horizontalPositioning,
+ verticalPositioning,
+ spacedBy));
}
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
- .description("Row layout implementation, positioning components one"
- + " after the other horizontally.\n\n"
- + "It supports weight and horizontal/vertical positioning.")
+ .description(
+ "Row layout implementation, positioning components one"
+ + " after the other horizontally.\n\n"
+ + "It supports weight and horizontal/vertical positioning.")
.examplesDimension(400, 100)
.exampleImage("Start", "layout-RowLayout-start-top.png")
.exampleImage("Center", "layout-RowLayout-center-top.png")
@@ -267,8 +358,10 @@
.exampleImage("SpaceAround", "layout-RowLayout-space-around-top.png")
.exampleImage("SpaceBetween", "layout-RowLayout-space-between-top.png")
.field(INT, "COMPONENT_ID", "unique id for this component")
- .field(INT, "ANIMATION_ID", "id used to match components,"
- + " for animation purposes")
+ .field(
+ INT,
+ "ANIMATION_ID",
+ "id used to match components," + " for animation purposes")
.field(INT, "HORIZONTAL_POSITIONING", "horizontal positioning value")
.possibleValues("START", RowLayout.START)
.possibleValues("CENTER", RowLayout.CENTER)
@@ -285,7 +378,12 @@
@Override
public void write(WireBuffer buffer) {
- apply(buffer, mComponentId, mAnimationId,
- mHorizontalPositioning, mVerticalPositioning, mSpacedBy);
+ apply(
+ buffer,
+ mComponentId,
+ mAnimationId,
+ mHorizontalPositioning,
+ mVerticalPositioning,
+ mSpacedBy);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java
new file mode 100644
index 0000000..b5c7281
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java
@@ -0,0 +1,565 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations.layout.managers;
+
+import com.android.internal.widget.remotecompose.core.CoreDocument;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStartOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * State-based animated layout
+ *
+ * <p>States are defined as child layouts. This layout handles interpolating between the different
+ * state in order to provide an automatic transition.
+ */
+public class StateLayout extends LayoutManager implements ComponentStartOperation {
+
+ public int measuredLayoutIndex = 0;
+ public int currentLayoutIndex = 0;
+ public int previousLayoutIndex = 0;
+ private int mIndexId = 0;
+
+ // This keep track of all the components associated with a given Id,
+ // (the key being the id), and the set of components corresponds to the set of states
+ // TODO: we should be able to optimize this
+ public Map<Integer, Component[]> statePaintedComponents = new HashMap<>();
+
+ public int MAX_CACHE_ELEMENTS = 16;
+ public int[] cacheListElementsId = new int[MAX_CACHE_ELEMENTS];
+
+ public boolean inTransition = false;
+
+ public StateLayout(
+ Component parent,
+ int componentId,
+ int animationId,
+ float x,
+ float y,
+ float width,
+ float height,
+ int indexId) {
+ super(parent, componentId, animationId, x, y, width, height);
+ // if (layoutInfo.visibleLayoutIndex != null) {
+ // layoutInfo.visibleLayoutIndex!!.addChangeListener(this)
+ // }
+ mIndexId = indexId;
+ }
+
+ @Override
+ public void inflate() {
+ super.inflate();
+ hideLayoutsOtherThan(currentLayoutIndex);
+ }
+
+ public void findAnimatedComponents() {
+ for (int i = 0; i < mChildrenComponents.size(); i++) {
+ Component cs = mChildrenComponents.get(i);
+ if (cs instanceof LayoutComponent) {
+ LayoutComponent state = (LayoutComponent) cs;
+ state.setX(0f);
+ state.setY(0f);
+ ArrayList<Component> childrenComponents = state.getChildrenComponents();
+ for (int j = 0; j < childrenComponents.size(); j++) {
+ Component child = childrenComponents.get(j);
+ if (child.getAnimationId() != -1) {
+ if (!statePaintedComponents.containsKey(child.getAnimationId())) {
+ statePaintedComponents.put(
+ child.getAnimationId(),
+ new Component[mChildrenComponents.size()]);
+ }
+ statePaintedComponents.get(child.getAnimationId())[i] = child;
+ }
+ }
+ }
+ }
+ collapsePaintedComponents();
+ }
+
+ public void collapsePaintedComponents() {
+ int numStates = mChildrenComponents.size();
+ for (Integer id : statePaintedComponents.keySet()) {
+ Component[] list = statePaintedComponents.get(id);
+ int numComponents = list.length;
+ if (numComponents > 1 && list[0] != null) {
+ Component c1 = list[0];
+ boolean same = true;
+ for (int i = 1; i < list.length; i++) {
+ Component c2 = list[i];
+ if (c2 == null || !c1.suitableForTransition(c2)) {
+ same = false;
+ break;
+ }
+ }
+ if (same) {
+ // TODO: Fix, shouldn't want to recopy all components
+ for (int i = 0; i < numStates; i++) {
+ list[i] = c1;
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void computeSize(
+ PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ MeasurePass measure) {
+ LayoutManager layout = getLayout(currentLayoutIndex);
+ layout.computeSize(context, minWidth, maxWidth, minHeight, maxHeight, measure);
+ }
+
+ @Override
+ public void internalLayoutMeasure(
+ PaintContext context,
+ // layoutInfo: LayoutInfo,
+ MeasurePass measure) {
+ LayoutManager layout = getLayout(currentLayoutIndex);
+ // layout.internalLayoutMeasure(context, layoutInfo, measure)
+ layout.internalLayoutMeasure(context, measure);
+ }
+
+ /** Subclasses can implement this to provide wrap sizing */
+ @Override
+ public void computeWrapSize(
+ PaintContext context, float maxWidth, float maxHeight, MeasurePass measure, Size size) {
+ LayoutManager layout = getLayout(currentLayoutIndex);
+ layout.computeWrapSize(context, maxWidth, maxHeight, measure, size);
+ }
+
+ @Override
+ public void onClick(RemoteContext context, CoreDocument document, float x, float y) {
+ if (!contains(x, y)) {
+ return;
+ }
+ LayoutManager layout = getLayout(currentLayoutIndex);
+ layout.onClick(context, document, x, y);
+ }
+
+ @Override
+ public void layout(RemoteContext context, MeasurePass measure) {
+ ComponentMeasure self = measure.get(this);
+ super.selfLayout(context, measure);
+
+ // We can simply layout the current layout...
+ LayoutManager layout = getLayout(currentLayoutIndex);
+
+ // Pass through the measure information from the state layout to the currently
+ // selected component that this being laid out.
+ ComponentMeasure layoutMeasure = measure.get(layout.getComponentId());
+ layoutMeasure.copyFrom(self);
+
+ layout.layout(context, measure);
+
+ // but if we are in a transition, we might have to layout previous widgets
+ if (inTransition && previousLayoutIndex != currentLayoutIndex) {
+ LayoutManager previous = getLayout(previousLayoutIndex);
+ for (Component c : previous.getChildrenComponents()) {
+ int id = c.getComponentId();
+ if (c.getAnimationId() != -1) {
+ id = c.getAnimationId();
+ Component[] rc = statePaintedComponents.get(id);
+ for (Component ac : rc) {
+ if (ac != null) {
+ ac.layout(context, measure);
+ }
+ }
+ }
+ if (measure.contains(id)) {
+ c.layout(context, measure);
+ }
+ }
+ }
+
+ mFirstLayout = false;
+ }
+
+ @Override
+ public void measure(
+ PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ MeasurePass measure) {
+ // The general approach for this widget is to do most of the work/setup in measure.
+ // layout and paint then simply use what's been setup in the measure phase.
+
+ // First, let's initialize the statePaintedComponents array;
+ // it contains for each animation id a set of components associated, one for each state.
+ // For now to keep things simple, all components sets have the same size (== number of
+ // states)
+ if (statePaintedComponents.isEmpty()) {
+ findAnimatedComponents();
+ }
+
+ // TODO : FIRST LAYOUT ANIMATE THE GONE ELEMENT, RESIZING IT. We should be able to fix it
+ // if we resize things before animting.
+
+ LayoutManager layout = getLayout(currentLayoutIndex);
+
+ // ok so *before* we do the layout, we should make sure to set the *new* widgets (that
+ // share the same id) to be at the same bounds / position as the current displayed ones
+ if (inTransition && currentLayoutIndex != previousLayoutIndex) {
+ LayoutManager previousLayout = getLayout(previousLayoutIndex);
+ for (Component c : layout.getChildrenComponents()) {
+ int id = c.getAnimationId();
+ if (id == -1) {
+ continue;
+ }
+ for (Component pc : previousLayout.getChildrenComponents()) {
+ if (pc.getAnimationId() == id) {
+ Component prev =
+ statePaintedComponents.get(c.getAnimationId())[previousLayoutIndex];
+ if (c != prev) {
+ c.measure(
+ context,
+ prev.getWidth(),
+ prev.getWidth(),
+ prev.getHeight(),
+ prev.getHeight(),
+ measure);
+ c.layout(context.getContext(), measure);
+ c.setX(prev.getX());
+ c.setY(prev.getY());
+ c.mVisibility = Visibility.GONE;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ // Alright, now that things are set in place, let's go ahead and measure the new world...
+ layout.measure(context, minWidth, maxWidth, minHeight, maxHeight, measure);
+
+ // recopy to animationIds the values
+ for (Component c : layout.getChildrenComponents()) {
+ ComponentMeasure cm = measure.get(c);
+ if (c.getAnimationId() != -1) {
+ // First, we grab the current component for an animation id, and get its measure,
+ // then set this measure to the measure for the animation id
+ ComponentMeasure m = measure.get(c.getAnimationId());
+ m.copyFrom(cm);
+
+ m.setVisibility(Visibility.VISIBLE);
+
+ // Then for each components sharing the id in all the states...
+ Component[] components = statePaintedComponents.get(c.getAnimationId());
+ for (int idx = 0; idx < components.length; idx++) {
+ Component ac = components[idx];
+ if (ac != null) {
+ ComponentMeasure m2 = measure.get(ac.getComponentId());
+
+ // ... we set their measures to be the measure of the current component
+ if (c != ac) {
+ m2.copyFrom(cm);
+ }
+
+ // Finally let's make sure that for all components we set their visibility
+ if (idx == currentLayoutIndex) {
+ m2.setVisibility(Visibility.VISIBLE);
+ } else {
+ if (c != ac) {
+ m2.setVisibility(Visibility.GONE);
+ }
+ }
+
+ // if the component isn't the current one, we should measure it
+ if (c != ac) {
+ ac.measure(context, m.getW(), m.getW(), m.getH(), m.getH(), measure);
+ }
+ }
+ }
+ } else {
+ // TODO: Ideally unify the visibility handing so that we also work in terms of
+ // component and not panel visibility. Ideally do not change the .visibility
+ // attribute at all and actually use the "current index" to decide whether to
+ // draw or not.
+ cm.setVisibility(Visibility.VISIBLE);
+ }
+ }
+
+ // Make sure to mark the components that are not in the new layout as being GONE
+ if (previousLayoutIndex != currentLayoutIndex) {
+ LayoutManager previousLayout = getLayout(previousLayoutIndex);
+ for (Component c : previousLayout.getChildrenComponents()) {
+ int id = c.getComponentId();
+ if (c.getAnimationId() != -1) {
+ id = c.getAnimationId();
+ }
+ if (!measure.contains(id)) {
+ ComponentMeasure m = measure.get(c.getComponentId());
+ m.setX(c.getX());
+ m.setY(c.getY());
+ m.setW(c.getWidth());
+ m.setH(c.getHeight());
+ m.setVisibility(Visibility.GONE);
+ }
+ }
+ }
+
+ ComponentMeasure m = measure.get(layout);
+ ComponentMeasure own = measure.get(this);
+ own.copyFrom(m);
+ measuredLayoutIndex = currentLayoutIndex;
+ }
+
+ public void hideLayoutsOtherThan(int idx) {
+ int index = 0;
+ for (Component pane : mChildrenComponents) {
+ if (pane instanceof LayoutComponent) {
+ if (index != idx) {
+ pane.mVisibility = Visibility.GONE;
+ } else {
+ pane.mVisibility = Visibility.VISIBLE;
+ }
+ index++;
+ }
+ }
+ }
+
+ public LayoutManager getLayout(int idx) {
+ int index = 0;
+ for (Component pane : mChildrenComponents) {
+ if (pane instanceof LayoutComponent) {
+ if (index == idx) {
+ return (LayoutManager) pane;
+ }
+ index++;
+ }
+ }
+ return (LayoutManager) mChildrenComponents.get(0);
+ }
+
+ @Override
+ public void paint(PaintContext context) {
+ if (mIndexId != 0) {
+ int newValue = context.getContext().mRemoteComposeState.getInteger(mIndexId);
+ if (newValue != currentLayoutIndex) {
+ previousLayoutIndex = currentLayoutIndex;
+ currentLayoutIndex = newValue;
+ inTransition = true;
+ System.out.println("currentLayout index is $currentLayoutIndex");
+ // executeValueSetActions(getLayout(currentLayoutIndex));
+ invalidateMeasure();
+ }
+ }
+ System.out.println("PAINTING LAYOUT STATELAYOUT, CURRENT INDEX " + currentLayoutIndex);
+ // Make sure to mark any components that are not in either the current or previous layout
+ // as being GONE.
+ int index = 0;
+ for (Component pane : mChildrenComponents) {
+ if (pane instanceof LayoutComponent) {
+ if (index != currentLayoutIndex && index != previousLayoutIndex) {
+ pane.mVisibility = Visibility.GONE;
+ }
+ if (index == currentLayoutIndex && pane.mVisibility != Visibility.VISIBLE) {
+ pane.mVisibility = Visibility.VISIBLE;
+ }
+ index++;
+ }
+ }
+
+ LayoutManager currentLayout = getLayout(measuredLayoutIndex);
+ boolean needsToPaintTransition = inTransition && previousLayoutIndex != measuredLayoutIndex;
+ if (needsToPaintTransition) {
+ // in case we have switched to a new state, during the transition
+ // we might still need to display the previous components that are not part of
+ // the new state (to enable them to run their exit animation)
+
+ LayoutManager previousLayout = getLayout(previousLayoutIndex);
+ int numPreviousComponents = previousLayout.getChildrenComponents().size();
+ if (numPreviousComponents > MAX_CACHE_ELEMENTS) {
+ MAX_CACHE_ELEMENTS *= 2;
+ cacheListElementsId = new int[MAX_CACHE_ELEMENTS];
+ }
+ // Make sure to apply the animation if there...
+ previousLayout.applyAnimationAsNeeded(context);
+
+ // Let's grab all the ids for the components of the previous layout...
+ int idIndex = 0;
+ for (Component c : previousLayout.getChildrenComponents()) {
+ cacheListElementsId[idIndex] = c.getPaintId();
+ idIndex++;
+ }
+ // ...then remove them if they are in the new layout
+ int count = idIndex;
+ for (Component c : currentLayout.getChildrenComponents()) {
+ int id = c.getPaintId();
+ for (int i = 0; i < idIndex; i++) {
+ if (cacheListElementsId[i] == id) {
+ cacheListElementsId[i] = -1;
+ count--;
+ }
+ }
+ }
+ // If we have components not present in the new state, paint them
+ if (count > 0) {
+ context.save();
+ context.translate(previousLayout.getX(), previousLayout.getY());
+ for (Component c : previousLayout.getChildrenComponents()) {
+ int id = c.getPaintId();
+ for (int i = 0; i < idIndex; i++) {
+ if (cacheListElementsId[i] == id) {
+ context.translate(
+ previousLayout.getMarginLeft(), previousLayout.getMarginTop());
+ c.paint(context);
+ context.translate(
+ -currentLayout.getMarginLeft(), -currentLayout.getMarginTop());
+ break;
+ }
+ }
+ }
+ context.restore();
+ }
+
+ // Make sure to apply the animation if there...
+ currentLayout.applyAnimationAsNeeded(context);
+ }
+
+ // We paint all the components and operations of the current layout
+ context.save();
+ context.translate(currentLayout.getX(), currentLayout.getY());
+ for (Operation op : currentLayout.getList()) {
+ if (op instanceof Component && ((Component) op).getAnimationId() != -1) {
+ Component[] stateComponents =
+ statePaintedComponents.get(((Component) op).getAnimationId());
+ Component component = stateComponents[measuredLayoutIndex];
+ if (needsToPaintTransition) {
+ // We might have two components to paint, as in case two different
+ // components share the same id, we'll fade the previous components out
+ // and fade in the new one
+ Component previousComponent = stateComponents[previousLayoutIndex];
+ if (previousComponent != null && component != previousComponent) {
+ context.translate(
+ currentLayout.getMarginLeft(), currentLayout.getMarginTop());
+ previousComponent.paint(context);
+ context.translate(
+ -currentLayout.getMarginLeft(), -currentLayout.getMarginTop());
+ }
+ }
+ context.translate(currentLayout.getMarginLeft(), currentLayout.getMarginTop());
+ component.paint(context);
+ context.translate(-currentLayout.getMarginLeft(), -currentLayout.getMarginTop());
+ } else if (op instanceof PaintOperation) {
+ ((PaintOperation) op).paint(context);
+ }
+ }
+ context.restore();
+
+ if (needsToPaintTransition) {
+ checkEndOfTransition();
+ }
+ }
+
+ public void checkEndOfTransition() {
+ LayoutManager currentLayout = getLayout(measuredLayoutIndex);
+ LayoutManager previousLayout = getLayout(previousLayoutIndex);
+ if (inTransition
+ && currentLayout.mAnimateMeasure == null
+ && previousLayout.mAnimateMeasure == null) {
+ inTransition = false;
+ LayoutManager previous = getLayout(previousLayoutIndex);
+ if (previous != currentLayout && previous.mVisibility != Visibility.GONE) {
+ previous.mVisibility = Visibility.GONE;
+ previous.needsRepaint();
+ }
+ }
+ }
+
+ // override fun onValueChanged(origamiValue: OrigamiValue<out Int>, oldValue: Int?, newValue:
+ // Int) {
+ // if (newValue != currentLayoutIndex) {
+ // previousLayoutIndex = currentLayoutIndex
+ // currentLayoutIndex = newValue
+ // inTransition = true
+ // println("currentLayout index is $currentLayoutIndex")
+ // executeValueSetActions(getLayout(currentLayoutIndex))
+ // invalidateMeasure()
+ // }
+ // }
+
+ // fun executeValueSetActions(layout: LayoutManager) {
+ // // FIXME : quick hack to support ValueSetClickActions, need to make that a little more
+ // // robust!
+ // for (op in layout.list) {
+ // if (op is LayoutComponent) {
+ // for (op2 in op.list) {
+ // if (op2 is OperationsList) {
+ // for (op3 in op2.list) {
+ // if (op3 is ValueSetClickAction<*, *>) {
+ // op3.onClick()
+ // }
+ // }
+ // }
+ // }
+ // }
+ // }
+ // }
+
+ @Override
+ public String toString() {
+ return "STATE_LAYOUT";
+ }
+
+ // companion object {
+ // fun documentation(doc: OrigamiDocumentation) {}
+ // }
+
+ public static void apply(
+ WireBuffer buffer,
+ int componentId,
+ int animationId,
+ int horizontalPositioning,
+ int verticalPositioning,
+ int indexId) {
+ buffer.start(Operations.LAYOUT_STATE);
+ buffer.writeInt(componentId);
+ buffer.writeInt(animationId);
+ buffer.writeInt(horizontalPositioning);
+ buffer.writeInt(verticalPositioning);
+ buffer.writeInt(indexId);
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ int componentId = buffer.readInt();
+ int animationId = buffer.readInt();
+ buffer.readInt(); // horizontalPositioning
+ buffer.readInt(); // verticalPositioning
+ int indexId = buffer.readInt();
+ operations.add(
+ new StateLayout(null, componentId, animationId, 0f, 0f, 100f, 100f, indexId));
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
index 2370500..c1cabcd 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.managers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -34,9 +34,7 @@
import java.util.List;
-/**
- * Text component, referencing a text id
- */
+/** Text component, referencing a text id */
public class TextLayout extends LayoutManager implements ComponentStartOperation, VariableSupport {
private static final boolean DEBUG = false;
@@ -86,10 +84,20 @@
needsRepaint();
}
- public TextLayout(Component parent, int componentId, int animationId,
- float x, float y, float width, float height,
- int textId, int color, float fontSize,
- int fontStyle, float fontWeight, int fontFamilyId) {
+ public TextLayout(
+ Component parent,
+ int componentId,
+ int animationId,
+ float x,
+ float y,
+ float width,
+ float height,
+ int textId,
+ int color,
+ float fontSize,
+ int fontStyle,
+ float fontWeight,
+ int fontFamilyId) {
super(parent, componentId, animationId, x, y, width, height);
mTextId = textId;
mColor = color;
@@ -99,11 +107,30 @@
mFontFamilyId = fontFamilyId;
}
- public TextLayout(Component parent, int componentId, int animationId,
- int textId, int color, float fontSize,
- int fontStyle, float fontWeight, int fontFamilyId) {
- this(parent, componentId, animationId, 0, 0, 0, 0,
- textId, color, fontSize, fontStyle, fontWeight, fontFamilyId);
+ public TextLayout(
+ Component parent,
+ int componentId,
+ int animationId,
+ int textId,
+ int color,
+ float fontSize,
+ int fontStyle,
+ float fontWeight,
+ int fontFamilyId) {
+ this(
+ parent,
+ componentId,
+ animationId,
+ 0,
+ 0,
+ 0,
+ 0,
+ textId,
+ color,
+ fontSize,
+ fontStyle,
+ fontWeight,
+ fontFamilyId);
}
public PaintBundle mPaint = new PaintBundle();
@@ -151,26 +178,57 @@
@Override
public String toString() {
- return "TEXT_LAYOUT [" + mComponentId + ":" + mAnimationId + "] (" + mX + ", "
- + mY + " - " + mWidth + " x " + mHeight + ") " + mVisibility;
+ return "TEXT_LAYOUT ["
+ + mComponentId
+ + ":"
+ + mAnimationId
+ + "] ("
+ + mX
+ + ", "
+ + mY
+ + " - "
+ + mWidth
+ + " x "
+ + mHeight
+ + ") "
+ + mVisibility;
}
+ @Override
protected String getSerializedName() {
return "TEXT_LAYOUT";
}
@Override
public void serializeToString(int indent, StringSerializer serializer) {
- serializer.append(indent, getSerializedName() + " [" + mComponentId
- + ":" + mAnimationId + "] = "
- + "[" + mX + ", " + mY + ", " + mWidth + ", " + mHeight + "] "
- + mVisibility + " (" + mTextId + ":\"" + mCachedString + "\")"
- );
+ serializer.append(
+ indent,
+ getSerializedName()
+ + " ["
+ + mComponentId
+ + ":"
+ + mAnimationId
+ + "] = "
+ + "["
+ + mX
+ + ", "
+ + mY
+ + ", "
+ + mWidth
+ + ", "
+ + mHeight
+ + "] "
+ + mVisibility
+ + " ("
+ + mTextId
+ + ":\""
+ + mCachedString
+ + "\")");
}
@Override
- public void computeWrapSize(PaintContext context, float maxWidth, float maxHeight,
- MeasurePass measure, Size size) {
+ public void computeWrapSize(
+ PaintContext context, float maxWidth, float maxHeight, MeasurePass measure, Size size) {
context.savePaint();
mPaint.reset();
mPaint.setTextSize(mFontSize);
@@ -196,9 +254,16 @@
return Operations.LAYOUT_TEXT;
}
- public static void apply(WireBuffer buffer, int componentId, int animationId,
- int textId, int color, float fontSize, int fontStyle,
- float fontWeight, int fontFamilyId) {
+ public static void apply(
+ WireBuffer buffer,
+ int componentId,
+ int animationId,
+ int textId,
+ int color,
+ float fontSize,
+ int fontStyle,
+ float fontWeight,
+ int fontFamilyId) {
buffer.start(id());
buffer.writeInt(componentId);
buffer.writeInt(animationId);
@@ -219,16 +284,27 @@
int fontStyle = buffer.readInt();
float fontWeight = buffer.readFloat();
int fontFamilyId = buffer.readInt();
- operations.add(new TextLayout(null, componentId, animationId, textId, color, fontSize,
- fontStyle, fontWeight, fontFamilyId));
+ operations.add(
+ new TextLayout(
+ null,
+ componentId,
+ animationId,
+ textId,
+ color,
+ fontSize,
+ fontStyle,
+ fontWeight,
+ fontFamilyId));
}
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
.description("Text layout implementation.\n\n")
.field(INT, "COMPONENT_ID", "unique id for this component")
- .field(INT, "ANIMATION_ID", "id used to match components,"
- + " for animation purposes")
+ .field(
+ INT,
+ "ANIMATION_ID",
+ "id used to match components," + " for animation purposes")
.field(INT, "COLOR", "text color")
.field(FLOAT, "FONT_SIZE", "font size")
.field(INT, "FONT_STYLE", "font style (0 = normal, 1 = italic)")
@@ -238,8 +314,15 @@
@Override
public void write(WireBuffer buffer) {
- apply(buffer, mComponentId, mAnimationId,
- mTextId, mColor, mFontSize, mFontStyle,
- mFontWeight, mFontFamilyId);
+ apply(
+ buffer,
+ mComponentId,
+ mAnimationId,
+ mTextId,
+ mColor,
+ mFontSize,
+ mFontStyle,
+ mFontWeight,
+ mFontFamilyId);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java
index 8dc10d5..285425f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java
@@ -13,13 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package com.android.internal.widget.remotecompose.core.operations.layout.measure;
+
import com.android.internal.widget.remotecompose.core.operations.layout.Component;
-/**
- * Encapsulate the result of a measure pass for a component
- */
+/** Encapsulate the result of a measure pass for a component */
public class ComponentMeasure {
int mId = -1;
float mX;
@@ -31,24 +29,31 @@
public void setX(float value) {
mX = value;
}
+
public void setY(float value) {
mY = value;
}
+
public void setW(float value) {
mW = value;
}
+
public void setH(float value) {
mH = value;
}
+
public float getX() {
return mX;
}
+
public float getY() {
return mY;
}
+
public float getW() {
return mW;
}
+
public float getH() {
return mH;
}
@@ -61,8 +66,8 @@
mVisibility = visibility;
}
- public ComponentMeasure(int id, float x, float y, float w, float h,
- Component.Visibility visibility) {
+ public ComponentMeasure(
+ int id, float x, float y, float w, float h, Component.Visibility visibility) {
this.mId = id;
this.mX = x;
this.mY = y;
@@ -76,8 +81,12 @@
}
public ComponentMeasure(Component component) {
- this(component.getComponentId(), component.getX(), component.getY(),
- component.getWidth(), component.getHeight(),
+ this(
+ component.getComponentId(),
+ component.getX(),
+ component.getY(),
+ component.getWidth(),
+ component.getHeight(),
component.mVisibility);
}
@@ -88,4 +97,8 @@
mH = m.mH;
mVisibility = m.mVisibility;
}
+
+ public boolean same(ComponentMeasure m) {
+ return mX == m.mX && mY == m.mY && mW == m.mW && mH == m.mH && mVisibility == m.mVisibility;
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Measurable.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Measurable.java
index d167d9b..b48c2d5 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Measurable.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Measurable.java
@@ -13,33 +13,33 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package com.android.internal.widget.remotecompose.core.operations.layout.measure;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.RemoteContext;
-/**
- * Interface describing the measure/layout contract for components
- */
+/** Interface describing the measure/layout contract for components */
public interface Measurable {
/**
- * Measure a component and store the result of the measure in the provided MeasurePass.
- * This does not apply the measure to the component.
+ * Measure a component and store the result of the measure in the provided MeasurePass. This
+ * does not apply the measure to the component.
*/
- void measure(PaintContext context, float minWidth, float maxWidth,
- float minHeight, float maxHeight, MeasurePass measure);
+ void measure(
+ PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ MeasurePass measure);
- /**
- * Apply a given measure to the component
- */
+ /** Apply a given measure to the component */
void layout(RemoteContext context, MeasurePass measure);
/**
* Return true if the component needs to be remeasured
+ *
* @return true if need to remeasured, false otherwise
*/
boolean needsMeasure();
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java
index 6801deb..8d01fea 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java
@@ -20,8 +20,8 @@
import java.util.HashMap;
/**
- * Represents the result of a measure pass on the entire hierarchy
- * TODO: optimize to use a flat array vs the current hashmap
+ * Represents the result of a measure pass on the entire hierarchy TODO: optimize to use a flat
+ * array vs the current hashmap
*/
public class MeasurePass {
HashMap<Integer, ComponentMeasure> mList = new HashMap<>();
@@ -43,8 +43,9 @@
public ComponentMeasure get(Component c) {
if (!mList.containsKey(c.getComponentId())) {
- ComponentMeasure measure = new ComponentMeasure(c.getComponentId(),
- c.getX(), c.getY(), c.getWidth(), c.getHeight());
+ ComponentMeasure measure =
+ new ComponentMeasure(
+ c.getComponentId(), c.getX(), c.getY(), c.getWidth(), c.getHeight());
mList.put(c.getComponentId(), measure);
return measure;
}
@@ -53,8 +54,8 @@
public ComponentMeasure get(int id) {
if (!mList.containsKey(id)) {
- ComponentMeasure measure = new ComponentMeasure(id,
- 0f, 0f, 0f, 0f, Component.Visibility.GONE);
+ ComponentMeasure measure =
+ new ComponentMeasure(id, 0f, 0f, 0f, 0f, Component.Visibility.GONE);
mList.put(id, measure);
return measure;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Size.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Size.java
index b11d8e8..53f4a71 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Size.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Size.java
@@ -15,12 +15,11 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.measure;
-/**
- * Basic data class representing a component size, used during layout computations.
- */
+/** Basic data class representing a component size, used during layout computations. */
public class Size {
float mWidth;
float mHeight;
+
public Size(float width, float height) {
this.mWidth = width;
this.mHeight = height;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java
index f3e6a8e..64e40f7 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -28,9 +28,7 @@
import java.util.List;
-/**
- * Component size-aware background draw
- */
+/** Component size-aware background draw */
public class BackgroundModifierOperation extends DecoratorModifierOperation {
private static final int OP_CODE = Operations.MODIFIER_BACKGROUND;
private static final String CLASS_NAME = "BackgroundModifierOperation";
@@ -46,9 +44,16 @@
public PaintBundle mPaint = new PaintBundle();
- public BackgroundModifierOperation(float x, float y, float width, float height,
- float r, float g, float b, float a,
- int shapeType) {
+ public BackgroundModifierOperation(
+ float x,
+ float y,
+ float width,
+ float height,
+ float r,
+ float g,
+ float b,
+ float a,
+ int shapeType) {
this.mX = x;
this.mY = y;
this.mWidth = width;
@@ -67,10 +72,27 @@
@Override
public void serializeToString(int indent, StringSerializer serializer) {
- serializer.append(indent, "BACKGROUND = [" + mX + ", "
- + mY + ", " + mWidth + ", " + mHeight
- + "] color [" + mR + ", " + mG + ", " + mB + ", " + mA
- + "] shape [" + mShapeType + "]");
+ serializer.append(
+ indent,
+ "BACKGROUND = ["
+ + mX
+ + ", "
+ + mY
+ + ", "
+ + mWidth
+ + ", "
+ + mHeight
+ + "] color ["
+ + mR
+ + ", "
+ + mG
+ + ", "
+ + mB
+ + ", "
+ + mA
+ + "] shape ["
+ + mShapeType
+ + "]");
}
@Override
@@ -92,8 +114,17 @@
return OP_CODE;
}
- public static void apply(WireBuffer buffer, float x, float y, float width, float height,
- float r, float g, float b, float a, int shapeType) {
+ public static void apply(
+ WireBuffer buffer,
+ float x,
+ float y,
+ float width,
+ float height,
+ float r,
+ float g,
+ float b,
+ float a,
+ int shapeType) {
buffer.start(OP_CODE);
buffer.writeFloat(x);
buffer.writeFloat(y);
@@ -118,11 +149,9 @@
float a = buffer.readFloat();
// shape type
int shapeType = buffer.readInt();
- operations.add(new BackgroundModifierOperation(x, y, width, height,
- r, g, b, a, shapeType));
+ operations.add(new BackgroundModifierOperation(x, y, width, height, r, g, b, a, shapeType));
}
-
@Override
public void paint(PaintContext context) {
context.savePaint();
@@ -133,16 +162,13 @@
if (mShapeType == ShapeType.RECTANGLE) {
context.drawRect(0f, 0f, mWidth, mHeight);
} else if (mShapeType == ShapeType.CIRCLE) {
- context.drawCircle(mWidth / 2f, mHeight / 2f,
- Math.min(mWidth, mHeight) / 2f);
+ context.drawCircle(mWidth / 2f, mHeight / 2f, Math.min(mWidth, mHeight) / 2f);
}
context.restorePaint();
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Modifier Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
.description("define the Background Modifier")
.field(FLOAT, "x", "")
.field(FLOAT, "y", "")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
index 4c83ec4..92c0a73 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -28,9 +28,7 @@
import java.util.List;
-/**
- * Component size-aware border draw
- */
+/** Component size-aware border draw */
public class BorderModifierOperation extends DecoratorModifierOperation {
private static final int OP_CODE = Operations.MODIFIER_BORDER;
public static final String CLASS_NAME = "BorderModifierOperation";
@@ -49,9 +47,18 @@
public PaintBundle paint = new PaintBundle();
- public BorderModifierOperation(float x, float y, float width, float height,
- float borderWidth, float roundedCorner,
- float r, float g, float b, float a, int shapeType) {
+ public BorderModifierOperation(
+ float x,
+ float y,
+ float width,
+ float height,
+ float borderWidth,
+ float roundedCorner,
+ float r,
+ float g,
+ float b,
+ float a,
+ int shapeType) {
this.mX = x;
this.mY = y;
this.mWidth = width;
@@ -67,17 +74,51 @@
@Override
public void serializeToString(int indent, StringSerializer serializer) {
- serializer.append(indent, "BORDER = [" + mX + ", " + mY + ", "
- + mWidth + ", " + mHeight + "] "
- + "color [" + mR + ", " + mG + ", " + mB + ", " + mA + "] "
- + "border [" + mBorderWidth + ", " + mRoundedCorner + "] "
- + "shape [" + mShapeType + "]");
+ serializer.append(
+ indent,
+ "BORDER = ["
+ + mX
+ + ", "
+ + mY
+ + ", "
+ + mWidth
+ + ", "
+ + mHeight
+ + "] "
+ + "color ["
+ + mR
+ + ", "
+ + mG
+ + ", "
+ + mB
+ + ", "
+ + mA
+ + "] "
+ + "border ["
+ + mBorderWidth
+ + ", "
+ + mRoundedCorner
+ + "] "
+ + "shape ["
+ + mShapeType
+ + "]");
}
@Override
public void write(WireBuffer buffer) {
- apply(buffer, mX, mY, mWidth, mHeight, mBorderWidth, mRoundedCorner,
- mR, mG, mB, mA, mShapeType);
+ apply(
+ buffer,
+ mX,
+ mY,
+ mWidth,
+ mHeight,
+ mBorderWidth,
+ mRoundedCorner,
+ mR,
+ mG,
+ mB,
+ mA,
+ mShapeType);
}
@Override
@@ -88,12 +129,29 @@
@Override
public String toString() {
- return "BorderModifierOperation(" + mX + "," + mY + " - " + mWidth + " x " + mHeight + ") "
- + "borderWidth(" + mBorderWidth + ") "
- + "color(" + mR + "," + mG + "," + mB + "," + mA + ")";
+ return "BorderModifierOperation("
+ + mX
+ + ","
+ + mY
+ + " - "
+ + mWidth
+ + " x "
+ + mHeight
+ + ") "
+ + "borderWidth("
+ + mBorderWidth
+ + ") "
+ + "color("
+ + mR
+ + ","
+ + mG
+ + ","
+ + mB
+ + ","
+ + mA
+ + ")";
}
-
public static String name() {
return CLASS_NAME;
}
@@ -102,10 +160,19 @@
return OP_CODE;
}
- public static void apply(WireBuffer buffer, float x, float y, float width, float height,
- float borderWidth, float roundedCorner,
- float r, float g, float b, float a,
- int shapeType) {
+ public static void apply(
+ WireBuffer buffer,
+ float x,
+ float y,
+ float width,
+ float height,
+ float borderWidth,
+ float roundedCorner,
+ float r,
+ float g,
+ float b,
+ float a,
+ int shapeType) {
buffer.start(OP_CODE);
buffer.writeFloat(x);
buffer.writeFloat(y);
@@ -134,11 +201,10 @@
float a = buffer.readFloat();
// shape type
int shapeType = buffer.readInt();
- operations.add(new BorderModifierOperation(x, y, width, height, bw,
- rc, r, g, b, a, shapeType));
+ operations.add(
+ new BorderModifierOperation(x, y, width, height, bw, rc, r, g, b, a, shapeType));
}
-
@Override
public void paint(PaintContext context) {
context.savePaint();
@@ -160,9 +226,7 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Modifier Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
.description("define the Border Modifier")
.field(FLOAT, "x", "")
.field(FLOAT, "y", "")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java
index 7cb7925..0d8aeaa 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java
@@ -27,9 +27,7 @@
import java.util.List;
-/**
- * Support modifier clip with a rectangle
- */
+/** Support modifier clip with a rectangle */
public class ClipRectModifierOperation extends DecoratorModifierOperation {
public static final String CLASS_NAME = "ClipRectModifierOperation";
private static final int OP_CODE = Operations.MODIFIER_CLIP_RECT;
@@ -48,15 +46,14 @@
}
@Override
- public void onClick(RemoteContext context, CoreDocument document,
- Component component, float x, float y) {
+ public void onClick(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
// nothing
}
@Override
public void serializeToString(int indent, StringSerializer serializer) {
- serializer.append(
- indent, "CLIP_RECT = [" + mWidth + ", " + mHeight + "]");
+ serializer.append(indent, "CLIP_RECT = [" + mWidth + ", " + mHeight + "]");
}
@Override
@@ -68,7 +65,6 @@
return CLASS_NAME;
}
-
public static int id() {
return OP_CODE;
}
@@ -77,16 +73,12 @@
buffer.start(OP_CODE);
}
-
public static void read(WireBuffer buffer, List<Operation> operations) {
operations.add(new ClipRectModifierOperation());
}
-
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw the specified round-rect");
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java
index f55c941..95786a8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java
@@ -29,9 +29,7 @@
import java.util.ArrayList;
-/**
- * Maintain a list of modifiers
- */
+/** Maintain a list of modifiers */
public class ComponentModifiers extends PaintOperation implements DecoratorComponent {
ArrayList<ModifierOperation> mList = new ArrayList<>();
@@ -49,7 +47,7 @@
@Override
public String toString() {
- String str = "ComponentModifiers \n";
+ String str = "ComponentModifiers \n";
for (ModifierOperation modifierOperation : mList) {
str += " " + modifierOperation.toString() + "\n";
}
@@ -125,8 +123,9 @@
mList.addAll(operations);
}
- public void onClick(RemoteContext context, CoreDocument document,
- Component component, float x, float y) {
+ @Override
+ public void onClick(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
for (ModifierOperation op : mList) {
if (op instanceof DecoratorComponent) {
((DecoratorComponent) op).onClick(context, document, component, x, y);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java
index 9c19073..312d016 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.Operation;
@@ -31,11 +31,9 @@
import java.util.List;
-/**
- * Allows setting visibility on a component
- */
-public class ComponentVisibilityOperation implements ModifierOperation,
- VariableSupport, DecoratorComponent {
+/** Allows setting visibility on a component */
+public class ComponentVisibilityOperation
+ implements ModifierOperation, VariableSupport, DecoratorComponent {
private static final int OP_CODE = Operations.MODIFIER_VISIBILITY;
int mVisibilityId;
@@ -61,19 +59,15 @@
}
@Override
- public void apply(RemoteContext context) {
- }
+ public void apply(RemoteContext context) {}
@Override
public String deepToString(String indent) {
return (indent != null ? indent : "") + toString();
}
-
@Override
- public void write(WireBuffer buffer) {
-
- }
+ public void write(WireBuffer buffer) {}
public static void apply(WireBuffer buffer, int valueId) {
buffer.start(OP_CODE);
@@ -87,8 +81,9 @@
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", OP_CODE, "ComponentVisibility")
- .description("This operation allows setting a component"
- + "visibility from a provided value")
+ .description(
+ "This operation allows setting a component"
+ + "visibility from a provided value")
.field(INT, "VALUE_ID", "Value ID representing the visibility");
}
@@ -119,13 +114,9 @@
}
@Override
- public void layout(RemoteContext context, float width, float height) {
-
- }
+ public void layout(RemoteContext context, float width, float height) {}
@Override
- public void onClick(RemoteContext context, CoreDocument document,
- Component component, float x, float y) {
-
- }
+ public void onClick(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DecoratorModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DecoratorModifierOperation.java
index 70a5728..41e18cb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DecoratorModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DecoratorModifierOperation.java
@@ -22,15 +22,15 @@
import com.android.internal.widget.remotecompose.core.operations.layout.DecoratorComponent;
/**
- * Represents a decorator modifier (lightweight component), ie a modifier
- * that impacts the visual output (background, border...)
+ * Represents a decorator modifier (lightweight component), ie a modifier that impacts the visual
+ * output (background, border...)
*/
public abstract class DecoratorModifierOperation extends PaintOperation
implements ModifierOperation, DecoratorComponent {
@Override
- public void onClick(RemoteContext context, CoreDocument document,
- Component component, float x, float y) {
+ public void onClick(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
// nothing
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java
index f085ffb..408bebc 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java
@@ -20,13 +20,16 @@
import com.android.internal.widget.remotecompose.core.operations.Utils;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
-/**
- * Base class for dimension modifiers
- */
+/** Base class for dimension modifiers */
public abstract class DimensionModifierOperation implements ModifierOperation, VariableSupport {
public enum Type {
- EXACT, FILL, WRAP, WEIGHT, INTRINSIC_MIN, INTRINSIC_MAX;
+ EXACT,
+ FILL,
+ WRAP,
+ WEIGHT,
+ INTRINSIC_MIN,
+ INTRINSIC_MAX;
static Type fromInt(int value) {
switch (value) {
@@ -67,10 +70,8 @@
@Override
public void updateVariables(RemoteContext context) {
if (mType == Type.EXACT) {
- mOutValue = (Float.isNaN(mValue))
- ? context.getFloat(Utils.idFromNan(mValue)) : mValue;
+ mOutValue = Float.isNaN(mValue) ? context.getFloat(Utils.idFromNan(mValue)) : mValue;
}
-
}
@Override
@@ -80,10 +81,8 @@
context.listensTo(Utils.idFromNan(mValue), this);
}
}
-
}
-
public boolean hasWeight() {
return mType == Type.WEIGHT;
}
@@ -108,7 +107,6 @@
mOutValue = mValue = value;
}
-
public String serializedName() {
return "DIMENSION";
}
@@ -121,8 +119,7 @@
}
@Override
- public void apply(RemoteContext context) {
- }
+ public void apply(RemoteContext context) {}
@Override
public String deepToString(String indent) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
index a0f576a..d3613f8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -25,9 +25,7 @@
import java.util.List;
-/**
- * Set the height dimension on a component
- */
+/** Set the height dimension on a component */
public class HeightModifierOperation extends DimensionModifierOperation {
private static final int OP_CODE = Operations.MODIFIER_HEIGHT;
public static final String CLASS_NAME = "HeightModifierOperation";
@@ -81,9 +79,7 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Modifier Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
.description("define the animation")
.field(INT, "type", "")
.field(FLOAT, "value", "");
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java
index d405b2b..ac42470a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.Operation;
@@ -29,9 +29,7 @@
import java.util.List;
-/**
- * Capture a host action information. This can be triggered on eg. a click.
- */
+/** Capture a host action information. This can be triggered on eg. a click. */
public class HostActionOperation implements ActionOperation {
private static final int OP_CODE = Operations.HOST_ACTION;
@@ -60,23 +58,19 @@
}
@Override
- public void apply(RemoteContext context) {
- }
+ public void apply(RemoteContext context) {}
@Override
public String deepToString(String indent) {
return (indent != null ? indent : "") + toString();
}
+ @Override
+ public void write(WireBuffer buffer) {}
@Override
- public void write(WireBuffer buffer) {
-
- }
-
- @Override
- public void runAction(RemoteContext context, CoreDocument document,
- Component component, float x, float y) {
+ public void runAction(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
context.runAction(mActionId, "");
}
@@ -95,5 +89,4 @@
.description("Host action. This operation represents a host action")
.field(INT, "ACTION_ID", "Host Action ID");
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java
index 35f202b..b674a58 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.Operation;
@@ -29,9 +29,7 @@
import java.util.List;
-/**
- * Capture a host action information. This can be triggered on eg. a click.
- */
+/** Capture a host action information. This can be triggered on eg. a click. */
public class HostNamedActionOperation implements ActionOperation {
private static final int OP_CODE = Operations.HOST_NAMED_ACTION;
@@ -56,23 +54,19 @@
}
@Override
- public void apply(RemoteContext context) {
- }
+ public void apply(RemoteContext context) {}
@Override
public String deepToString(String indent) {
return (indent != null ? indent : "") + toString();
}
+ @Override
+ public void write(WireBuffer buffer) {}
@Override
- public void write(WireBuffer buffer) {
-
- }
-
- @Override
- public void runAction(RemoteContext context, CoreDocument document,
- Component component, float x, float y) {
+ public void runAction(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
context.runNamedAction(mTextId);
}
@@ -91,5 +85,4 @@
.description("Host Named action. This operation represents a host action")
.field(INT, "TEXT_ID", "Named Host Action Text ID");
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java
index 5299719..50f098e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java
@@ -18,9 +18,7 @@
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
-/**
- * Represents a modifier
- */
+/** Represents a modifier */
public interface ModifierOperation extends Operation {
void serializeToString(int indent, StringSerializer serializer);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java
index 668db3b..e0ec1a6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -27,8 +27,8 @@
import java.util.List;
/**
- * Represents a padding modifier.
- * Padding modifiers can be chained and will impact following modifiers.
+ * Represents a padding modifier. Padding modifiers can be chained and will impact following
+ * modifiers.
*/
public class PaddingModifierOperation implements ModifierOperation {
private static final int OP_CODE = Operations.MODIFIER_PADDING;
@@ -84,13 +84,12 @@
@Override
public void serializeToString(int indent, StringSerializer serializer) {
- serializer.append(indent, "PADDING = [" + mLeft + ", " + mTop + ", "
- + mRight + ", " + mBottom + "]");
+ serializer.append(
+ indent, "PADDING = [" + mLeft + ", " + mTop + ", " + mRight + ", " + mBottom + "]");
}
@Override
- public void apply(RemoteContext context) {
- }
+ public void apply(RemoteContext context) {}
@Override
public String deepToString(String indent) {
@@ -99,8 +98,15 @@
@Override
public String toString() {
- return "PaddingModifierOperation(" + mLeft + ", " + mTop
- + ", " + mRight + ", " + mBottom + ")";
+ return "PaddingModifierOperation("
+ + mLeft
+ + ", "
+ + mTop
+ + ", "
+ + mRight
+ + ", "
+ + mBottom
+ + ")";
}
public static String name() {
@@ -111,8 +117,7 @@
return Operations.MODIFIER_PADDING;
}
- public static void apply(WireBuffer buffer,
- float left, float top, float right, float bottom) {
+ public static void apply(WireBuffer buffer, float left, float top, float right, float bottom) {
buffer.start(Operations.MODIFIER_PADDING);
buffer.writeFloat(left);
buffer.writeFloat(top);
@@ -129,9 +134,7 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Modifier Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
.description("define the Padding Modifier")
.field(FLOAT, "left", "")
.field(FLOAT, "top", "")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java
index 9b662bf5..dc95fe7 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.Operation;
@@ -31,9 +31,7 @@
import java.util.List;
-/**
- * Support clip with a rounded rectangle
- */
+/** Support clip with a rounded rectangle */
public class RoundedClipRectModifierOperation extends DrawBase4
implements ModifierOperation, DecoratorComponent {
public static final int OP_CODE = Operations.MODIFIER_ROUNDED_CLIP_RECT;
@@ -52,44 +50,41 @@
return CLASS_NAME;
}
-
- protected void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3,
- float v4) {
+ @Override
+ protected void write(WireBuffer buffer, float v1, float v2, float v3, float v4) {
apply(buffer, v1, v2, v3, v4);
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Modifier Operations",
- id(),
- "RoundedClipRectModifierOperation")
+ doc.operation("Modifier Operations", id(), "RoundedClipRectModifierOperation")
.description("clip with rectangle")
- .field(FLOAT, "topStart",
+ .field(
+ FLOAT,
+ "topStart",
"The topStart radius of the rectangle to "
+ "intersect with the current clip")
- .field(FLOAT, "topEnd",
+ .field(
+ FLOAT,
+ "topEnd",
"The topEnd radius of the rectangle to "
+ "intersect with the current clip")
- .field(FLOAT, "bottomStart",
+ .field(
+ FLOAT,
+ "bottomStart",
"The bottomStart radius of the rectangle to "
+ "intersect with the current clip")
- .field(FLOAT, "bottomEnd",
+ .field(
+ FLOAT,
+ "bottomEnd",
"The bottomEnd radius of the rectangle to "
+ "intersect with the current clip");
}
-
float mWidth;
float mHeight;
-
public RoundedClipRectModifierOperation(
- float topStart,
- float topEnd,
- float bottomStart,
- float bottomEnd) {
+ float topStart, float topEnd, float bottomStart, float bottomEnd) {
super(topStart, topEnd, bottomStart, bottomEnd);
mName = CLASS_NAME;
}
@@ -106,33 +101,41 @@
}
@Override
- public void onClick(RemoteContext context, CoreDocument document,
- Component component, float x, float y) {
+ public void onClick(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
// nothing
}
@Override
public void serializeToString(int indent, StringSerializer serializer) {
serializer.append(
- indent, "ROUNDED_CLIP_RECT = [" + mWidth + ", " + mHeight
- + ", " + mX1 + ", " + mY1
- + ", " + mX2 + ", " + mY2 + "]");
+ indent,
+ "ROUNDED_CLIP_RECT = ["
+ + mWidth
+ + ", "
+ + mHeight
+ + ", "
+ + mX1
+ + ", "
+ + mY1
+ + ", "
+ + mX2
+ + ", "
+ + mY2
+ + "]");
}
/**
* Writes out the rounded rect clip to the buffer
*
- * @param buffer buffer to write to
- * @param topStart topStart radius
- * @param topEnd topEnd radius
- * @param bottomStart bottomStart radius
- * @param bottomEnd bottomEnd radius
+ * @param buffer buffer to write to
+ * @param topStart topStart radius
+ * @param topEnd topEnd radius
+ * @param bottomStart bottomStart radius
+ * @param bottomEnd bottomEnd radius
*/
- public static void apply(WireBuffer buffer,
- float topStart,
- float topEnd,
- float bottomStart,
- float bottomEnd) {
+ public static void apply(
+ WireBuffer buffer, float topStart, float topEnd, float bottomStart, float bottomEnd) {
write(buffer, OP_CODE, topStart, topEnd, bottomStart, bottomEnd);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ShapeType.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ShapeType.java
index e425b4e..0cd062e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ShapeType.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ShapeType.java
@@ -15,9 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-/**
- * Known shapes, used for modifiers (clip/background/border)
- */
+/** Known shapes, used for modifiers (clip/background/border) */
public class ShapeType {
public static int RECTANGLE = 0;
public static int CIRCLE = 1;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java
index 3f19c9b..8876720 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.Operation;
@@ -29,9 +29,7 @@
import java.util.List;
-/**
- * Apply a value change on an integer variable.
- */
+/** Apply a value change on an integer variable. */
public class ValueIntegerChangeActionOperation implements ActionOperation {
private static final int OP_CODE = Operations.VALUE_INTEGER_CHANGE_ACTION;
@@ -54,28 +52,23 @@
@Override
public void serializeToString(int indent, StringSerializer serializer) {
- serializer.append(indent, serializedName()
- + " = " + mTargetValueId + " -> " + mValue);
+ serializer.append(indent, serializedName() + " = " + mTargetValueId + " -> " + mValue);
}
@Override
- public void apply(RemoteContext context) {
- }
+ public void apply(RemoteContext context) {}
@Override
public String deepToString(String indent) {
return (indent != null ? indent : "") + toString();
}
+ @Override
+ public void write(WireBuffer buffer) {}
@Override
- public void write(WireBuffer buffer) {
-
- }
-
- @Override
- public void runAction(RemoteContext context, CoreDocument document,
- Component component, float x, float y) {
+ public void runAction(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
context.overrideInteger(mTargetValueId, mValue);
}
@@ -93,11 +86,10 @@
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", OP_CODE, "ValueIntegerChangeActionOperation")
- .description("ValueIntegerChange action. "
- + " This operation represents a value change for the given id")
+ .description(
+ "ValueIntegerChange action. "
+ + " This operation represents a value change for the given id")
.field(INT, "TARGET_VALUE_ID", "Value ID")
- .field(INT, "VALUE", "integer value to be assigned to the target")
- ;
+ .field(INT, "VALUE", "integer value to be assigned to the target");
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java
new file mode 100644
index 0000000..fb5e911
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java
@@ -0,0 +1,96 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations.layout.modifiers;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import com.android.internal.widget.remotecompose.core.CoreDocument;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.operations.layout.ActionOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+
+import java.util.List;
+
+/** Apply a value change on an integer variable. */
+public class ValueIntegerExpressionChangeActionOperation implements ActionOperation {
+ private static final int OP_CODE = Operations.VALUE_INTEGER_EXPRESSION_CHANGE_ACTION;
+
+ long mTargetValueId = -1;
+ long mValueExpressionId = -1;
+
+ public ValueIntegerExpressionChangeActionOperation(long id, long value) {
+ mTargetValueId = id;
+ mValueExpressionId = value;
+ }
+
+ @Override
+ public String toString() {
+ return "ValueIntegerExpressionChangeActionOperation(" + mTargetValueId + ")";
+ }
+
+ public String serializedName() {
+ return "VALUE_INTEGER_EXPRESSION_CHANGE";
+ }
+
+ @Override
+ public void serializeToString(int indent, StringSerializer serializer) {
+ serializer.append(
+ indent, serializedName() + " = " + mTargetValueId + " -> " + mValueExpressionId);
+ }
+
+ @Override
+ public void apply(RemoteContext context) {}
+
+ @Override
+ public String deepToString(String indent) {
+ return (indent != null ? indent : "") + toString();
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {}
+
+ @Override
+ public void runAction(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
+ document.evaluateIntExpression(mValueExpressionId, (int) mTargetValueId, context);
+ }
+
+ public static void apply(WireBuffer buffer, long valueId, long value) {
+ buffer.start(OP_CODE);
+ buffer.writeLong(valueId);
+ buffer.writeLong(value);
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ long valueId = buffer.readLong();
+ long value = buffer.readLong();
+ operations.add(new ValueIntegerExpressionChangeActionOperation(valueId, value));
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Layout Operations", OP_CODE, "ValueIntegerExpressionChangeActionOperation")
+ .description(
+ "ValueIntegerExpressionChange action. "
+ + " This operation represents a value change for the given id")
+ .field(INT, "TARGET_VALUE_ID", "Value ID")
+ .field(INT, "VALUE_ID", "id of the value to be assigned to the target");
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java
index e2542e5..a64a492 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.Operation;
@@ -29,9 +29,7 @@
import java.util.List;
-/**
- * Apply a value change on a string variable.
- */
+/** Apply a value change on a string variable. */
public class ValueStringChangeActionOperation implements ActionOperation {
private static final int OP_CODE = Operations.VALUE_STRING_CHANGE_ACTION;
@@ -62,8 +60,7 @@
}
@Override
- public void apply(RemoteContext context) {
- }
+ public void apply(RemoteContext context) {}
@Override
public String deepToString(String indent) {
@@ -71,13 +68,11 @@
}
@Override
- public void write(WireBuffer buffer) {
-
- }
+ public void write(WireBuffer buffer) {}
@Override
- public void runAction(RemoteContext context, CoreDocument document,
- Component component, float x, float y) {
+ public void runAction(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
context.overrideText(mTargetValueId, mValueId);
}
@@ -95,13 +90,14 @@
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", OP_CODE, "ValueStringChangeActionOperation")
- .description("ValueStrin gChange action. "
- + " This operation represents a String change (referenced by id) "
- + "for the given string id")
+ .description(
+ "ValueStrin gChange action. "
+ + " This operation represents a String change (referenced by id) "
+ + "for the given string id")
.field(INT, "TARGET_ID", "Target Value ID")
- .field(INT, "VALUE_ID", "Value ID to be assigned to the target "
- + "value as a string")
- ;
+ .field(
+ INT,
+ "VALUE_ID",
+ "Value ID to be assigned to the target " + "value as a string");
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
index 7fd5e51..62403b3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -25,14 +25,11 @@
import java.util.List;
-/**
- * Set the width dimension on a component
- */
+/** Set the width dimension on a component */
public class WidthModifierOperation extends DimensionModifierOperation {
private static final int OP_CODE = Operations.MODIFIER_WIDTH;
public static final String CLASS_NAME = "WidthModifierOperation";
-
public static String name() {
return CLASS_NAME;
}
@@ -82,9 +79,7 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Modifier Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
.description("define the animation")
.field(INT, "type", "")
.field(FLOAT, "value", "");
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/DebugLog.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/DebugLog.java
index 7ccf7f4..4849b12 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/DebugLog.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/DebugLog.java
@@ -17,9 +17,7 @@
import java.util.ArrayList;
-/**
- * Internal utility debug class
- */
+/** Internal utility debug class */
public class DebugLog {
public static final boolean DEBUG_LAYOUT_ON = false;
@@ -109,8 +107,12 @@
if (node instanceof LogNode) {
builder.append(indentation).append(" ").append(node.name).append("\n");
} else {
- builder.append(indentation).append("-- ").append(node.name)
- .append(" : ").append(node.endString).append("\n");
+ builder.append(indentation)
+ .append("-- ")
+ .append(node.name)
+ .append(" : ")
+ .append(node.endString)
+ .append("\n");
}
}
}
@@ -124,4 +126,3 @@
}
}
}
-
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/StringValueSupplier.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/StringValueSupplier.java
index 79ef16b..701167a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/StringValueSupplier.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/StringValueSupplier.java
@@ -15,12 +15,11 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.utils;
-/**
- * Basic interface for a lambda (used for logging)
- */
+/** Basic interface for a lambda (used for logging) */
public interface StringValueSupplier {
/**
* returns a string value
+ *
* @return a string
*/
String getString();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
index 6c8049a..e714947 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
@@ -23,9 +23,7 @@
import java.util.Arrays;
-/**
- * Paint Bundle represents a delta of changes to a paint object
- */
+/** Paint Bundle represents a delta of changes to a paint object */
public class PaintBundle {
int[] mArray = new int[200];
int[] mOutArray = null;
@@ -33,6 +31,7 @@
/**
* Apply changes to a PaintChanges interface
+ *
* @param paintContext
* @param p
*/
@@ -46,10 +45,9 @@
int cmd = mOutArray[i++];
mask = mask | (1 << (cmd - 1));
switch (cmd & 0xFFFF) {
- case TEXT_SIZE: {
+ case TEXT_SIZE:
p.setTextSize(Float.intBitsToFloat(mOutArray[i++]));
break;
- }
case TYPEFACE:
int style = (cmd >> 16);
int weight = style & 0x3ff;
@@ -59,99 +57,86 @@
p.setTypeFace(font_type, weight, italic);
break;
case COLOR_ID: // mOutArray should have already decoded it
- case COLOR: {
+ case COLOR:
p.setColor(mOutArray[i++]);
break;
- }
- case STROKE_WIDTH: {
+ case STROKE_WIDTH:
p.setStrokeWidth(Float.intBitsToFloat(mOutArray[i++]));
break;
- }
- case STROKE_MITER: {
+ case STROKE_MITER:
p.setStrokeMiter(Float.intBitsToFloat(mOutArray[i++]));
break;
- }
- case STROKE_CAP: {
+ case STROKE_CAP:
p.setStrokeCap(cmd >> 16);
break;
- }
- case STYLE: {
+ case STYLE:
p.setStyle(cmd >> 16);
break;
- }
- case SHADER: {
+ case SHADER:
p.setShader(mOutArray[i++]);
break;
- }
- case STROKE_JOIN: {
+ case STROKE_JOIN:
p.setStrokeJoin(cmd >> 16);
break;
- }
- case IMAGE_FILTER_QUALITY: {
+ case IMAGE_FILTER_QUALITY:
p.setImageFilterQuality(cmd >> 16);
break;
- }
- case BLEND_MODE: {
+ case BLEND_MODE:
p.setBlendMode(cmd >> 16);
break;
- }
- case FILTER_BITMAP: {
+ case FILTER_BITMAP:
p.setFilterBitmap(!((cmd >> 16) == 0));
break;
- }
- case GRADIENT: {
+ case GRADIENT:
i = callSetGradient(cmd, mOutArray, i, p);
break;
- }
- case COLOR_FILTER: {
+ case COLOR_FILTER_ID:
+ case COLOR_FILTER:
p.setColorFilter(mOutArray[i++], cmd >> 16);
break;
- }
- case ALPHA: {
+ case ALPHA:
p.setAlpha(Float.intBitsToFloat(mOutArray[i++]));
break;
- }
+ case CLEAR_COLOR_FILTER:
+ p.clear(0x1L << PaintBundle.COLOR_FILTER);
+ break;
}
}
-
- mask = (~mask) & PaintChanges.VALID_BITS;
-
- p.clear(mask);
}
- private String toName(int id) {
- switch (id) {
- case TEXT_SIZE:
- return "TEXT_SIZE";
- case COLOR:
- return "COLOR";
- case STROKE_WIDTH:
- return "STROKE_WIDTH";
- case STROKE_MITER:
- return "STROKE_MITER";
- case TYPEFACE:
- return "TYPEFACE";
- case STROKE_CAP:
- return "CAP";
- case STYLE:
- return "STYLE";
- case SHADER:
- return "SHADER";
- case IMAGE_FILTER_QUALITY:
- return "IMAGE_FILTER_QUALITY";
- case BLEND_MODE:
- return "BLEND_MODE";
- case FILTER_BITMAP:
- return "FILTER_BITMAP";
- case GRADIENT:
- return "GRADIENT_LINEAR";
- case ALPHA:
- return "ALPHA";
- case COLOR_FILTER:
- return "COLOR_FILTER";
- }
- return "????" + id + "????";
- }
+ // private String toName(int id) {
+ // switch (id) {
+ // case TEXT_SIZE:
+ // return "TEXT_SIZE";
+ // case COLOR:
+ // return "COLOR";
+ // case STROKE_WIDTH:
+ // return "STROKE_WIDTH";
+ // case STROKE_MITER:
+ // return "STROKE_MITER";
+ // case TYPEFACE:
+ // return "TYPEFACE";
+ // case STROKE_CAP:
+ // return "CAP";
+ // case STYLE:
+ // return "STYLE";
+ // case SHADER:
+ // return "SHADER";
+ // case IMAGE_FILTER_QUALITY:
+ // return "IMAGE_FILTER_QUALITY";
+ // case BLEND_MODE:
+ // return "BLEND_MODE";
+ // case FILTER_BITMAP:
+ // return "FILTER_BITMAP";
+ // case GRADIENT:
+ // return "GRADIENT_LINEAR";
+ // case ALPHA:
+ // return "ALPHA";
+ // case COLOR_FILTER:
+ // return "COLOR_FILTER";
+ // }
+ // return "????" + id + "????";
+ // }
private static String colorInt(int color) {
String str = "000000000000" + Integer.toHexString(color);
@@ -185,103 +170,189 @@
int cmd = mArray[i++];
int type = cmd & 0xFFFF;
switch (type) {
-
- case TEXT_SIZE: {
- ret.append(" TextSize("
- + asFloatStr(mArray[i++]));
- }
-
- break;
- case TYPEFACE: {
+ case TEXT_SIZE:
+ ret.append(" TextSize(" + asFloatStr(mArray[i++]));
+ break;
+ case TYPEFACE:
int style = (cmd >> 16);
int weight = style & 0x3ff;
boolean italic = (style >> 10) > 0;
int font_type = mArray[i++];
- ret.append(" TypeFace(" + (font_type + ", "
- + weight + ", " + italic));
- }
- break;
- case COLOR: {
+ ret.append(" TypeFace(" + (font_type + ", " + weight + ", " + italic));
+ break;
+ case COLOR:
ret.append(" Color(" + colorInt(mArray[i++]));
- }
- break;
- case COLOR_ID: {
+ break;
+ case COLOR_ID:
ret.append(" ColorId([" + mArray[i++] + "]");
- }
- break;
- case STROKE_WIDTH: {
- ret.append(" StrokeWidth("
- + (asFloatStr(mArray[i++])));
- }
- break;
- case STROKE_MITER: {
- ret.append(" StrokeMiter("
- + (asFloatStr(mArray[i++])));
- }
- break;
- case STROKE_CAP: {
- ret.append(" StrokeCap("
- + (cmd >> 16));
- }
- break;
- case STYLE: {
+ break;
+ case STROKE_WIDTH:
+ ret.append(" StrokeWidth(" + asFloatStr(mArray[i++]));
+ break;
+ case STROKE_MITER:
+ ret.append(" StrokeMiter(" + asFloatStr(mArray[i++]));
+ break;
+ case STROKE_CAP:
+ ret.append(" StrokeCap(" + (cmd >> 16));
+ break;
+ case STYLE:
ret.append(" Style(" + (cmd >> 16));
- }
- break;
- case COLOR_FILTER: {
- ret.append(" ColorFilter(color="
- + colorInt(mArray[i++])
- + ", mode=" + blendModeString(cmd >> 16));
- }
- break;
- case SHADER: {
+ break;
+ case COLOR_FILTER:
+ ret.append(
+ " ColorFilter(color="
+ + colorInt(mArray[i++])
+ + ", mode="
+ + blendModeString(cmd >> 16));
+ break;
+ case COLOR_FILTER_ID:
+ ret.append(
+ " ColorFilterID(color=["
+ + mArray[i++]
+ + "], mode="
+ + blendModeString(cmd >> 16));
+ break;
+ case CLEAR_COLOR_FILTER:
+ ret.append(" clearColorFilter");
+ break;
+ case SHADER:
ret.append(" Shader(" + mArray[i++]);
- }
- break;
- case ALPHA: {
- ret.append(" Alpha("
- + (asFloatStr(mArray[i++])));
- }
- break;
- case IMAGE_FILTER_QUALITY: {
+ break;
+ case ALPHA:
+ ret.append(" Alpha(" + asFloatStr(mArray[i++]));
+ break;
+ case IMAGE_FILTER_QUALITY:
ret.append(" ImageFilterQuality(" + (cmd >> 16));
- }
- break;
- case BLEND_MODE: {
+ break;
+ case BLEND_MODE:
ret.append(" BlendMode(" + blendModeString(cmd >> 16));
- }
- break;
- case FILTER_BITMAP: {
- ret.append(" FilterBitmap("
- + (!((cmd >> 16) == 0)));
- }
- break;
- case STROKE_JOIN: {
+ break;
+ case FILTER_BITMAP:
+ ret.append(" FilterBitmap(" + !(cmd >> 16 == 0));
+ break;
+ case STROKE_JOIN:
ret.append(" StrokeJoin(" + (cmd >> 16));
- }
- break;
- case ANTI_ALIAS: {
+ break;
+ case ANTI_ALIAS:
ret.append(" AntiAlias(" + (cmd >> 16));
- }
- break;
- case GRADIENT: {
+ break;
+ case GRADIENT:
i = callPrintGradient(cmd, mArray, i, ret);
- }
}
ret.append("),\n");
}
return ret.toString();
}
+ private void registerFloat(int iv, RemoteContext context, VariableSupport support) {
+ float v = Float.intBitsToFloat(iv);
+ if (Float.isNaN(v)) {
+ context.listensTo(Utils.idFromNan(v), support);
+ }
+ }
+
+ int callRegisterGradient(
+ int cmd, int[] array, int i, RemoteContext context, VariableSupport support) {
+ int ret = i;
+ int type = (cmd >> 16);
+ int control = array[ret++];
+ int len = 0xFF & control; // maximum 256 colors
+ int register = 0xFFFF & (control >> 16);
+ int tileMode = 0;
+ switch (type) {
+ /* see {@link #setLinearGradient} */
+ case LINEAR_GRADIENT:
+ if (len > 0) {
+
+ for (int j = 0; j < len; j++) {
+ int color = array[ret++];
+ if ((register & (1 << j)) != 0) {
+ context.listensTo(color, support);
+ }
+ }
+ }
+ len = array[ret++];
+
+ if (len > 0) {
+
+ for (int j = 0; j < len; j++) {
+ registerFloat(array[ret++], context, support);
+ }
+ }
+
+ // start x
+ registerFloat(array[ret++], context, support);
+ // start y
+ registerFloat(array[ret++], context, support);
+ // end x
+ registerFloat(array[ret++], context, support);
+ // end y
+ registerFloat(array[ret++], context, support);
+ tileMode = array[ret++];
+ break;
+ /* see {@link #setRadialGradient} */
+ case RADIAL_GRADIENT:
+ if (len > 0) {
+
+ for (int j = 0; j < len; j++) {
+ int color = array[ret++];
+ if ((register & (1 << j)) != 0) {
+ context.listensTo(color, support);
+ }
+ }
+ }
+ len = array[ret++]; // stops
+ for (int j = 0; j < len; j++) {
+ registerFloat(array[ret++], context, support);
+ }
+
+ // center x
+ registerFloat(array[ret++], context, support);
+ // center y
+ registerFloat(array[ret++], context, support);
+ // radius
+ registerFloat(array[ret++], context, support);
+
+ tileMode = array[ret++]; // tile Mode
+ break;
+ /* see {@link #setSweepGradient} */
+ case SWEEP_GRADIENT:
+ if (len > 0) {
+
+ for (int j = 0; j < len; j++) {
+ int color = array[ret++];
+ if ((register & (1 << j)) != 0) {
+ context.listensTo(color, support);
+ }
+ }
+ }
+
+ len = array[ret++]; // stops
+ for (int j = 0; j < len; j++) {
+ registerFloat(array[ret++], context, support);
+ }
+ // center x
+ registerFloat(array[ret++], context, support);
+ // center y
+ registerFloat(array[ret++], context, support);
+ break;
+ default:
+ System.out.println("error ");
+ }
+
+ return ret;
+ }
+
int callPrintGradient(int cmd, int[] array, int i, StringBuilder p) {
int ret = i;
int type = (cmd >> 16);
+ int tileMode = 0;
+ int len = array[ret++];
+ int[] colors = null;
+ String[] stops = null;
switch (type) {
-
- case 0: {
+ case 0:
p.append(" LinearGradient(\n");
- int len = array[ret++];
- int[] colors = null;
if (len > 0) {
colors = new int[len];
for (int j = 0; j < colors.length; j++) {
@@ -289,7 +360,6 @@
}
}
len = array[ret++];
- String[] stops = null;
if (len > 0) {
stops = new String[len];
for (int j = 0; j < stops.length; j++) {
@@ -305,24 +375,18 @@
p.append(" end = ");
p.append("[" + asFloatStr(array[ret++]));
p.append(", " + asFloatStr(array[ret++]) + "],\n");
- int tileMode = array[ret++];
+ tileMode = array[ret++];
p.append(" tileMode = " + tileMode + "\n ");
- }
-
- break;
- case 1: {
+ break;
+ case 1:
p.append(" RadialGradient(\n");
- int len = array[ret++];
- int[] colors = null;
if (len > 0) {
colors = new int[len];
for (int j = 0; j < colors.length; j++) {
colors[j] = array[ret++];
-
}
}
len = array[ret++];
- String[] stops = null;
if (len > 0) {
stops = new String[len];
for (int j = 0; j < stops.length; j++) {
@@ -337,24 +401,18 @@
p.append(", " + asFloatStr(array[ret++]) + "],\n");
p.append(" radius =");
p.append(" " + asFloatStr(array[ret++]) + ",\n");
- int tileMode = array[ret++];
+ tileMode = array[ret++];
p.append(" tileMode = " + tileMode + "\n ");
- }
-
- break;
- case 2: {
+ break;
+ case 2:
p.append(" SweepGradient(\n");
- int len = array[ret++];
- int[] colors = null;
if (len > 0) {
colors = new int[len];
for (int j = 0; j < colors.length; j++) {
colors[j] = array[ret++];
-
}
}
len = array[ret++];
- String[] stops = null;
if (len > 0) {
stops = new String[len];
for (int j = 0; j < stops.length; j++) {
@@ -365,13 +423,10 @@
p.append(" stops = " + Arrays.toString(stops) + ",\n");
p.append(" center = ");
p.append("[" + asFloatStr(array[ret++]));
- p.append(", "
- + asFloatStr(array[ret++]) + "],\n ");
- }
- break;
- default: {
+ p.append(", " + asFloatStr(array[ret++]) + "],\n ");
+ break;
+ default:
p.append("GRADIENT_??????!!!!");
- }
}
return ret;
@@ -381,7 +436,8 @@
int ret = i;
int gradientType = (cmd >> 16);
- int len = array[ret++];
+ int len = 0xFF & array[ret++]; // maximum 256 colors
+
int[] colors = null;
if (len > 0) {
colors = new int[len];
@@ -402,33 +458,30 @@
return ret;
}
- switch (gradientType) {
+ int tileMode = 0;
+ float centerX = 0f;
+ float centerY = 0f;
- case LINEAR_GRADIENT: {
+ switch (gradientType) {
+ case LINEAR_GRADIENT:
float startX = Float.intBitsToFloat(array[ret++]);
float startY = Float.intBitsToFloat(array[ret++]);
float endX = Float.intBitsToFloat(array[ret++]);
float endY = Float.intBitsToFloat(array[ret++]);
- int tileMode = array[ret++];
- p.setLinearGradient(colors, stops, startX,
- startY, endX, endY, tileMode);
- }
-
- break;
- case RADIAL_GRADIENT: {
- float centerX = Float.intBitsToFloat(array[ret++]);
- float centerY = Float.intBitsToFloat(array[ret++]);
+ tileMode = array[ret++];
+ p.setLinearGradient(colors, stops, startX, startY, endX, endY, tileMode);
+ break;
+ case RADIAL_GRADIENT:
+ centerX = Float.intBitsToFloat(array[ret++]);
+ centerY = Float.intBitsToFloat(array[ret++]);
float radius = Float.intBitsToFloat(array[ret++]);
- int tileMode = array[ret++];
- p.setRadialGradient(colors, stops, centerX, centerY,
- radius, tileMode);
- }
- break;
- case SWEEP_GRADIENT: {
- float centerX = Float.intBitsToFloat(array[ret++]);
- float centerY = Float.intBitsToFloat(array[ret++]);
+ tileMode = array[ret++];
+ p.setRadialGradient(colors, stops, centerX, centerY, radius, tileMode);
+ break;
+ case SWEEP_GRADIENT:
+ centerX = Float.intBitsToFloat(array[ret++]);
+ centerY = Float.intBitsToFloat(array[ret++]);
p.setSweepGradient(colors, stops, centerX, centerY);
- }
}
return ret;
@@ -453,9 +506,9 @@
mPos = len;
}
- public static final int TEXT_SIZE = 1; // float
+ public static final int TEXT_SIZE = 1; // float
- public static final int COLOR = 4; // int
+ public static final int COLOR = 4; // int
public static final int STROKE_WIDTH = 5; // float
public static final int STROKE_MITER = 6;
public static final int STROKE_CAP = 7; // int
@@ -470,7 +523,9 @@
public static final int TYPEFACE = 16;
public static final int FILTER_BITMAP = 17;
public static final int BLEND_MODE = 18;
- public static final int COLOR_ID = 19; // int
+ public static final int COLOR_ID = 19;
+ public static final int COLOR_FILTER_ID = 20;
+ public static final int CLEAR_COLOR_FILTER = 21;
public static final int BLEND_MODE_CLEAR = 0;
public static final int BLEND_MODE_SRC = 1;
@@ -524,27 +579,28 @@
/**
* sets a shader that draws a linear gradient along a line.
*
- * @param startX The x-coordinate for the start of the gradient line
- * @param startY The y-coordinate for the start of the gradient line
- * @param endX The x-coordinate for the end of the gradient line
- * @param endY The y-coordinate for the end of the gradient line
- * @param colors The sRGB colors to be distributed along the gradient line
- * @param stops May be null. The relative positions [0..1] of
- * each corresponding color in the colors array. If this is null,
- * the colors are distributed evenly along the gradient line.
+ * @param startX The x-coordinate for the start of the gradient line
+ * @param startY The y-coordinate for the start of the gradient line
+ * @param endX The x-coordinate for the end of the gradient line
+ * @param endY The y-coordinate for the end of the gradient line
+ * @param colors The sRGB colors to be distributed along the gradient line
+ * @param stops May be null. The relative positions [0..1] of each corresponding color in the
+ * colors array. If this is null, the colors are distributed evenly along the gradient line.
* @param tileMode The Shader tiling mode
*/
- public void setLinearGradient(int[] colors,
- float[] stops,
- float startX,
- float startY,
- float endX,
- float endY,
- int tileMode) {
- int startPos = mPos;
+ public void setLinearGradient(
+ int[] colors,
+ int idMask,
+ float[] stops,
+ float startX,
+ float startY,
+ float endX,
+ float endY,
+ int tileMode) {
+ // int startPos = mPos;
int len;
mArray[mPos++] = GRADIENT | (LINEAR_GRADIENT << 16);
- mArray[mPos++] = len = (colors == null) ? 0 : colors.length;
+ mArray[mPos++] = (idMask << 16) | (len = (colors == null) ? 0 : colors.length);
for (int i = 0; i < len; i++) {
mArray[mPos++] = colors[i];
}
@@ -565,20 +621,18 @@
*
* @param centerX The x-coordinate of the center
* @param centerY The y-coordinate of the center
- * @param colors The sRGB colors to be distributed around the center.
- * There must be at least 2 colors in the array.
- * @param stops May be NULL. The relative position of
- * each corresponding color in the colors array, beginning
- * with 0 and ending with 1.0. If the values are not
- * monotonic, the drawing may produce unexpected results.
- * If positions is NULL, then the colors are automatically
- * spaced evenly.
+ * @param colors The sRGB colors to be distributed around the center. There must be at least 2
+ * colors in the array.
+ * @param stops May be NULL. The relative position of each corresponding color in the colors
+ * array, beginning with 0 and ending with 1.0. If the values are not monotonic, the drawing
+ * may produce unexpected results. If positions is NULL, then the colors are automatically
+ * spaced evenly.
*/
- public void setSweepGradient(int[] colors, float[] stops, float centerX, float centerY) {
- int startPos = mPos;
+ public void setSweepGradient(
+ int[] colors, int idMask, float[] stops, float centerX, float centerY) {
int len;
mArray[mPos++] = GRADIENT | (SWEEP_GRADIENT << 16);
- mArray[mPos++] = len = (colors == null) ? 0 : colors.length;
+ mArray[mPos++] = (idMask << 16) | (len = (colors == null) ? 0 : colors.length);
for (int i = 0; i < len; i++) {
mArray[mPos++] = colors[i];
}
@@ -594,29 +648,28 @@
/**
* Sets a shader that draws a radial gradient given the center and radius.
*
- * @param centerX The x-coordinate of the center of the radius
- * @param centerY The y-coordinate of the center of the radius
- * @param radius Must be positive. The radius of the gradient.
- * @param colors The sRGB colors distributed between the center and edge
- * @param stops May be <code>null</code>.
- * Valid values are between <code>0.0f</code> and
- * <code>1.0f</code>. The relative position of each
- * corresponding color in
- * the colors array. If <code>null</code>, colors are
- * distributed evenly
- * between the center and edge of the circle.
+ * @param centerX The x-coordinate of the center of the radius
+ * @param centerY The y-coordinate of the center of the radius
+ * @param radius Must be positive. The radius of the gradient.
+ * @param colors The sRGB colors distributed between the center and edge
+ * @param stops May be <code>null</code>. Valid values are between <code>0.0f</code> and <code>
+ * 1.0f</code>. The relative position of each corresponding color in the colors array. If
+ * <code>null</code>, colors are distributed evenly between the center and edge of the
+ * circle.
* @param tileMode The Shader tiling mode
*/
- public void setRadialGradient(int[] colors,
- float[] stops,
- float centerX,
- float centerY,
- float radius,
- int tileMode) {
- int startPos = mPos;
+ public void setRadialGradient(
+ int[] colors,
+ int idMask,
+ float[] stops,
+ float centerX,
+ float centerY,
+ float radius,
+ int tileMode) {
+ // int startPos = mPos;
int len;
mArray[mPos++] = GRADIENT | (RADIAL_GRADIENT << 16);
- mArray[mPos++] = len = (colors == null) ? 0 : colors.length;
+ mArray[mPos++] = (idMask << 16) | (len = (colors == null) ? 0 : colors.length);
for (int i = 0; i < len; i++) {
mArray[mPos++] = colors[i];
}
@@ -629,14 +682,13 @@
mArray[mPos++] = Float.floatToRawIntBits(centerY);
mArray[mPos++] = Float.floatToRawIntBits(radius);
mArray[mPos++] = tileMode;
-
}
/**
* Create a color filter that uses the specified color and Porter-Duff mode.
*
* @param color The ARGB source color used with the Porter-Duff mode
- * @param mode The porter-duff mode that is applied
+ * @param mode The porter-duff mode that is applied
*/
public void setColorFilter(int color, int mode) {
mArray[mPos] = COLOR_FILTER | (mode << 16);
@@ -645,12 +697,29 @@
}
/**
+ * Create a color filter that uses the specified color and Porter-Duff mode.
+ *
+ * @param color The id source color used with the Porter-Duff mode
+ * @param mode The porter-duff mode that is applied
+ */
+ public void setColorFilterId(int color, int mode) {
+ mArray[mPos] = COLOR_FILTER_ID | (mode << 16);
+ mPos++;
+ mArray[mPos++] = color;
+ }
+
+ /** This sets the color filter to null */
+ public void clearColorFilter() {
+ mArray[mPos] = CLEAR_COLOR_FILTER;
+ mPos++;
+ }
+
+ /**
* Set the paint's text size. This value must be > 0
*
* @param size set the paint's text size in pixel units.
*/
public void setTextSize(float size) {
- int p = mPos;
mArray[mPos] = TEXT_SIZE;
mPos++;
mArray[mPos] = Float.floatToRawIntBits(size);
@@ -659,22 +728,21 @@
/**
* @param fontType 0 = default 1 = sans serif 2 = serif 3 = monospace
- * @param weight 100-1000
- * @param italic tur
+ * @param weight 100-1000
+ * @param italic tur
*/
public void setTextStyle(int fontType, int weight, boolean italic) {
- int style = (weight & 0x3FF) | (italic ? 2048 : 0); // pack the weight and italic
+ int style = (weight & 0x3FF) | (italic ? 2048 : 0); // pack the weight and italic
mArray[mPos++] = TYPEFACE | (style << 16);
mArray[mPos++] = fontType;
}
/**
- * Set the width for stroking.
- * Pass 0 to stroke in hairline mode.
- * Hairlines always draws a single pixel independent of the canvas's matrix.
+ * Set the width for stroking. Pass 0 to stroke in hairline mode. Hairlines always draws a
+ * single pixel independent of the canvas's matrix.
*
- * @param width set the paint's stroke width, used whenever the paint's
- * style is Stroke or StrokeAndFill.
+ * @param width set the paint's stroke width, used whenever the paint's style is Stroke or
+ * StrokeAndFill.
*/
public void setStrokeWidth(float width) {
mArray[mPos] = STROKE_WIDTH;
@@ -685,6 +753,7 @@
/**
* Set the Color based on Color
+ *
* @param color
*/
public void setColor(int color) {
@@ -696,6 +765,7 @@
/**
* Set the color based the R,G,B,A values
+ *
* @param r red (0 to 255)
* @param g green (0 to 255)
* @param b blue (0 to 255)
@@ -708,6 +778,7 @@
/**
* Set the color based the R,G,B,A values
+ *
* @param r red (0.0 to 1.0)
* @param g green (0.0 to 1.0)
* @param b blue (0.0 to 1.0)
@@ -719,6 +790,7 @@
/**
* Set the Color based on ID
+ *
* @param color
*/
public void setColorId(int color) {
@@ -728,12 +800,11 @@
mPos++;
}
-
/**
* Set the paint's Cap.
*
- * @param cap set the paint's line cap style, used whenever the paint's
- * style is Stroke or StrokeAndFill.
+ * @param cap set the paint's line cap style, used whenever the paint's style is Stroke or
+ * StrokeAndFill.
*/
public void setStrokeCap(int cap) {
mArray[mPos] = STROKE_CAP | (cap << 16);
@@ -742,6 +813,7 @@
/**
* Set the style STROKE and/or FILL
+ *
* @param style
*/
public void setStyle(int style) {
@@ -751,6 +823,7 @@
/**
* Set the shader id to use
+ *
* @param shaderId
*/
public void setShader(int shaderId) {
@@ -760,9 +833,7 @@
mPos++;
}
- /**
- * Set the Alpha value
- */
+ /** Set the Alpha value */
public void setAlpha(float alpha) {
mArray[mPos] = ALPHA;
mPos++;
@@ -771,11 +842,11 @@
}
/**
- * Set the paint's stroke miter value. This is used to control the behavior
- * of miter joins when the joins angle is sharp. This value must be >= 0.
+ * Set the paint's stroke miter value. This is used to control the behavior of miter joins when
+ * the joins angle is sharp. This value must be >= 0.
*
- * @param miter set the miter limit on the paint, used whenever the paint's
- * style is Stroke or StrokeAndFill.
+ * @param miter set the miter limit on the paint, used whenever the paint's style is Stroke or
+ * StrokeAndFill.
*/
public void setStrokeMiter(float miter) {
mArray[mPos] = STROKE_MITER;
@@ -787,8 +858,7 @@
/**
* Set the paint's Join.
*
- * @param join set the paint's Join, used whenever the paint's style is
- * Stroke or StrokeAndFill.
+ * @param join set the paint's Join, used whenever the paint's style is Stroke or StrokeAndFill.
*/
public void setStrokeJoin(int join) {
mArray[mPos] = STROKE_JOIN | (join << 16);
@@ -801,10 +871,8 @@
}
/**
- * Set or clear the blend mode. A blend mode defines how source pixels
- * (generated by a drawing command) are composited with the
- * destination pixels
- * (content of the render target).
+ * Set or clear the blend mode. A blend mode defines how source pixels (generated by a drawing
+ * command) are composited with the destination pixels (content of the render target).
*
* @param blendmode The blend mode to be installed in the paint
*/
@@ -814,15 +882,14 @@
}
/**
- * Helper for setFlags(), setting or clearing the ANTI_ALIAS_FLAG bit
- * AntiAliasing smooths out the edges of what is being drawn, but is has
- * no impact on the interior of the shape. See setDither() and
- * setFilterBitmap() to affect how colors are treated.
+ * Helper for setFlags(), setting or clearing the ANTI_ALIAS_FLAG bit AntiAliasing smooths out
+ * the edges of what is being drawn, but is has no impact on the interior of the shape. See
+ * setDither() and setFilterBitmap() to affect how colors are treated.
*
* @param aa true to set the antialias bit in the flags, false to clear it
*/
public void setAntiAlias(boolean aa) {
- mArray[mPos] = ANTI_ALIAS | (((aa) ? 1 : 0) << 16);
+ mArray[mPos] = ANTI_ALIAS | (aa ? 1 : 0) << 16;
mPos++;
}
@@ -903,6 +970,7 @@
/**
* Check all the floats for Nan(id) floats and call listenTo
+ *
* @param context
* @param support
*/
@@ -921,6 +989,7 @@
context.listensTo(Utils.idFromNan(v), support);
}
break;
+ case COLOR_FILTER_ID:
case COLOR_ID:
context.listensTo(mArray[i++], support);
break;
@@ -940,16 +1009,15 @@
case ANTI_ALIAS:
break;
- case GRADIENT: {
- // TODO gradients should be handled correctly
- i = callPrintGradient(cmd, mArray, i, new StringBuilder());
- }
+ case GRADIENT:
+ i = callRegisterGradient(cmd, mArray, i, context, support);
}
}
}
/**
* Update variables if any are float ids
+ *
* @param context
*/
public void updateVariables(RemoteContext context) {
@@ -970,6 +1038,7 @@
mOutArray[i] = fixFloatVar(mArray[i], context);
i++;
break;
+ case COLOR_FILTER_ID:
case COLOR_ID:
mOutArray[i] = fixColor(mArray[i], context);
i++;
@@ -987,12 +1056,12 @@
case IMAGE_FILTER_QUALITY:
case BLEND_MODE:
case ANTI_ALIAS:
+ case CLEAR_COLOR_FILTER:
break;
- case GRADIENT: {
+ case GRADIENT:
// TODO gradients should be handled correctly
i = updateFloatsInGradient(cmd, mOutArray, mArray, i, context);
- }
}
}
}
@@ -1011,21 +1080,25 @@
return n;
}
- int updateFloatsInGradient(int cmd, int[] out, int[] array,
- int i,
- RemoteContext context) {
+ int updateFloatsInGradient(int cmd, int[] out, int[] array, int i, RemoteContext context) {
int ret = i;
int type = (cmd >> 16);
+ int control = array[ret++];
+ int len = 0xFF & control; // maximum 256 colors
+ int register = 0xFFFF & (control >> 16);
switch (type) {
- case 0: {
- int len = array[ret++];
+ case LINEAR_GRADIENT:
if (len > 0) {
+
for (int j = 0; j < len; j++) {
+ int color = array[ret];
+ if ((register & (1 << j)) != 0) {
+ out[ret] = fixColor(color, context);
+ }
ret++;
}
}
len = array[ret++];
-
if (len > 0) {
for (int j = 0; j < len; j++) {
out[ret] = fixFloatVar(array[ret], context);
@@ -1044,14 +1117,16 @@
out[ret] = fixFloatVar(array[ret], context);
ret++;
ret++; // tileMode
- }
-
- break;
- case 1: {
+ break;
+ case RADIAL_GRADIENT:
// RadialGradient
- int len = array[ret++];
if (len > 0) {
+
for (int j = 0; j < len; j++) {
+ int color = array[ret];
+ if ((register & (1 << j)) != 0) {
+ out[ret] = fixColor(color, context);
+ }
ret++;
}
}
@@ -1063,7 +1138,6 @@
}
}
-
// center
out[ret] = fixFloatVar(array[ret], context);
ret++;
@@ -1073,19 +1147,17 @@
out[ret] = fixFloatVar(array[ret], context);
ret++;
ret++; // tileMode
-
- }
-
- break;
- case 2: {
+ break;
+ case SWEEP_GRADIENT:
// SweepGradient
- int len = array[ret++];
- int[] colors = null;
if (len > 0) {
- colors = new int[len];
- for (int j = 0; j < colors.length; j++) {
- colors[j] = array[ret++];
+ for (int j = 0; j < len; j++) {
+ int color = array[ret];
+ if ((register & (1 << j)) != 0) {
+ out[ret] = fixColor(color, context);
+ }
+ ret++;
}
}
len = array[ret++];
@@ -1103,14 +1175,12 @@
ret++;
out[ret] = fixFloatVar(array[ret], context);
ret++;
- }
- break;
- default: {
+
+ break;
+ default:
System.err.println("gradient type unknown");
- }
}
return ret;
}
-
-}
\ No newline at end of file
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java
index 28fe63a..e2402be 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java
@@ -18,112 +18,73 @@
public class PaintChangeAdapter implements PaintChanges {
@Override
- public void setTextSize(float size) {
-
- }
+ public void setTextSize(float size) {}
@Override
- public void setTypeFace(int fontType, int weight, boolean italic) {
-
- }
+ public void setTypeFace(int fontType, int weight, boolean italic) {}
@Override
- public void setStrokeWidth(float width) {
-
- }
+ public void setStrokeWidth(float width) {}
@Override
- public void setColor(int color) {
-
- }
+ public void setColor(int color) {}
@Override
- public void setStrokeCap(int cap) {
-
- }
+ public void setStrokeCap(int cap) {}
@Override
- public void setStyle(int style) {
-
- }
+ public void setStyle(int style) {}
@Override
- public void setShader(int shader) {
-
- }
+ public void setShader(int shader) {}
@Override
- public void setImageFilterQuality(int quality) {
-
- }
+ public void setImageFilterQuality(int quality) {}
@Override
- public void setAlpha(float a) {
-
- }
+ public void setAlpha(float a) {}
@Override
- public void setStrokeMiter(float miter) {
-
- }
+ public void setStrokeMiter(float miter) {}
@Override
- public void setStrokeJoin(int join) {
-
- }
+ public void setStrokeJoin(int join) {}
@Override
- public void setFilterBitmap(boolean filter) {
-
- }
+ public void setFilterBitmap(boolean filter) {}
@Override
- public void setBlendMode(int blendmode) {
-
- }
+ public void setBlendMode(int blendmode) {}
@Override
- public void setAntiAlias(boolean aa) {
-
- }
+ public void setAntiAlias(boolean aa) {}
@Override
- public void clear(long mask) {
-
- }
+ public void clear(long mask) {}
@Override
- public void setLinearGradient(int[] colorsArray,
- float[] stopsArray,
- float startX,
- float startY,
- float endX,
- float endY,
- int tileMode) {
-
- }
+ public void setLinearGradient(
+ int[] colorsArray,
+ float[] stopsArray,
+ float startX,
+ float startY,
+ float endX,
+ float endY,
+ int tileMode) {}
@Override
- public void setRadialGradient(int[] colorsArray,
- float[] stopsArray,
- float centerX,
- float centerY,
- float radius,
- int tileMode) {
-
- }
+ public void setRadialGradient(
+ int[] colorsArray,
+ float[] stopsArray,
+ float centerX,
+ float centerY,
+ float radius,
+ int tileMode) {}
@Override
- public void setSweepGradient(int[] colorsArray,
- float[] stopsArray,
- float centerX,
- float centerY) {
-
- }
+ public void setSweepGradient(
+ int[] colorsArray, float[] stopsArray, float centerX, float centerY) {}
@Override
- public void setColorFilter(int color, int mode) {
-
- }
-
+ public void setColorFilter(int color, int mode) {}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java
index d5dc388..486d763 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java
@@ -15,10 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.paint;
-/**
- * Interface to a paint object
- * For more details see Android Paint
- */
+/** Interface to a paint object For more details see Android Paint */
public interface PaintChanges {
// MASK to be set/cleared
@@ -30,8 +27,7 @@
int CLEAR_CAP = 1 << (PaintBundle.STROKE_CAP - 1);
int CLEAR_STYLE = 1 << (PaintBundle.STYLE - 1);
int CLEAR_SHADER = 1 << (PaintBundle.SHADER - 1);
- int CLEAR_IMAGE_FILTER_QUALITY =
- 1 << (PaintBundle.IMAGE_FILTER_QUALITY - 1);
+ int CLEAR_IMAGE_FILTER_QUALITY = 1 << (PaintBundle.IMAGE_FILTER_QUALITY - 1);
int CLEAR_RADIENT = 1 << (PaintBundle.GRADIENT - 1);
int CLEAR_ALPHA = 1 << (PaintBundle.ALPHA - 1);
int CLEAR_COLOR_FILTER = 1 << (PaintBundle.COLOR_FILTER - 1);
@@ -39,91 +35,105 @@
/**
* Set the size of text
+ *
* @param size
*/
void setTextSize(float size);
/**
* Set the width of lines
+ *
* @param width
*/
void setStrokeWidth(float width);
/**
* Set the color to use
+ *
* @param color
*/
void setColor(int color);
/**
* Set the Stroke Cap
+ *
* @param cap
*/
void setStrokeCap(int cap);
/**
* Set the Stroke style FILL and/or STROKE
+ *
* @param style
*/
void setStyle(int style);
/**
* Set the id of the shader to use
+ *
* @param shader
*/
void setShader(int shader);
/**
* Set the way image is interpolated
+ *
* @param quality
*/
void setImageFilterQuality(int quality);
/**
* Set the alpha to draw under
+ *
* @param a
*/
void setAlpha(float a);
/**
* Set the Stroke Miter
+ *
* @param miter
*/
void setStrokeMiter(float miter);
/**
* Set the Stroke Join
+ *
* @param join
*/
void setStrokeJoin(int join);
/**
* Should bitmaps be interpolated
+ *
* @param filter
*/
void setFilterBitmap(boolean filter);
/**
* Set the blend mode can be porterduff + others
+ *
* @param mode
*/
void setBlendMode(int mode);
/**
- * Set the AntiAlias. Typically true
- * Set to off when you need pixilated look (e.g. QR codes)
+ * Set the AntiAlias. Typically true Set to off when you need pixilated look (e.g. QR codes)
+ *
* @param aa
*/
void setAntiAlias(boolean aa);
/**
* Clear some sub set of the settings
+ *
* @param mask
*/
void clear(long mask);
/**
* Set a linear gradient fill
+ *
* @param colorsArray
* @param stopsArray
* @param startX
@@ -139,11 +149,11 @@
float startY,
float endX,
float endY,
- int tileMode
- );
+ int tileMode);
/**
* Set a radial gradient fill
+ *
* @param colorsArray
* @param stopsArray
* @param centerX
@@ -157,36 +167,32 @@
float centerX,
float centerY,
float radius,
- int tileMode
- );
+ int tileMode);
/**
* Set a sweep gradient fill
+ *
* @param colorsArray
* @param stopsArray
* @param centerX
* @param centerY
*/
- void setSweepGradient(
- int[] colorsArray,
- float[] stopsArray,
- float centerX,
- float centerY
- );
+ void setSweepGradient(int[] colorsArray, float[] stopsArray, float centerX, float centerY);
/**
* Set Color filter mod
+ *
* @param color
* @param mode
*/
void setColorFilter(int color, int mode);
/**
- * Set TypeFace 0,1,2
- * TODO above should point to a string to be decoded
+ * Set TypeFace 0,1,2 TODO above should point to a string to be decoded
+ *
* @param fontType
* @param weight
* @param italic
*/
void setTypeFace(int fontType, int weight, boolean italic);
-}
\ No newline at end of file
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/Painter.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/Painter.java
index ada3757..9a3cd54 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/Painter.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/Painter.java
@@ -15,16 +15,11 @@
*/
package com.android.internal.widget.remotecompose.core.operations.paint;
-
-/**
- * Provides a Builder pattern for a PaintBundle
- */
+/** Provides a Builder pattern for a PaintBundle */
class Painter {
PaintBundle mPaint;
- /**
- * Write the paint to the buffer
- */
+ /** Write the paint to the buffer */
public PaintBundle commit() {
return mPaint;
}
@@ -47,8 +42,7 @@
/**
* Set the paint's Join.
*
- * @param join set the paint's Join, used whenever the paint's style is
- * Stroke or StrokeAndFill.
+ * @param join set the paint's Join, used whenever the paint's style is Stroke or StrokeAndFill.
*/
public Painter setStrokeJoin(int join) {
mPaint.setStrokeJoin(join);
@@ -56,12 +50,11 @@
}
/**
- * Set the width for stroking. Pass 0 to stroke in hairline mode.
- * Hairlines always draws a single
- * pixel independent of the canvas's matrix.
+ * Set the width for stroking. Pass 0 to stroke in hairline mode. Hairlines always draws a
+ * single pixel independent of the canvas's matrix.
*
- * @param width set the paint's stroke width, used whenever the paint's
- * style is Stroke or StrokeAndFill.
+ * @param width set the paint's stroke width, used whenever the paint's style is Stroke or
+ * StrokeAndFill.
*/
public Painter setStrokeWidth(float width) {
mPaint.setStrokeWidth(width);
@@ -69,8 +62,8 @@
}
/**
- * Set the paint's style, used for controlling how primitives' geometries
- * are interpreted (except for drawBitmap, which always assumes Fill).
+ * Set the paint's style, used for controlling how primitives' geometries are interpreted
+ * (except for drawBitmap, which always assumes Fill).
*
* @param style The new style to set in the paint
*/
@@ -82,8 +75,8 @@
/**
* Set the paint's Cap.
*
- * @param cap set the paint's line cap style, used whenever the paint's
- * style is Stroke or StrokeAndFill.
+ * @param cap set the paint's line cap style, used whenever the paint's style is Stroke or
+ * StrokeAndFill.
*/
public Painter setStrokeCap(int cap) {
mPaint.setStrokeCap(cap);
@@ -91,11 +84,11 @@
}
/**
- * Set the paint's stroke miter value. This is used to control the behavior
- * of miter joins when the joins angle is sharp. This value must be >= 0.
+ * Set the paint's stroke miter value. This is used to control the behavior of miter joins when
+ * the joins angle is sharp. This value must be >= 0.
*
- * @param miter set the miter limit on the paint, used whenever the paint's
- * style is Stroke or StrokeAndFill.
+ * @param miter set the miter limit on the paint, used whenever the paint's style is Stroke or
+ * StrokeAndFill.
*/
public Painter setStrokeMiter(float miter) {
mPaint.setStrokeMiter(miter);
@@ -103,9 +96,8 @@
}
/**
- * Helper to setColor(), that only assigns the color's alpha value,
- * leaving its r,g,b values unchanged. Results are undefined if the alpha
- * value is outside of the range [0..1.0]
+ * Helper to setColor(), that only assigns the color's alpha value, leaving its r,g,b values
+ * unchanged. Results are undefined if the alpha value is outside of the range [0..1.0]
*
* @param alpha set the alpha component [0..1.0] of the paint's color.
*/
@@ -117,9 +109,8 @@
/**
* Create a color filter that uses the specified color and Porter-Duff mode.
*
- * @param color The ARGB source color used with the specified Porter-Duff
- * mode
- * @param mode The porter-duff mode that is applied
+ * @param color The ARGB source color used with the specified Porter-Duff mode
+ * @param mode The porter-duff mode that is applied
*/
public Painter setPorterDuffColorFilter(int color, int mode) {
mPaint.setColorFilter(color, mode);
@@ -129,17 +120,15 @@
/**
* sets a shader that draws a linear gradient along a line.
*
- * @param startX The x-coordinate for the start of the gradient line
- * @param startY The y-coordinate for the start of the gradient line
- * @param endX The x-coordinate for the end of the gradient line
- * @param endY The y-coordinate for the end of the gradient line
- * @param colors The sRGB colors to be distributed along the gradient
- * line
- * @param positions May be null. The relative positions [0..1] of each
- * corresponding color in the colors array. If this is null,
- * the colors are distributed evenly along the gradient
- * line.
- * @param tileMode The Shader tiling mode
+ * @param startX The x-coordinate for the start of the gradient line
+ * @param startY The y-coordinate for the start of the gradient line
+ * @param endX The x-coordinate for the end of the gradient line
+ * @param endY The y-coordinate for the end of the gradient line
+ * @param colors The sRGB colors to be distributed along the gradient line
+ * @param positions May be null. The relative positions [0..1] of each corresponding color in
+ * the colors array. If this is null, the colors are distributed evenly along the gradient
+ * line.
+ * @param tileMode The Shader tiling mode
*/
public Painter setLinearGradient(
float startX,
@@ -148,29 +137,23 @@
float endY,
int[] colors,
float[] positions,
- int tileMode
- ) {
- mPaint.setLinearGradient(colors, positions, startX,
- startY, endX, endY, tileMode);
+ int tileMode) {
+ mPaint.setLinearGradient(colors, 0, positions, startX, startY, endX, endY, tileMode);
return this;
}
/**
* Sets a shader that draws a radial gradient given the center and radius.
*
- * @param centerX The x-coordinate of the center of the radius
- * @param centerY The y-coordinate of the center of the radius
- * @param radius Must be positive. The radius of the circle for this
- * gradient.
- * @param colors The sRGB colors to be distributed between the center
- * and edge of the circle
- * @param positions May be <code>null</code>. Valid values are between
- * <code>0.0f</code> and
- * <code>1.0f</code>. The relative position of each
- * corresponding color in the colors array. If
- * <code>null</code>, colors are distributed evenly
- * between the center and edge of the circle.
- * @param tileMode The Shader tiling mode
+ * @param centerX The x-coordinate of the center of the radius
+ * @param centerY The y-coordinate of the center of the radius
+ * @param radius Must be positive. The radius of the circle for this gradient.
+ * @param colors The sRGB colors to be distributed between the center and edge of the circle
+ * @param positions May be <code>null</code>. Valid values are between <code>0.0f</code> and
+ * <code>1.0f</code>. The relative position of each corresponding color in the colors array.
+ * If <code>null</code>, colors are distributed evenly between the center and edge of the
+ * circle.
+ * @param tileMode The Shader tiling mode
*/
public Painter setRadialGradient(
float centerX,
@@ -178,33 +161,25 @@
float radius,
int[] colors,
float[] positions,
- int tileMode
- ) {
- mPaint.setRadialGradient(colors, positions, centerX,
- centerY, radius, tileMode);
+ int tileMode) {
+ mPaint.setRadialGradient(colors, 0, positions, centerX, centerY, radius, tileMode);
return this;
}
/**
* Set a shader that draws a sweep gradient around a center point.
*
- * @param centerX The x-coordinate of the center
- * @param centerY The y-coordinate of the center
- * @param colors The sRGB colors to be distributed between around the
- * center. There must be at least 2 colors in the array.
- * @param positions May be NULL. The relative position of each corresponding
- * color in the colors array, beginning with 0 and ending
- * with 1.0. If the values are not monotonic, the drawing
- * may produce unexpected results. If positions is NULL,
- * then the colors are automatically spaced evenly.
+ * @param centerX The x-coordinate of the center
+ * @param centerY The y-coordinate of the center
+ * @param colors The sRGB colors to be distributed between around the center. There must be at
+ * least 2 colors in the array.
+ * @param positions May be NULL. The relative position of each corresponding color in the colors
+ * array, beginning with 0 and ending with 1.0. If the values are not monotonic, the drawing
+ * may produce unexpected results. If positions is NULL, then the colors are automatically
+ * spaced evenly.
*/
- public Painter setSweepGradient(
- float centerX,
- float centerY,
- int[] colors,
- float[] positions
- ) {
- mPaint.setSweepGradient(colors, positions, centerX, centerY);
+ public Painter setSweepGradient(float centerX, float centerY, int[] colors, float[] positions) {
+ mPaint.setSweepGradient(colors, 0, positions, centerX, centerY);
return this;
}
@@ -219,10 +194,11 @@
}
/**
- * sets a typeface object that best matches the specified existing
- * typeface and the specified weight and italic style
+ * sets a typeface object that best matches the specified existing typeface and the specified
+ * weight and italic style
*
- * <p>Below are numerical values and corresponding common weight names.</p>
+ * <p>Below are numerical values and corresponding common weight names.
+ *
* <table> <thead>
* <tr><th>Value</th><th>Common weight name</th></tr> </thead> <tbody>
* <tr><td>100</td><td>Thin</td></tr>
@@ -236,25 +212,21 @@
* <tr><td>900</td><td>Black</td></tr> </tbody> </table>
*
* @param fontType 0 = default 1 = sans serif 2 = serif 3 = monospace
- * @param weight The desired weight to be drawn.
- * @param italic {@code true} if italic style is desired to be drawn.
- * Otherwise, {@code false}
+ * @param weight The desired weight to be drawn.
+ * @param italic {@code true} if italic style is desired to be drawn. Otherwise, {@code false}
*/
public Painter setTypeface(int fontType, int weight, boolean italic) {
mPaint.setTextStyle(fontType, weight, italic);
return this;
}
-
public Painter setFilterBitmap(boolean filter) {
mPaint.setFilterBitmap(filter);
return this;
}
-
public Painter setShader(int id) {
mPaint.setShader(id);
return this;
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
index 5b295eb..1d673c4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
@@ -15,9 +15,6 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities;
-import static com.android.internal.widget.remotecompose.core.operations.utilities.NanMap.ID_REGION_ARRAY;
-import static com.android.internal.widget.remotecompose.core.operations.utilities.NanMap.ID_REGION_MASK;
-
/**
* high performance floating point expression evaluator used in animation
*/
@@ -59,7 +56,6 @@
public static final float RAD = asNan(OFFSET + 30);
public static final float CEIL = asNan(OFFSET + 31);
-
// Array ops
public static final float A_DEREF = asNan(OFFSET + 32);
public static final float A_MAX = asNan(OFFSET + 33);
@@ -69,14 +65,13 @@
public static final float A_LEN = asNan(OFFSET + 37);
public static final int LAST_OP = OFFSET + 37;
-
public static final float VAR1 = asNan(OFFSET + 38);
public static final float VAR2 = asNan(OFFSET + 39);
// TODO CLAMP, CBRT, DEG, RAD, EXPM1, CEIL, FLOOR
- private static final float FP_PI = (float) Math.PI;
- private static final float FP_TO_RAD = 57.29577951f; // 180/PI
- private static final float FP_TO_DEG = 0.01745329252f; // 180/PI
+ // private static final float FP_PI = (float) Math.PI;
+ private static final float FP_TO_RAD = 57.29578f; // 180/PI
+ private static final float FP_TO_DEG = 0.017453292f; // 180/PI
float[] mStack;
float[] mLocalStack = new float[128];
@@ -106,20 +101,13 @@
int eval(int sp);
}
-
/**
- * Evaluate a float expression
- * This system works by processing an Array of float (float[])
- * in reverse polish notation (rpn)
- * Within that array some floats are commands
- * they are encoded within an NaN.
- * After processing the array the last item on the array is returned.
- * The system supports variables allowing expressions like.
- * sin(sqrt(x*x+y*y))/sqrt(x*x+y*y)
- * Where x & y are passe as parameters
- * Examples:
- * (1+2) (1, 2, ADD) adds two numbers returns 3
- * eval(new float[]{ Var1, Var * }
+ * Evaluate a float expression This system works by processing an Array of float (float[]) in
+ * reverse polish notation (rpn) Within that array some floats are commands they are encoded
+ * within an NaN. After processing the array the last item on the array is returned. The system
+ * supports variables allowing expressions like. sin(sqrt(x*x+y*y))/sqrt(x*x+y*y) Where x & y
+ * are passe as parameters Examples: (1+2) (1, 2, ADD) adds two numbers returns 3 eval(new
+ * float[]{ Var1, Var * }
*
* @param exp
* @param var
@@ -140,7 +128,6 @@
return mStack[sp];
}
-
/**
* Evaluate a float expression
*
@@ -149,8 +136,9 @@
* @param var
* @return
*/
- public float eval(CollectionsAccess ca, float[] exp, float... var) {
- mStack = exp;
+ public float eval(CollectionsAccess ca, float[] exp, int len, float... var) {
+ System.arraycopy(exp, 0, mLocalStack, 0, len);
+ mStack = mLocalStack;
mVar = var;
mCollectionsAccess = ca;
int sp = -1;
@@ -159,7 +147,7 @@
float v = mStack[i];
if (Float.isNaN(v)) {
int id = fromNaN(v);
- if ((id & ID_REGION_MASK) != ID_REGION_ARRAY) {
+ if ((id & NanMap.ID_REGION_MASK) != NanMap.ID_REGION_ARRAY) {
sp = mOps[id - OFFSET].eval(sp);
} else {
mStack[++sp] = v;
@@ -171,6 +159,34 @@
return mStack[sp];
}
+ /**
+ * Evaluate a float expression
+ *
+ * @param ca
+ * @param exp
+ * @return
+ */
+ public float eval(CollectionsAccess ca, float[] exp, int len) {
+ System.arraycopy(exp, 0, mLocalStack, 0, len);
+ mStack = mLocalStack;
+ mCollectionsAccess = ca;
+ int sp = -1;
+
+ for (int i = 0; i < len; i++) {
+ float v = mStack[i];
+ if (Float.isNaN(v)) {
+ int id = fromNaN(v);
+ if ((id & NanMap.ID_REGION_MASK) != NanMap.ID_REGION_ARRAY) {
+ sp = mOps[id - OFFSET].eval(sp);
+ } else {
+ mStack[++sp] = v;
+ }
+ } else {
+ mStack[++sp] = v;
+ }
+ }
+ return mStack[sp];
+ }
private int dereference(CollectionsAccess ca, int id, int sp) {
mStack[sp] = ca.getFloatValue(id, (int) (mStack[sp]));
@@ -238,11 +254,11 @@
mStack[sp - 1] = mStack[sp - 1] * mStack[sp];
return sp - 1;
},
- (sp) -> { // DIV
+ (sp) -> { // DIV
mStack[sp - 1] = mStack[sp - 1] / mStack[sp];
return sp - 1;
},
- (sp) -> { // MOD
+ (sp) -> { // MOD
mStack[sp - 1] = mStack[sp - 1] % mStack[sp];
return sp - 1;
},
@@ -327,13 +343,11 @@
return sp - 2;
},
(sp) -> { // Ternary conditional
- mStack[sp - 2] = (mStack[sp] > 0)
- ? mStack[sp - 1] : mStack[sp - 2];
+ mStack[sp - 2] = (mStack[sp] > 0) ? mStack[sp - 1] : mStack[sp - 2];
return sp - 2;
},
(sp) -> { // CLAMP(min,max, val)
- mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]),
- mStack[sp - 1]);
+ mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]), mStack[sp - 1]);
return sp - 2;
},
(sp) -> { // CBRT cuberoot
@@ -354,7 +368,7 @@
},
(sp) -> { // A_DEREF
int id = fromNaN(mStack[sp]);
- mStack[sp] = mCollectionsAccess.getFloatValue(id, (int) (mStack[sp - 1]));
+ mStack[sp] = mCollectionsAccess.getFloatValue(id, (int) mStack[sp - 1]);
return sp - 1;
},
(sp) -> { // A_MAX
@@ -399,11 +413,9 @@
},
(sp) -> { // A_LEN
int id = fromNaN(mStack[sp]);
- mStack[sp] = mCollectionsAccess.getFloatsLength(id);
+ mStack[sp] = mCollectionsAccess.getListLength(id);
return sp;
},
-
-
(sp) -> { // first var =
mStack[sp] = mVar[0];
return sp;
@@ -492,7 +504,8 @@
s.append(toMathName(v));
} else {
int id = fromNaN(v);
- String idString = (id > ID_REGION_ARRAY) ? ("A_" + (id & 0xFFFFF)) : "" + id;
+ String idString =
+ (id > NanMap.ID_REGION_ARRAY) ? ("A_" + (id & 0xFFFFF)) : "" + id;
s.append("[");
s.append(idString);
s.append("]");
@@ -513,7 +526,7 @@
}
static String toString(float[] exp, int sp) {
- String[] str = new String[exp.length];
+ // String[] str = new String[exp.length];
if (Float.isNaN(exp[sp])) {
int id = fromNaN(exp[sp]) - OFFSET;
switch (NO_OF_OPS[id]) {
@@ -523,24 +536,38 @@
return sNames.get(id) + "(" + toString(exp, sp + 1) + ") ";
case 2:
if (infix(id)) {
- return "(" + toString(exp, sp + 1)
- + sNames.get(id) + " "
- + toString(exp, sp + 2) + ") ";
+ return "("
+ + toString(exp, sp + 1)
+ + sNames.get(id)
+ + " "
+ + toString(exp, sp + 2)
+ + ") ";
} else {
- return sNames.get(id) + "("
- + toString(exp, sp + 1) + ", "
- + toString(exp, sp + 2) + ")";
+ return sNames.get(id)
+ + "("
+ + toString(exp, sp + 1)
+ + ", "
+ + toString(exp, sp + 2)
+ + ")";
}
case 3:
if (infix(id)) {
- return "((" + toString(exp, sp + 1) + ") ? "
- + toString(exp, sp + 2) + ":"
- + toString(exp, sp + 3) + ")";
+ return "(("
+ + toString(exp, sp + 1)
+ + ") ? "
+ + toString(exp, sp + 2)
+ + ":"
+ + toString(exp, sp + 3)
+ + ")";
} else {
return sNames.get(id)
- + "(" + toString(exp, sp + 1)
- + ", " + toString(exp, sp + 2)
- + ", " + toString(exp, sp + 3) + ")";
+ + "("
+ + toString(exp, sp + 1)
+ + ", "
+ + toString(exp, sp + 2)
+ + ", "
+ + toString(exp, sp + 3)
+ + ")";
}
}
}
@@ -549,12 +576,40 @@
static final int[] NO_OF_OPS = {
-1, // no op
- 2, 2, 2, 2, 2, // + - * / %
- 2, 2, 2, // min max, power
- 1, 1, 1, 1, 1, 1, 1, 1, //sqrt,abs,CopySign,exp,floor,log,ln
- 1, 1, 1, 1, 1, 1, 1, 2, // round,sin,cos,tan,asin,acos,atan,atan2
- 3, 3, 3, 1, 1, 1, 1,
- 0, 0, 0 // mad, ?:,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2, // + - * / %
+ 2,
+ 2,
+ 2, // min max, power
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1, // sqrt,abs,CopySign,exp,floor,log,ln
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2, // round,sin,cos,tan,asin,acos,atan,atan2
+ 3,
+ 3,
+ 3,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0 // mad, ?:,
// a[0],a[1],a[2]
};
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ArrayAccess.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ArrayAccess.java
index 4c7cc38..eb5e482 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ArrayAccess.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ArrayAccess.java
@@ -15,10 +15,21 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities;
+/**
+ * Support a standardized interface to commands that contain arrays All commands that implement
+ * array access will be collected in a map in the state TODO refactor to DataAccess,
+ * FloatArrayAccess, ListAccess, MapAccess
+ */
public interface ArrayAccess {
float getFloatValue(int index);
+
+ default int getId(int index) {
+ return 0;
+ }
+
float[] getFloats();
- int getFloatsLength();
+
+ int getLength();
default int getIntValue(int index) {
return (int) getFloatValue(index);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java
index 9e53126..0128253 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java
@@ -15,12 +15,20 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities;
+/**
+ * interface to allow expressions to access collections Todo define a convention for when access is
+ * unavailable
+ */
public interface CollectionsAccess {
float getFloatValue(int id, int index);
- float[] getFloats(int id);
- int getFloatsLength(int id);
- default int getIntValue(int id, int index) {
- return (int) getFloatValue(id, index);
+ float[] getFloats(int id);
+
+ int getListLength(int id);
+
+ int getId(int listId, int index);
+
+ default int getIntValue(int listId, int index) {
+ return (int) getFloatValue(listId, index);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java
index fb96cef..937a25d2 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java
@@ -16,21 +16,14 @@
package com.android.internal.widget.remotecompose.core.operations.utilities;
/**
- * These are tools to use long Color as variables
- * long colors are stored a 0xXXXXXXXX XXXXXX??
- * in SRGB the colors are stored 0xAARRGGBB,00000000
- * SRGB color sapce is color space 0
- * Our Color will use color float with a
- * Current android supports
- * SRGB, LINEAR_SRGB, EXTENDED_SRGB, LINEAR_EXTENDED_SRGB, BT709, BT2020,
- * DCI_P3, DISPLAY_P3, NTSC_1953, SMPTE_C, ADOBE_RGB, PRO_PHOTO_RGB, ACES,
- * ACESCG, CIE_XYZ, CIE_LAB, BT2020_HLG, BT2020_PQ 0..17 respectively
+ * These are tools to use long Color as variables long colors are stored a 0xXXXXXXXX XXXXXX?? in
+ * SRGB the colors are stored 0xAARRGGBB,00000000 SRGB color sapce is color space 0 Our Color will
+ * use color float with a Current android supports SRGB, LINEAR_SRGB, EXTENDED_SRGB,
+ * LINEAR_EXTENDED_SRGB, BT709, BT2020, DCI_P3, DISPLAY_P3, NTSC_1953, SMPTE_C, ADOBE_RGB,
+ * PRO_PHOTO_RGB, ACES, ACESCG, CIE_XYZ, CIE_LAB, BT2020_HLG, BT2020_PQ 0..17 respectively
*
- * Our color space will be 62 (MAX_ID-1). (0x3E)
- * Storing the default value in SRGB format and having the
- * id of the color between the ARGB values and the 62 i.e.
- * 0xAARRGGBB 00 00 00 3E
- *
+ * <p>Our color space will be 62 (MAX_ID-1). (0x3E) Storing the default value in SRGB format and
+ * having the id of the color between the ARGB values and the 62 i.e. 0xAARRGGBB 00 00 00 3E
*/
public class ColorUtils {
public static int RC_COLOR = 62;
@@ -53,6 +46,7 @@
/**
* get default color from long color
+ *
* @param color
* @return
*/
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/DataMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/DataMap.java
new file mode 100644
index 0000000..24f17d7
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/DataMap.java
@@ -0,0 +1,46 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations.utilities;
+
+public class DataMap {
+ public String[] mNames;
+ public int[] mIds;
+ public byte[] mTypes;
+
+ public DataMap(String[] names, byte[] types, int[] ids) {
+ mNames = names;
+ mTypes = types;
+ mIds = ids;
+ }
+
+ public int getPos(String str) {
+ for (int i = 0; i < mNames.length; i++) {
+ String name = mNames[i];
+ if (str.equals(name)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public byte getType(int pos) {
+ return mTypes[pos];
+ }
+
+ public int getId(int pos) {
+ return mIds[pos];
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ImageScaling.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ImageScaling.java
new file mode 100644
index 0000000..00c87c1
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ImageScaling.java
@@ -0,0 +1,226 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations.utilities;
+
+/** Implement the scaling logic for Compose Image or ImageView */
+public class ImageScaling {
+
+ private static final boolean DEBUG = false;
+
+ public static final int SCALE_NONE = 0;
+ public static final int SCALE_INSIDE = 1;
+ public static final int SCALE_FILL_WIDTH = 2;
+ public static final int SCALE_FILL_HEIGHT = 3;
+ public static final int SCALE_FIT = 4;
+ public static final int SCALE_CROP = 5;
+ public static final int SCALE_FILL_BOUNDS = 6;
+ public static final int SCALE_FIXED_SCALE = 7;
+
+ private float mSrcLeft;
+ private float mSrcTop;
+ private float mSrcRight;
+ private float mSrcBottom;
+ private float mDstLeft;
+ private float mDstTop;
+ private float mDstRight;
+ private float mDstBottom;
+ private float mScaleFactor;
+ private int mScaleType;
+
+ public float mFinalDstLeft;
+ public float mFinalDstTop;
+ public float mFinalDstRight;
+ public float mFinalDstBottom;
+
+ public ImageScaling() {}
+
+ public ImageScaling(
+ float srcLeft,
+ float srcTop,
+ float srcRight,
+ float srcBottom,
+ float dstLeft,
+ float dstTop,
+ float dstRight,
+ float dstBottom,
+ int type,
+ float scale) {
+
+ mSrcLeft = srcLeft;
+ mSrcTop = srcTop;
+ mSrcRight = srcRight;
+ mSrcBottom = srcBottom;
+ mDstLeft = dstLeft;
+ mDstTop = dstTop;
+ mDstRight = dstRight;
+ mDstBottom = dstBottom;
+ mScaleType = type;
+ mScaleFactor = scale;
+ adjustDrawToType();
+ }
+
+ public void setup(
+ float srcLeft,
+ float srcTop,
+ float srcRight,
+ float srcBottom,
+ float dstLeft,
+ float dstTop,
+ float dstRight,
+ float dstBottom,
+ int type,
+ float scale) {
+
+ mSrcLeft = srcLeft;
+ mSrcTop = srcTop;
+ mSrcRight = srcRight;
+ mSrcBottom = srcBottom;
+ mDstLeft = dstLeft;
+ mDstTop = dstTop;
+ mDstRight = dstRight;
+ mDstBottom = dstBottom;
+ mScaleType = type;
+ mScaleFactor = scale;
+ adjustDrawToType();
+ }
+
+ static String str(float v) {
+ String s = " " + (int) v;
+ return s.substring(s.length() - 3);
+ }
+
+ void print(String str, float left, float top, float right, float bottom) {
+ String s = str;
+ s += str(left) + ", " + str(top) + ", " + str(right) + ", " + str(bottom) + ", ";
+ s += " [" + str(right - left) + " x " + str(bottom - top) + "]";
+ System.out.println(s);
+ }
+
+ /** This adjust destnation on the DrawBitMapInt to support all contentScale types */
+ private void adjustDrawToType() {
+ int sw = (int) (mSrcRight - mSrcLeft);
+ int sh = (int) (mSrcBottom - mSrcTop);
+ float width = mDstRight - mDstLeft;
+ float height = mDstBottom - mDstTop;
+ int dw = (int) width;
+ int dh = (int) height;
+ int dLeft = 0;
+ int dRight = dw;
+ int dTop = 0;
+ int dBottom = dh;
+ if (DEBUG) {
+ print("test rc ", mSrcLeft, mSrcTop, mSrcRight, mSrcBottom);
+ print("test dst ", mDstLeft, mDstTop, mDstRight, mDstBottom);
+ }
+
+ switch (mScaleType) {
+ case SCALE_NONE:
+ dh = sh;
+ dw = sw;
+ dTop = ((int) height - dh) / 2;
+ dBottom = dh + dTop;
+ dLeft = ((int) width - dw) / 2;
+ dRight = dw + dLeft;
+ break;
+ case SCALE_INSIDE:
+ if (dh > sh && dw > sw) {
+ dh = sh;
+ dw = sw;
+ } else if (sw * height > width * sh) { // width dominated
+ dh = (dw * sh) / sw;
+ } else {
+ dw = (dh * sw) / sh;
+ }
+ dTop = ((int) height - dh) / 2;
+ dBottom = dh + dTop;
+ dLeft = ((int) width - dw) / 2;
+ dRight = dw + dLeft;
+ break;
+ case SCALE_FILL_WIDTH:
+ dh = (dw * sh) / sw;
+
+ dTop = ((int) height - dh) / 2;
+ dBottom = dh + dTop;
+ dLeft = ((int) width - dw) / 2;
+ dRight = dw + dLeft;
+ break;
+ case SCALE_FILL_HEIGHT:
+ dw = (dh * sw) / sh;
+
+ dTop = ((int) height - dh) / 2;
+ dBottom = dh + dTop;
+ dLeft = ((int) width - dw) / 2;
+ dRight = dw + dLeft;
+ break;
+ case SCALE_FIT:
+ if (sw * height > width * sh) { // width dominated
+ dh = (dw * sh) / sw;
+ dTop = ((int) height - dh) / 2;
+ dBottom = dh + dTop;
+ } else {
+ dw = (dh * sw) / sh;
+ dLeft = ((int) width - dw) / 2;
+ dRight = dw + dLeft;
+ }
+ break;
+ case SCALE_CROP:
+ if (sw * height < width * sh) { // width dominated
+ dh = (dw * sh) / sw;
+ dTop = ((int) height - dh) / 2;
+ dBottom = dh + dTop;
+ } else {
+ dw = (dh * sw) / sh;
+ dLeft = ((int) width - dw) / 2;
+ dRight = dw + dLeft;
+ }
+ break;
+ case SCALE_FILL_BOUNDS:
+ // do nothing
+ break;
+ case SCALE_FIXED_SCALE:
+ dh = (int) (sh * mScaleFactor);
+ dw = (int) (sw * mScaleFactor);
+ dTop = ((int) height - dh) / 2;
+ dBottom = dh + dTop;
+ dLeft = ((int) width - dw) / 2;
+ dRight = dw + dLeft;
+ break;
+ }
+
+ mFinalDstRight = dRight + mDstLeft;
+ mFinalDstLeft = dLeft + mDstLeft;
+ mFinalDstBottom = dBottom + mDstTop;
+ mFinalDstTop = dTop + mDstTop;
+
+ if (DEBUG) {
+ print("test out ", mFinalDstLeft, mFinalDstTop, mFinalDstRight, mFinalDstBottom);
+ }
+ }
+
+ public static String typeToString(int type) {
+ String[] typeString = {
+ "none",
+ "inside",
+ "fill_width",
+ "fill_height",
+ "fit",
+ "crop",
+ "fill_bounds",
+ "fixed_scale"
+ };
+ return typeString[type];
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntFloatMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntFloatMap.java
index b2d714e..3b4ece9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntFloatMap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntFloatMap.java
@@ -32,9 +32,7 @@
mValues = new float[DEFAULT_CAPACITY];
}
- /**
- * clear the map
- */
+ /** clear the map */
public void clear() {
Arrays.fill(mKeys, NOT_PRESENT);
Arrays.fill(mValues, Float.NaN); // not strictly necessary but defensive
@@ -78,8 +76,7 @@
int index = findKey(key);
if (index == -1) {
return 0;
- } else
- return mValues[index];
+ } else return mValues[index];
}
/**
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntIntMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntIntMap.java
index 606dc78..68cd0e6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntIntMap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntIntMap.java
@@ -31,9 +31,7 @@
mValues = new int[DEFAULT_CAPACITY];
}
- /**
- * clear the map
- */
+ /** clear the map */
public void clear() {
Arrays.fill(mKeys, NOT_PRESENT);
Arrays.fill(mValues, 0);
@@ -77,8 +75,7 @@
int index = findKey(key);
if (index == -1) {
return 0;
- } else
- return mValues[index];
+ } else return mValues[index];
}
/**
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
index 0512fa6..84e7843 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
@@ -42,7 +42,7 @@
mSize = 0;
}
- public T put(int key, T value) {
+ public T put(int key, T value) {
if (key == NOT_PRESENT) throw new IllegalArgumentException("Key cannot be NOT_PRESENT");
if (mSize > mKeys.length * LOAD_FACTOR) {
resize();
@@ -50,24 +50,23 @@
return insert(key, value);
}
- public T get(int key) {
+ public T get(int key) {
int index = findKey(key);
if (index == -1) {
- return null;
- } else
- return mValues.get(index);
+ return null;
+ } else return mValues.get(index);
}
public int size() {
return mSize;
}
- private T insert(int key, T value) {
+ private T insert(int key, T value) {
int index = hash(key) % mKeys.length;
while (mKeys[index] != NOT_PRESENT && mKeys[index] != key) {
index = (index + 1) % mKeys.length;
}
- T oldValue = null;
+ T oldValue = null;
if (mKeys[index] == NOT_PRESENT) {
mSize++;
} else {
@@ -78,7 +77,7 @@
return oldValue;
}
- private int findKey(int key) {
+ private int findKey(int key) {
int index = hash(key) % mKeys.length;
while (mKeys[index] != NOT_PRESENT) {
if (mKeys[index] == key) {
@@ -89,11 +88,11 @@
return -1;
}
- private int hash(int key) {
+ private int hash(int key) {
return key;
}
- private void resize() {
+ private void resize() {
int[] oldKeys = mKeys;
ArrayList<T> oldValues = mValues;
mKeys = new int[(oldKeys.length * 2)];
@@ -111,4 +110,46 @@
}
}
}
+
+ public T remove(int key) {
+ int index = hash(key) % mKeys.length;
+ int initialIndex = index;
+
+ while (mKeys[index] != NOT_PRESENT) {
+ if (mKeys[index] == key) {
+ T oldValue = mValues.get(index);
+ mKeys[index] = NOT_PRESENT;
+ mValues.set(index, null);
+ mSize--;
+
+ // Rehash the cluster of keys following the removed key
+ rehashFrom((index + 1) % mKeys.length);
+ return oldValue;
+ }
+ index = (index + 1) % mKeys.length;
+ if (index == initialIndex) {
+ break; // Avoid infinite loop
+ }
+ }
+ return null; // Key not found
+ }
+
+ private void rehashFrom(int startIndex) {
+ int index = startIndex;
+
+ while (mKeys[index] != NOT_PRESENT) {
+ int keyToRehash = mKeys[index];
+ T valueToRehash = mValues.get(index);
+
+ // Remove the key-value pair from the current position
+ mKeys[index] = NOT_PRESENT;
+ mValues.set(index, null);
+ mSize--;
+
+ // Re-insert the key-value pair
+ insert(keyToRehash, valueToRehash);
+
+ index = (index + 1) % mKeys.length;
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java
index ae61ec1..baa144d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java
@@ -18,8 +18,8 @@
/**
* High performance Integer expression evaluator
*
- * The evaluation is based on int opMask, int[]exp
- * exp[i] is an operator if (opMask*(1 << i) != 0)
+ * <p>The evaluation is based on int opMask, int[]exp exp[i] is an operator if (opMask*(1 << i) !=
+ * 0)
*/
public class IntegerExpressionEvaluator {
static IntMap<String> sNames = new IntMap<>();
@@ -51,26 +51,25 @@
public static final int I_IFELSE = OFFSET + 22;
public static final int I_MAD = OFFSET + 23;
- public static final float LAST_OP = 24;
+ public static final float LAST_OP = 25;
public static final int I_VAR1 = OFFSET + 24;
- public static final int I_VAR2 = OFFSET + 24;
-
+ public static final int I_VAR2 = OFFSET + 25;
int[] mStack;
int[] mLocalStack = new int[128];
int[] mVar;
-
interface Op {
int eval(int sp);
}
/**
* Evaluate an integer expression
+ *
* @param mask bits that are operators
- * @param exp rpn sequence of values and operators
- * @param var variables if the expression is a function
+ * @param exp rpn sequence of values and operators
+ * @param var variables if the expression is a function
* @return return the results of evaluating the expression
*/
public int eval(int mask, int[] exp, int... var) {
@@ -90,10 +89,11 @@
/**
* Evaluate a integer expression
+ *
* @param mask bits that are operators
- * @param exp rpn sequence of values and operators
- * @param len the number of values in the expression
- * @param var variables if the expression is a function
+ * @param exp rpn sequence of values and operators
+ * @param len the number of values in the expression
+ * @param var variables if the expression is a function
* @return return the results of evaluating the expression
*/
public int eval(int mask, int[] exp, int len, int... var) {
@@ -114,9 +114,10 @@
/**
* Evaluate a int expression
+ *
* @param opMask bits that are operators
- * @param exp rpn sequence of values and operators
- * @param var variables if the expression is a function
+ * @param exp rpn sequence of values and operators
+ * @param var variables if the expression is a function
* @return return the results of evaluating the expression
*/
public int evalDB(int opMask, int[] exp, int... var) {
@@ -150,11 +151,11 @@
mStack[sp - 1] = mStack[sp - 1] * mStack[sp];
return sp - 1;
},
- (sp) -> { // DIV
+ (sp) -> { // DIV
mStack[sp - 1] = mStack[sp - 1] / mStack[sp];
return sp - 1;
},
- (sp) -> { // MOD
+ (sp) -> { // MOD
mStack[sp - 1] = mStack[sp - 1] % mStack[sp];
return sp - 1;
},
@@ -183,8 +184,7 @@
return sp - 1;
},
(sp) -> { // COPY_SIGN copy the sing of (using bit magic)
- mStack[sp - 1] = (mStack[sp - 1] ^ (mStack[sp] >> 31))
- - (mStack[sp] >> 31);
+ mStack[sp - 1] = (mStack[sp - 1] ^ (mStack[sp] >> 31)) - (mStack[sp] >> 31);
return sp - 1;
},
(sp) -> { // MIN
@@ -219,22 +219,18 @@
mStack[sp] = (mStack[sp] >> 31) | (-mStack[sp] >>> 31);
return sp;
},
-
(sp) -> { // CLAMP(min,max, val)
- mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]),
- mStack[sp - 1]);
+ mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]), mStack[sp - 1]);
return sp - 2;
},
(sp) -> { // Ternary conditional
- mStack[sp - 2] = (mStack[sp] > 0)
- ? mStack[sp - 1] : mStack[sp - 2];
+ mStack[sp - 2] = (mStack[sp] > 0) ? mStack[sp - 1] : mStack[sp - 2];
return sp - 2;
},
(sp) -> { // MAD
mStack[sp - 2] = mStack[sp] + mStack[sp - 1] * mStack[sp - 2];
return sp - 2;
},
-
(sp) -> { // first var =
mStack[sp] = mVar[0];
return sp;
@@ -296,7 +292,7 @@
* Convert an expression encoded as an array of ints int to a string
*
* @param opMask bits that are operators
- * @param exp rpn sequence of values and operators
+ * @param exp rpn sequence of values and operators
* @param labels String that represent the variable names
* @return
*/
@@ -328,7 +324,7 @@
* Convert an expression encoded as an array of ints int ot a string
*
* @param opMask bit mask of operators vs commands
- * @param exp rpn sequence of values and operators
+ * @param exp rpn sequence of values and operators
* @return string representation of the expression
*/
public static String toString(int opMask, int[] exp) {
@@ -357,8 +353,9 @@
/**
* This creates an infix string expression
+ *
* @param opMask The bits that are operators
- * @param exp the array of expressions
+ * @param exp the array of expressions
* @return infix string
*/
public static String toStringInfix(int opMask, int[] exp) {
@@ -375,24 +372,39 @@
return sNames.get(id) + "(" + toString(mask, exp, sp - 1) + ") ";
case 2:
if (infix(id)) {
- return "(" + toString(mask, exp, sp - 2)
- + " " + sNames.get(id) + " "
- + toString(mask, exp, sp - 1) + ") ";
+ return "("
+ + toString(mask, exp, sp - 2)
+ + " "
+ + sNames.get(id)
+ + " "
+ + toString(mask, exp, sp - 1)
+ + ") ";
} else {
- return sNames.get(id) + "("
- + toString(mask, exp, sp - 2) + ", "
- + toString(mask, exp, sp - 1) + ")";
+ return sNames.get(id)
+ + "("
+ + toString(mask, exp, sp - 2)
+ + ", "
+ + toString(mask, exp, sp - 1)
+ + ")";
}
case 3:
if (infix(id)) {
- return "((" + toString(mask, exp, sp + 3) + ") ? "
- + toString(mask, exp, sp - 2) + ":"
- + toString(mask, exp, sp - 1) + ")";
+ return "(("
+ + toString(mask, exp, sp + 3)
+ + ") ? "
+ + toString(mask, exp, sp - 2)
+ + ":"
+ + toString(mask, exp, sp - 1)
+ + ")";
} else {
return sNames.get(id)
- + "(" + toString(mask, exp, sp - 3)
- + ", " + toString(mask, exp, sp - 2)
- + ", " + toString(mask, exp, sp - 1) + ")";
+ + "("
+ + toString(mask, exp, sp - 3)
+ + ", "
+ + toString(mask, exp, sp - 2)
+ + ", "
+ + toString(mask, exp, sp - 1)
+ + ")";
}
}
}
@@ -401,11 +413,32 @@
static final int[] NO_OF_OPS = {
-1, // no op
- 2, 2, 2, 2, 2, // + - * / %
- 2, 2, 2, 2, 2, 2, 2, 2, 2, //<<, >> , >>> , | , &, ^, min max
- 1, 1, 1, 1, 1, 1, // neg, abs, ++, -- , not , sign
- 3, 3, 3, // clamp, ifElse, mad,
- 0, 0, 0 // mad, ?:,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2, // + - * / %
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2, // <<, >> , >>> , | , &, ^, min max
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1, // neg, abs, ++, -- , not , sign
+ 3,
+ 3,
+ 3, // clamp, ifElse, mad,
+ 0,
+ 0,
+ 0 // mad, ?:,
// a[0],a[1],a[2]
};
@@ -416,13 +449,14 @@
* @return true if the operator is infix
*/
static boolean infix(int n) {
- return ((n < 12));
+ return n < 12;
}
/**
* is it an id or operation
+ *
* @param opMask the bits that mark elements as an operation
- * @param i the bit to check
+ * @param i the bit to check
* @return true if the bit is 1
*/
public static boolean isOperation(int opMask, int i) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java
index 1e669c6..0616cc7 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java
@@ -18,17 +18,12 @@
import com.android.internal.widget.remotecompose.core.operations.Utils;
/**
- * This defines the major id maps and ranges used by remote compose
- * Generally ids ranging from 1 ... 7FFFFF (4095) are for ids
- * The data range is divided int to bits
- * 0xxxxx are allocated for Predefined Global System Variables
- * 1xxxxx are allocated to normal variables
- * 2xxxxx are allocated to List&MAPS (Arrays of stuff)
- * 3xxxxx are allocated to path & float operations
- * 4xxxxx,5xxxxx,7xxxxx are reserved for future use
- * 0x1000-0x1100 are used for path operations in PathData
- * 0x1100-0x1200 are used for math operations in Animated float
- * 0x
+ * This defines the major id maps and ranges used by remote compose Generally ids ranging from 1 ...
+ * 7FFFFF (4095) are for ids The data range is divided int to bits 0xxxxx are allocated for
+ * Predefined Global System Variables 1xxxxx are allocated to normal variables 2xxxxx are allocated
+ * to List&MAPS (Arrays of stuff) 3xxxxx are allocated to path & float operations
+ * 4xxxxx,5xxxxx,7xxxxx are reserved for future use 0x1000-0x1100 are used for path operations in
+ * PathData 0x1100-0x1200 are used for math operations in Animated float 0x
*/
public class NanMap {
public static final int MOVE = 0x300_000;
@@ -71,7 +66,6 @@
public static final int ID_REGION_MASK = 0x700000;
public static final int ID_REGION_ARRAY = 0x200000;
-
/**
* Get ID from Nan float
*
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringSerializer.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringSerializer.java
index fb90781..ab7576e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringSerializer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringSerializer.java
@@ -15,15 +15,14 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities;
-/**
- * Utility serializer maintaining an indent buffer
- */
+/** Utility serializer maintaining an indent buffer */
public class StringSerializer {
StringBuffer mBuffer = new StringBuffer();
String mIndentBuffer = " ";
/**
* Append some content to the current buffer
+ *
* @param indent the indentation level to use
* @param content content to append
*/
@@ -35,17 +34,17 @@
mBuffer.append("\n");
}
- /**
- * Reset the buffer
- */
+ /** Reset the buffer */
public void reset() {
mBuffer = new StringBuffer();
}
/**
* Return a string representation of the buffer
+ *
* @return string representation
*/
+ @Override
public String toString() {
return mBuffer.toString();
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
index 8dd5405..f2ccb40 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
@@ -17,26 +17,21 @@
import java.util.Arrays;
-/**
- * Utilities for string manipulation
- */
+/** Utilities for string manipulation */
public class StringUtils {
/**
- * Converts a float into a string.
- * Providing a defined number of characters before and after the
+ * Converts a float into a string. Providing a defined number of characters before and after the
* decimal point.
*
- * @param value The value to convert to string
+ * @param value The value to convert to string
* @param beforeDecimalPoint digits before the decimal point
- * @param afterDecimalPoint digits after the decimal point
- * @param pre character to pad width 0 = no pad typically ' ' or '0'
- * @param post character to pad width 0 = no pad typically ' ' or '0'
+ * @param afterDecimalPoint digits after the decimal point
+ * @param pre character to pad width 0 = no pad typically ' ' or '0'
+ * @param post character to pad width 0 = no pad typically ' ' or '0'
* @return
*/
- public static String floatToString(float value,
- int beforeDecimalPoint,
- int afterDecimalPoint,
- char pre, char post) {
+ public static String floatToString(
+ float value, int beforeDecimalPoint, int afterDecimalPoint, char pre, char post) {
int integerPart = (int) value;
float fractionalPart = value % 1;
@@ -52,7 +47,6 @@
integerPartString = new String(pad) + integerPartString;
}
-
} else if (iLen > beforeDecimalPoint) {
integerPartString = integerPartString.substring(iLen - beforeDecimalPoint);
}
@@ -68,7 +62,7 @@
fractionalPart = Math.round(fractionalPart);
for (int i = 0; i < afterDecimalPoint; i++) {
- fractionalPart *= .1;
+ fractionalPart *= .1F;
}
String fact = Float.toString(fractionalPart);
@@ -92,5 +86,4 @@
return integerPartString + "." + fact;
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java
index c3cd5ae9..3161aa1 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java
@@ -15,9 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
-/**
- * Provide a specific bouncing easing function
- */
+/** Provide a specific bouncing easing function */
public class BounceCurve extends Easing {
private static final float N1 = 7.5625f;
private static final float D1 = 2.75f;
@@ -63,5 +61,4 @@
}
return 0f;
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java
index fd1ee03..60a59cf 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java
@@ -16,7 +16,6 @@
package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
class CubicEasing extends Easing {
- float mType = 0;
float mX1 = 0f;
float mY1 = 0f;
float mX2 = 0f;
@@ -92,25 +91,24 @@
return mY1 * f1 + mY2 * f2 + f3;
}
- private float getDiffX(float t) {
- float t1 = 1 - t;
- return 3 * t1 * t1 * mX1 + 6 * t1 * t * (mX2 - mX1) + 3 * t * t * (1 - mX2);
- }
+ // private float getDiffX(float t) {
+ // float t1 = 1 - t;
+ // return 3 * t1 * t1 * mX1 + 6 * t1 * t * (mX2 - mX1) + 3 * t * t * (1 - mX2);
+ // }
- private float getDiffY(float t) {
- float t1 = 1 - t;
- return 3 * t1 * t1 * mY1 + 6 * t1 * t * (mY2 - mY1) + 3 * t * t * (1 - mY2);
- }
+ // private float getDiffY(float t) {
+ // float t1 = 1 - t;
+ // return 3 * t1 * t1 * mY1 + 6 * t1 * t * (mY2 - mY1) + 3 * t * t * (1 - mY2);
+ // }
- /**
- * binary search for the region and linear interpolate the answer
- */
+ /** binary search for the region and linear interpolate the answer */
+ @Override
public float getDiff(float x) {
float t = 0.5f;
float range = 0.5f;
while (range > D_ERROR) {
float tx = getX(t);
- range *= 0.5;
+ range *= 0.5f;
if (tx < x) {
t += range;
} else {
@@ -124,9 +122,8 @@
return (y2 - y1) / (x2 - x1);
}
- /**
- * binary search for the region and linear interpolate the answer
- */
+ /** binary search for the region and linear interpolate the answer */
+ @Override
public float get(float x) {
if (x <= 0.0f) {
return 0f;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java
index 4ed9550..d8bc83e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java
@@ -15,19 +15,14 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
-/**
- * The standard interface to Easing functions
- */
+/** The standard interface to Easing functions */
public abstract class Easing {
int mType;
- /**
- * get the value at point x
- */
+
+ /** get the value at point x */
public abstract float get(float x);
- /**
- * get the slope of the easing function at at x
- */
+ /** get the slope of the easing function at at x */
public abstract float getDiff(float x);
public int getType() {
@@ -44,5 +39,4 @@
public static final int SPLINE_CUSTOM = 12;
public static final int EASE_OUT_BOUNCE = 13;
public static final int EASE_OUT_ELASTIC = 14;
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java
index e269583..d53cff5 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java
@@ -15,9 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
-/**
- * Provide a bouncing Easing function
- */
+/** Provide a bouncing Easing function */
public class ElasticOutCurve extends Easing {
private static final float F_PI = (float) Math.PI;
private static final float C4 = 2 * F_PI / 3;
@@ -31,9 +29,7 @@
}
if (x >= 1) {
return 1.0f;
- } else
- return (float) (Math.pow(2.0f, -10 * x)
- * Math.sin((x * 10 - 0.75f) * C4) + 1);
+ } else return (float) (Math.pow(2.0f, -10 * x) * Math.sin((x * 10 - 0.75f) * C4) + 1);
}
@Override
@@ -41,9 +37,11 @@
if (x < 0 || x > 1) {
return 0.0f;
} else
- return (float) ((5 * Math.pow(2.0f, (1 - 10 * x))
- * (LOG_8 * Math.cos(TWENTY_PI * x / 3) + 2
- * F_PI * Math.sin(TWENTY_PI * x / 3))
- / 3));
+ return (float)
+ (5
+ * Math.pow(2.0f, 1 - 10 * x)
+ * (LOG_8 * Math.cos(TWENTY_PI * x / 3)
+ + 2 * F_PI * Math.sin(TWENTY_PI * x / 3))
+ / 3);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java
index 98dbf00..a29b8af 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java
@@ -15,9 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
-/**
- * Support Animation of the FloatExpression
- */
+/** Support Animation of the FloatExpression */
public class FloatAnimation extends Easing {
float[] mSpec;
// mSpec[0] = duration
@@ -25,12 +23,12 @@
// mSpec[2..1+num_of_param] params
// mSpec[2+num_of_param] starting Value
Easing mEasingCurve;
- private int mType = CUBIC_STANDARD;
+
private float mDuration = 1;
private float mWrap = Float.NaN;
private float mInitialValue = Float.NaN;
private float mTargetValue = Float.NaN;
- private float mScale = 1;
+ // private float mScale = 1;
float mOffset = 0;
@Override
@@ -51,20 +49,19 @@
}
public FloatAnimation() {
+ mType = CUBIC_STANDARD;
mEasingCurve = new CubicEasing(mType);
}
public FloatAnimation(float... description) {
+ mType = CUBIC_STANDARD;
setAnimationDescription(description);
}
- public FloatAnimation(int type,
- float duration,
- float[] description,
- float initialValue,
- float wrap) {
- setAnimationDescription(packToFloatArray(duration,
- type, description, initialValue, wrap));
+ public FloatAnimation(
+ int type, float duration, float[] description, float initialValue, float wrap) {
+ mType = CUBIC_STANDARD;
+ setAnimationDescription(packToFloatArray(duration, type, description, initialValue, wrap));
}
/**
@@ -76,11 +73,8 @@
* @param initialValue
* @return
*/
- public static float[] packToFloatArray(float duration,
- int type,
- float[] spec,
- float initialValue,
- float wrap) {
+ public static float[] packToFloatArray(
+ float duration, int type, float[] spec, float initialValue, float wrap) {
int count = 0;
if (!Float.isNaN(initialValue)) {
@@ -104,7 +98,7 @@
if (duration != 1 || count > 0) {
count++;
}
- if (!Float.isNaN(wrap) || !Float.isNaN(initialValue)) {
+ if (!Float.isNaN(wrap) || !Float.isNaN(initialValue)) {
count++;
}
float[] ret = new float[count];
@@ -113,11 +107,10 @@
if (ret.length > 0) {
ret[pos++] = duration;
-
}
if (ret.length > 1) {
- int wrapBit = (Float.isNaN(wrap)) ? 0 : 1;
- int initBit = (Float.isNaN(initialValue)) ? 0 : 2;
+ int wrapBit = Float.isNaN(wrap) ? 0 : 1;
+ int initBit = Float.isNaN(initialValue) ? 0 : 2;
int bits = type | ((wrapBit | initBit) << 8);
ret[pos++] = Float.intBitsToFloat(specLen << 16 | bits);
}
@@ -137,6 +130,7 @@
/**
* Create an animation based on a float encoding of the animation
+ *
* @param description
*/
public void setAnimationDescription(float[] description) {
@@ -171,11 +165,12 @@
mEasingCurve = new CubicEasing(type);
break;
case CUBIC_CUSTOM:
- mEasingCurve = new CubicEasing(params[offset + 0],
- params[offset + 1],
- params[offset + 2],
- params[offset + 3]
- );
+ mEasingCurve =
+ new CubicEasing(
+ params[offset + 0],
+ params[offset + 1],
+ params[offset + 2],
+ params[offset + 3]);
break;
case EASE_OUT_BOUNCE:
mEasingCurve = new BounceCurve(type);
@@ -191,6 +186,7 @@
/**
* Get the duration the interpolate is to take
+ *
* @return duration in seconds
*/
public float getDuration() {
@@ -199,6 +195,7 @@
/**
* Set the initial Value
+ *
* @param value
*/
public void setInitialValue(float value) {
@@ -213,6 +210,7 @@
/**
* Set the target value to interpolate to
+ *
* @param value
*/
public void setTargetValue(float value) {
@@ -236,25 +234,23 @@
private void setScaleOffset() {
if (!Float.isNaN(mInitialValue) && !Float.isNaN(mTargetValue)) {
- mScale = (mTargetValue - mInitialValue);
+ // mScale = (mTargetValue - mInitialValue); // TODO: commented out because
+ // unused.
mOffset = mInitialValue;
} else {
- mScale = 1;
+ // mScale = 1; // TODO: commented out because its unused
mOffset = 0;
}
}
- /**
- * get the value at time t in seconds since start
- */
+ /** get the value at time t in seconds since start */
+ @Override
public float get(float t) {
- return mEasingCurve.get(t / mDuration)
- * (mTargetValue - mInitialValue) + mInitialValue;
+ return mEasingCurve.get(t / mDuration) * (mTargetValue - mInitialValue) + mInitialValue;
}
- /**
- * get the slope of the easing function at at x
- */
+ /** get the slope of the easing function at at x */
+ @Override
public float getDiff(float t) {
return mEasingCurve.getDiff(t / mDuration) * (mTargetValue - mInitialValue);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java
index 50a7d59..75a6032 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java
@@ -15,15 +15,14 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
-/**
- * Provides and interface to create easing functions
- */
-public class GeneralEasing extends Easing{
+/** Provides and interface to create easing functions */
+public class GeneralEasing extends Easing {
float[] mEasingData = new float[0];
Easing mEasingCurve = new CubicEasing(CUBIC_STANDARD);
/**
* Set the curve based on the float encoding of it
+ *
* @param data
*/
public void setCurveSpecification(float[] data) {
@@ -47,11 +46,9 @@
mEasingCurve = new CubicEasing(type);
break;
case CUBIC_CUSTOM:
- mEasingCurve = new CubicEasing(mEasingData[1],
- mEasingData[2],
- mEasingData[3],
- mEasingData[5]
- );
+ mEasingCurve =
+ new CubicEasing(
+ mEasingData[1], mEasingData[2], mEasingData[3], mEasingData[5]);
break;
case EASE_OUT_BOUNCE:
mEasingCurve = new BounceCurve(type);
@@ -59,23 +56,20 @@
}
}
- /**
- * get the value at point x
- */
+ /** get the value at point x */
+ @Override
public float get(float x) {
return mEasingCurve.get(x);
}
- /**
- * get the slope of the easing function at at x
- */
+ /** get the slope of the easing function at at x */
+ @Override
public float getDiff(float x) {
return mEasingCurve.getDiff(x);
}
+ @Override
public int getType() {
return mEasingCurve.getType();
}
-
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java
index 23930b9..9355cac 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java
@@ -17,12 +17,8 @@
import java.util.Arrays;
-/**
- * This performs a spline interpolation in multiple dimensions
- *
- *
- */
-public class MonotonicCurveFit {
+/** This performs a spline interpolation in multiple dimensions */
+public class MonotonicCurveFit {
private static final String TAG = "MonotonicCurveFit";
private double[] mT;
private double[][] mY;
@@ -32,6 +28,7 @@
/**
* create a collection of curves
+ *
* @param time the point along the curve
* @param y the parameter at those points
*/
@@ -78,6 +75,7 @@
/**
* Get the position of all curves at time t
+ *
* @param t
* @param v
*/
@@ -137,6 +135,7 @@
/**
* Get the position of all curves at time t
+ *
* @param t
* @param v
*/
@@ -196,6 +195,7 @@
/**
* Get the position of the jth curve at time t
+ *
* @param t
* @param j
* @return
@@ -230,7 +230,6 @@
double t1 = mTangent[i][j];
double t2 = mTangent[i + 1][j];
return interpolate(h, x, y1, y2, t1, t2);
-
}
}
return 0; // should never reach here
@@ -238,6 +237,7 @@
/**
* Get the slope of all the curves at position t
+ *
* @param t
* @param v
*/
@@ -264,11 +264,11 @@
break;
}
}
- return;
}
/**
* Get the slope of the j curve at position t
+ *
* @param t
* @param j
* @return
@@ -299,34 +299,38 @@
return mT;
}
- /**
- * Cubic Hermite spline
- */
- private static double interpolate(double h,
- double x,
- double y1,
- double y2,
- double t1,
- double t2) {
+ /** Cubic Hermite spline */
+ private static double interpolate(
+ double h, double x, double y1, double y2, double t1, double t2) {
double x2 = x * x;
double x3 = x2 * x;
- return -2 * x3 * y2 + 3 * x2 * y2 + 2 * x3 * y1 - 3 * x2 * y1 + y1
- + h * t2 * x3 + h * t1 * x3 - h * t2 * x2 - 2 * h * t1 * x2
+ return -2 * x3 * y2
+ + 3 * x2 * y2
+ + 2 * x3 * y1
+ - 3 * x2 * y1
+ + y1
+ + h * t2 * x3
+ + h * t1 * x3
+ - h * t2 * x2
+ - 2 * h * t1 * x2
+ h * t1 * x;
}
- /**
- * Cubic Hermite spline slope differentiated
- */
+ /** Cubic Hermite spline slope differentiated */
private static double diff(double h, double x, double y1, double y2, double t1, double t2) {
double x2 = x * x;
- return -6 * x2 * y2 + 6 * x * y2 + 6 * x2 * y1 - 6 * x * y1 + 3 * h * t2 * x2
- + 3 * h * t1 * x2 - 2 * h * t2 * x - 4 * h * t1 * x + h * t1;
+ return -6 * x2 * y2
+ + 6 * x * y2
+ + 6 * x2 * y1
+ - 6 * x * y1
+ + 3 * h * t2 * x2
+ + 3 * h * t1 * x2
+ - 2 * h * t2 * x
+ - 4 * h * t1 * x
+ + h * t1;
}
- /**
- * This builds a monotonic spline to be used as a wave function
- */
+ /** This builds a monotonic spline to be used as a wave function */
public static MonotonicCurveFit buildWave(String configString) {
// done this way for efficiency
String str = configString;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java
index 6ed6548..b459689 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java
@@ -15,14 +15,13 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
-
/**
- * This class translates a series of floating point values into a continuous
- * curve for use in an easing function including quantize functions
- * it is used with the "spline(0,0.3,0.3,0.5,...0.9,1)" it should start at 0 and end at one 1
+ * This class translates a series of floating point values into a continuous curve for use in an
+ * easing function including quantize functions it is used with the "spline(0,0.3,0.3,0.5,...0.9,1)"
+ * it should start at 0 and end at one 1
*/
public class StepCurve extends Easing {
- private static final boolean DEBUG = false;
+ // private static final boolean DEBUG = false;
MonotonicCurveFit mCurveFit;
public StepCurve(float[] params, int offset, int len) {
@@ -55,12 +54,16 @@
@Override
public float getDiff(float x) {
+ if (x < 0f) return 0;
+ if (x > 1f) return 0;
return (float) mCurveFit.getSlope(x, 0);
}
-
@Override
public float get(float x) {
+ if (x < 0f) return 0;
+ if (x > 1f) return 1;
+
return (float) mCurveFit.getPos(x, 0);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java
index 9045bcbd..57a8042 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java
@@ -15,23 +15,21 @@
*/
package com.android.internal.widget.remotecompose.core.types;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.BYTE;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.BYTE;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Used to represent a boolean
- */
+/** Used to represent a boolean */
public class BooleanConstant implements Operation {
private static final int OP_CODE = Operations.DATA_BOOLEAN;
- boolean mValue = false;
+ private boolean mValue = false;
private int mId;
public BooleanConstant(int id, boolean value) {
@@ -39,15 +37,22 @@
mValue = value;
}
+ /**
+ * Get the value of the boolean constant
+ *
+ * @return the value of the boolean
+ */
+ public boolean getValue() {
+ return mValue;
+ }
+
@Override
public void write(WireBuffer buffer) {
apply(buffer, mId, mValue);
}
@Override
- public void apply(RemoteContext context) {
-
- }
+ public void apply(RemoteContext context) {}
@Override
public String deepToString(String indent) {
@@ -88,14 +93,9 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Expressions Operations",
- OP_CODE,
- "BooleanConstant")
+ doc.operation("Expressions Operations", OP_CODE, "BooleanConstant")
.description("A boolean and its associated id")
- .field(INT, "id", "id of Int")
- .field(BYTE, "value",
- "8-bit 0 or 1");
-
+ .field(DocumentedOperation.INT, "id", "id of Int")
+ .field(BYTE, "value", "8-bit 0 or 1");
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java
index 90faf52..3ef9db9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java
@@ -15,20 +15,18 @@
*/
package com.android.internal.widget.remotecompose.core.types;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Represents a single integer typically used for states
- * or named for input into the system
- */
+/** Represents a single integer typically used for states or named for input into the system */
public class IntegerConstant implements Operation {
private int mValue = 0;
private int mId;
@@ -87,13 +85,9 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Expressions Operations",
- id(),
- "IntegerConstant")
+ doc.operation("Expressions Operations", id(), "IntegerConstant")
.description("A integer and its associated id")
- .field(INT, "id", "id of Int")
- .field(INT, "value",
- "32-bit int value");
-
+ .field(DocumentedOperation.INT, "id", "id of Int")
+ .field(INT, "value", "32-bit int value");
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java
index 70402cf..6d51d19 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java
@@ -15,23 +15,21 @@
*/
package com.android.internal.widget.remotecompose.core.types;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.LONG;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.LONG;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Used to represent a long
- */
+/** Used to represent a long */
public class LongConstant implements Operation {
private static final int OP_CODE = Operations.DATA_LONG;
- long mValue;
+ private long mValue;
private int mId;
public LongConstant(int id, long value) {
@@ -39,6 +37,15 @@
mValue = value;
}
+ /**
+ * Get the value of the long constant
+ *
+ * @return the value of the long
+ */
+ public long getValue() {
+ return mValue;
+ }
+
@Override
public void write(WireBuffer buffer) {
apply(buffer, mId, mValue);
@@ -46,6 +53,7 @@
@Override
public void apply(RemoteContext context) {
+ context.putObject(mId, this);
}
@Override
@@ -79,14 +87,9 @@
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Expressions Operations",
- OP_CODE,
- "LongConstant")
+ doc.operation("Expressions Operations", OP_CODE, "LongConstant")
.description("A boolean and its associated id")
- .field(INT, "id", "id of Int")
- .field(LONG, "value",
- "The long Value");
-
+ .field(DocumentedOperation.INT, "id", "id of Int")
+ .field(LONG, "value", "The long Value");
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
index e32f823..906282c 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
@@ -22,9 +22,7 @@
import java.io.InputStream;
-/**
- * Public API to create a new RemoteComposeDocument coming from an input stream
- */
+/** Public API to create a new RemoteComposeDocument coming from an input stream */
public class RemoteComposeDocument {
CoreDocument mDocument = new CoreDocument();
@@ -48,23 +46,19 @@
}
/**
- * Called when an initialization is needed, allowing the document to eg load
- * resources / cache them.
+ * Called when an initialization is needed, allowing the document to eg load resources / cache
+ * them.
*/
public void initializeContext(RemoteContext context) {
mDocument.initializeContext(context);
}
- /**
- * Returns the width of the document in pixels
- */
+ /** Returns the width of the document in pixels */
public int getWidth() {
return mDocument.getWidth();
}
- /**
- * Returns the height of the document in pixels
- */
+ /** Returns the height of the document in pixels */
public int getHeight() {
return mDocument.getHeight();
}
@@ -77,7 +71,7 @@
* Paint the document
*
* @param context the provided PaintContext
- * @param theme the theme we want to use for this document.
+ * @param theme the theme we want to use for this document.
*/
public void paint(RemoteContext context, int theme) {
mDocument.paint(context, theme);
@@ -105,8 +99,7 @@
@Override
public String toString() {
- return "Document{\n"
- + mDocument + '}';
+ return "Document{\n" + mDocument + '}';
}
/**
@@ -120,6 +113,7 @@
/**
* Return a component associated with id
+ *
* @param id the component id
* @return the corresponding component or null if not found
*/
@@ -138,4 +132,3 @@
return mDocument.getStats();
}
}
-
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
index 3d78680..06bf4cd 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
@@ -29,9 +29,7 @@
import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
import com.android.internal.widget.remotecompose.player.platform.RemoteComposeCanvas;
-/**
- * A view to to display and play RemoteCompose documents
- */
+/** A view to to display and play RemoteCompose documents */
public class RemoteComposePlayer extends FrameLayout {
private RemoteComposeCanvas mInner;
@@ -73,10 +71,7 @@
public void setDocument(RemoteComposeDocument value) {
if (value != null) {
if (value.canBeDisplayed(
- MAX_SUPPORTED_MAJOR_VERSION,
- MAX_SUPPORTED_MINOR_VERSION, 0L
- )
- ) {
+ MAX_SUPPORTED_MAJOR_VERSION, MAX_SUPPORTED_MINOR_VERSION, 0L)) {
mInner.setDocument(value);
int contentBehavior = value.getDocument().getContentScroll();
applyContentBehavior(contentBehavior);
@@ -90,65 +85,58 @@
}
/**
- * Apply the content behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL) to the player,
- * adding or removing scrollviews as needed.
+ * Apply the content behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL) to the player, adding or
+ * removing scrollviews as needed.
*
* @param contentBehavior document content behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
*/
private void applyContentBehavior(int contentBehavior) {
switch (contentBehavior) {
- case RootContentBehavior.SCROLL_HORIZONTAL: {
+ case RootContentBehavior.SCROLL_HORIZONTAL:
if (!(mInner.getParent() instanceof HorizontalScrollView)) {
((ViewGroup) mInner.getParent()).removeView(mInner);
removeAllViews();
- LayoutParams layoutParamsInner = new LayoutParams(
- LayoutParams.WRAP_CONTENT,
- LayoutParams.MATCH_PARENT);
+ LayoutParams layoutParamsInner =
+ new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
HorizontalScrollView horizontalScrollView =
new HorizontalScrollView(getContext());
horizontalScrollView.setBackgroundColor(Color.TRANSPARENT);
horizontalScrollView.setFillViewport(true);
horizontalScrollView.addView(mInner, layoutParamsInner);
- LayoutParams layoutParams = new LayoutParams(
- LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT);
+ LayoutParams layoutParams =
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
addView(horizontalScrollView, layoutParams);
}
- }
- break;
- case RootContentBehavior.SCROLL_VERTICAL: {
+ break;
+ case RootContentBehavior.SCROLL_VERTICAL:
if (!(mInner.getParent() instanceof ScrollView)) {
((ViewGroup) mInner.getParent()).removeView(mInner);
removeAllViews();
- LayoutParams layoutParamsInner = new LayoutParams(
- LayoutParams.MATCH_PARENT,
- LayoutParams.WRAP_CONTENT);
+ LayoutParams layoutParamsInner =
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
ScrollView scrollView = new ScrollView(getContext());
scrollView.setBackgroundColor(Color.TRANSPARENT);
scrollView.setFillViewport(true);
scrollView.addView(mInner, layoutParamsInner);
- LayoutParams layoutParams = new LayoutParams(
- LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT);
+ LayoutParams layoutParams =
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
addView(scrollView, layoutParams);
}
- }
- break;
+ break;
default:
if (mInner.getParent() != this) {
((ViewGroup) mInner.getParent()).removeView(mInner);
removeAllViews();
- LayoutParams layoutParams = new LayoutParams(
- LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT);
+ LayoutParams layoutParams =
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
addView(mInner, layoutParams);
}
}
}
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
- LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT);
+ LayoutParams layoutParams =
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
setBackgroundColor(Color.TRANSPARENT);
mInner = new RemoteComposeCanvas(context, attrs, defStyleAttr);
mInner.setBackgroundColor(Color.TRANSPARENT);
@@ -241,24 +229,25 @@
* Add a callback for handling click events on the document
*
* @param callback the callback lambda that will be used when a click is detected
- * <p>
- * The parameter of the callback are:
- * id : the id of the clicked area
- * metadata: a client provided unstructured string associated with that area
+ * <p>The parameter of the callback are:
+ * <ul>
+ * <li>id : the id of the clicked area
+ * <li>metadata: a client provided unstructured string associated with that area
+ * </ul>
*/
public void addClickListener(ClickCallbacks callback) {
mInner.addClickListener((id, metadata) -> callback.click(id, metadata));
}
/**
- * Set the playback theme for the document. This allows to filter operations in order
- * to have the document adapt to the given theme. This method is intended to be used
- * to support night/light themes (system or app level), not custom themes.
+ * Set the playback theme for the document. This allows to filter operations in order to have
+ * the document adapt to the given theme. This method is intended to be used to support
+ * night/light themes (system or app level), not custom themes.
*
- * @param theme the theme used for playing the document. Possible values for theme are:
- * - Theme.UNSPECIFIED -- all instructions in the document will be executed
- * - Theme.DARK -- only executed NON Light theme instructions
- * - Theme.LIGHT -- only executed NON Dark theme instructions
+ * @param theme the theme used for playing the document. Possible values for theme are: -
+ * Theme.UNSPECIFIED -- all instructions in the document will be executed - Theme.DARK --
+ * only executed NON Light theme instructions - Theme.LIGHT -- only executed NON Dark theme
+ * instructions
*/
public void setTheme(int theme) {
if (mInner.getTheme() != theme) {
@@ -277,8 +266,7 @@
}
/**
- * This sets a color based on its name. Overriding the color set in
- * the document.
+ * This sets a color based on its name. Overriding the color set in the document.
*
* @param colorName Name of the color
* @param colorValue The new color value
@@ -364,7 +352,7 @@
case "colorFocusedHighlight":
setRColor(s, android.R.attr.colorFocusedHighlight);
break;
- case "colorForeground": // General foreground color for views.
+ case "colorForeground": // General foreground color for views.
setRColor(s, android.R.attr.colorForeground);
break;
// Foreground color for inverse backgrounds.
@@ -483,15 +471,14 @@
}
private int getColorFromResource(int id) {
+
TypedValue typedValue = new TypedValue();
- try (TypedArray arr = getContext()
- .getApplicationContext()
- .obtainStyledAttributes(typedValue.data, new int[]{id})) {
+ try (TypedArray arr =
+ getContext()
+ .getApplicationContext()
+ .obtainStyledAttributes(typedValue.data, new int[] {id})) {
int color = arr.getColor(0, -1);
return color;
}
}
-
-
}
-
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
index 4416cf7..f59a0d3 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
@@ -43,8 +43,8 @@
import java.util.List;
/**
- * An implementation of PaintContext for the Android Canvas.
- * This is used to play the RemoteCompose operations on Android.
+ * An implementation of PaintContext for the Android Canvas. This is used to play the RemoteCompose
+ * operations on Android.
*/
public class AndroidPaintContext extends PaintContext {
Paint mPaint = new Paint();
@@ -83,37 +83,36 @@
/**
* Draw an image onto the canvas
*
- * @param imageId the id of the image
- * @param srcLeft left coordinate of the source area
- * @param srcTop top coordinate of the source area
- * @param srcRight right coordinate of the source area
+ * @param imageId the id of the image
+ * @param srcLeft left coordinate of the source area
+ * @param srcTop top coordinate of the source area
+ * @param srcRight right coordinate of the source area
* @param srcBottom bottom coordinate of the source area
- * @param dstLeft left coordinate of the destination area
- * @param dstTop top coordinate of the destination area
- * @param dstRight right coordinate of the destination area
+ * @param dstLeft left coordinate of the destination area
+ * @param dstTop top coordinate of the destination area
+ * @param dstRight right coordinate of the destination area
* @param dstBottom bottom coordinate of the destination area
*/
-
@Override
- public void drawBitmap(int imageId,
- int srcLeft,
- int srcTop,
- int srcRight,
- int srcBottom,
- int dstLeft,
- int dstTop,
- int dstRight,
- int dstBottom,
- int cdId) {
+ public void drawBitmap(
+ int imageId,
+ int srcLeft,
+ int srcTop,
+ int srcRight,
+ int srcBottom,
+ int dstLeft,
+ int dstTop,
+ int dstRight,
+ int dstBottom,
+ int cdId) {
AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext;
if (androidContext.mRemoteComposeState.containsId(imageId)) {
- Bitmap bitmap = (Bitmap) androidContext.mRemoteComposeState
- .getFromId(imageId);
+ Bitmap bitmap = (Bitmap) androidContext.mRemoteComposeState.getFromId(imageId);
mCanvas.drawBitmap(
bitmap,
new Rect(srcLeft, srcTop, srcRight, srcBottom),
- new Rect(dstLeft, dstTop, dstRight, dstBottom), mPaint
- );
+ new Rect(dstLeft, dstTop, dstRight, dstBottom),
+ mPaint);
}
}
@@ -128,28 +127,23 @@
}
@Override
- public void drawArc(float left,
- float top,
- float right,
- float bottom,
- float startAngle,
- float sweepAngle) {
- mCanvas.drawArc(left, top, right, bottom, startAngle,
- sweepAngle, true, mPaint);
+ public void drawArc(
+ float left, float top, float right, float bottom, float startAngle, float sweepAngle) {
+ mCanvas.drawArc(left, top, right, bottom, startAngle, sweepAngle, false, mPaint);
}
@Override
- public void drawBitmap(int id,
- float left,
- float top,
- float right,
- float bottom) {
+ public void drawSector(
+ float left, float top, float right, float bottom, float startAngle, float sweepAngle) {
+ mCanvas.drawArc(left, top, right, bottom, startAngle, sweepAngle, true, mPaint);
+ }
+
+ @Override
+ public void drawBitmap(int id, float left, float top, float right, float bottom) {
AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext;
if (androidContext.mRemoteComposeState.containsId(id)) {
- Bitmap bitmap =
- (Bitmap) androidContext.mRemoteComposeState.getFromId(id);
- Rect src = new Rect(0, 0,
- bitmap.getWidth(), bitmap.getHeight());
+ Bitmap bitmap = (Bitmap) androidContext.mRemoteComposeState.getFromId(id);
+ Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
RectF dst = new RectF(left, top, right, bottom);
mCanvas.drawBitmap(bitmap, src, dst, mPaint);
}
@@ -191,21 +185,13 @@
}
@Override
- public void drawRoundRect(float left,
- float top,
- float right,
- float bottom,
- float radiusX,
- float radiusY) {
- mCanvas.drawRoundRect(left, top, right, bottom,
- radiusX, radiusY, mPaint);
+ public void drawRoundRect(
+ float left, float top, float right, float bottom, float radiusX, float radiusY) {
+ mCanvas.drawRoundRect(left, top, right, bottom, radiusX, radiusY, mPaint);
}
@Override
- public void drawTextOnPath(int textId,
- int pathId,
- float hOffset,
- float vOffset) {
+ public void drawTextOnPath(int textId, int pathId, float hOffset, float vOffset) {
mCanvas.drawTextOnPath(getText(textId), getPath(pathId, 0, 1), hOffset, vOffset, mPaint);
}
@@ -237,14 +223,15 @@
}
@Override
- public void drawTextRun(int textID,
- int start,
- int end,
- int contextStart,
- int contextEnd,
- float x,
- float y,
- boolean rtl) {
+ public void drawTextRun(
+ int textID,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean rtl) {
String textToPaint = getText(textID);
if (textToPaint == null) {
@@ -262,11 +249,7 @@
}
@Override
- public void drawTweenPath(int path1Id,
- int path2Id,
- float tween,
- float start,
- float end) {
+ public void drawTweenPath(int path1Id, int path2Id, float tween, float start, float end) {
mCanvas.drawPath(getPath(path1Id, path2Id, tween, start, end), mPaint);
}
@@ -381,224 +364,219 @@
/**
* This applies paint changes to the current paint
*
- * @param mPaintData the list change to the paint
+ * @param paintData the list change to the paint
*/
@Override
- public void applyPaint(PaintBundle mPaintData) {
- mPaintData.applyPaintChange((PaintContext) this, new PaintChanges() {
- @Override
- public void setTextSize(float size) {
- mPaint.setTextSize(size);
- }
-
- @Override
- public void setTypeFace(int fontType, int weight, boolean italic) {
- int[] type = new int[]{Typeface.NORMAL, Typeface.BOLD,
- Typeface.ITALIC, Typeface.BOLD_ITALIC};
-
- switch (fontType) {
- case PaintBundle.FONT_TYPE_DEFAULT: {
- if (weight == 400 && !italic) { // for normal case
- mPaint.setTypeface(Typeface.DEFAULT);
- } else {
- mPaint.setTypeface(Typeface.create(Typeface.DEFAULT,
- weight, italic));
- }
- break;
+ public void applyPaint(PaintBundle paintData) {
+ paintData.applyPaintChange(
+ (PaintContext) this,
+ new PaintChanges() {
+ @Override
+ public void setTextSize(float size) {
+ mPaint.setTextSize(size);
}
- case PaintBundle.FONT_TYPE_SERIF: {
- if (weight == 400 && !italic) { // for normal case
- mPaint.setTypeface(Typeface.SERIF);
- } else {
- mPaint.setTypeface(Typeface.create(Typeface.SERIF,
- weight, italic));
- }
- break;
- }
- case PaintBundle.FONT_TYPE_SANS_SERIF: {
- if (weight == 400 && !italic) { // for normal case
- mPaint.setTypeface(Typeface.SANS_SERIF);
- } else {
- mPaint.setTypeface(
- Typeface.create(Typeface.SANS_SERIF,
- weight, italic));
- }
- break;
- }
- case PaintBundle.FONT_TYPE_MONOSPACE: {
- if (weight == 400 && !italic) { // for normal case
- mPaint.setTypeface(Typeface.MONOSPACE);
- } else {
- mPaint.setTypeface(
- Typeface.create(Typeface.MONOSPACE,
- weight, italic));
- }
- break;
- }
- }
+ @Override
+ public void setTypeFace(int fontType, int weight, boolean italic) {
+ int[] type =
+ new int[] {
+ Typeface.NORMAL,
+ Typeface.BOLD,
+ Typeface.ITALIC,
+ Typeface.BOLD_ITALIC
+ };
- }
+ switch (fontType) {
+ case PaintBundle.FONT_TYPE_DEFAULT:
+ if (weight == 400 && !italic) { // for normal case
+ mPaint.setTypeface(Typeface.DEFAULT);
+ } else {
+ mPaint.setTypeface(
+ Typeface.create(Typeface.DEFAULT, weight, italic));
+ }
+ break;
+ case PaintBundle.FONT_TYPE_SERIF:
+ if (weight == 400 && !italic) { // for normal case
+ mPaint.setTypeface(Typeface.SERIF);
+ } else {
+ mPaint.setTypeface(
+ Typeface.create(Typeface.SERIF, weight, italic));
+ }
+ break;
+ case PaintBundle.FONT_TYPE_SANS_SERIF:
+ if (weight == 400 && !italic) { // for normal case
+ mPaint.setTypeface(Typeface.SANS_SERIF);
+ } else {
+ mPaint.setTypeface(
+ Typeface.create(Typeface.SANS_SERIF, weight, italic));
+ }
+ break;
+ case PaintBundle.FONT_TYPE_MONOSPACE:
+ if (weight == 400 && !italic) { // for normal case
+ mPaint.setTypeface(Typeface.MONOSPACE);
+ } else {
+ mPaint.setTypeface(
+ Typeface.create(Typeface.MONOSPACE, weight, italic));
+ }
- @Override
- public void setStrokeWidth(float width) {
- mPaint.setStrokeWidth(width);
- }
-
- @Override
- public void setColor(int color) {
- mPaint.setColor(color);
- }
-
- @Override
- public void setStrokeCap(int cap) {
- mPaint.setStrokeCap(Paint.Cap.values()[cap]);
- }
-
- @Override
- public void setStyle(int style) {
- mPaint.setStyle(Paint.Style.values()[style]);
- }
-
- @Override
- public void setShader(int shaderId) {
- // TODO this stuff should check the shader creation
- if (shaderId == 0) {
- mPaint.setShader(null);
- return;
- }
- ShaderData data = getShaderData(shaderId);
- RuntimeShader shader = new RuntimeShader(getText(data.getShaderTextId()));
- String[] names = data.getUniformFloatNames();
- for (int i = 0; i < names.length; i++) {
- String name = names[i];
- float[] val = data.getUniformFloats(name);
- shader.setFloatUniform(name, val);
- }
- names = data.getUniformIntegerNames();
- for (int i = 0; i < names.length; i++) {
- String name = names[i];
- int[] val = data.getUniformInts(name);
- shader.setIntUniform(name, val);
- }
- names = data.getUniformBitmapNames();
- for (int i = 0; i < names.length; i++) {
- String name = names[i];
- int val = data.getUniformBitmapId(name);
- }
- mPaint.setShader(shader);
- }
-
- @Override
- public void setImageFilterQuality(int quality) {
- Utils.log(" quality =" + quality);
- }
-
- @Override
- public void setBlendMode(int mode) {
- mPaint.setBlendMode(origamiToBlendMode(mode));
- }
-
- @Override
- public void setAlpha(float a) {
- mPaint.setAlpha((int) (255 * a));
- }
-
- @Override
- public void setStrokeMiter(float miter) {
- mPaint.setStrokeMiter(miter);
- }
-
- @Override
- public void setStrokeJoin(int join) {
- mPaint.setStrokeJoin(Paint.Join.values()[join]);
- }
-
- @Override
- public void setFilterBitmap(boolean filter) {
- mPaint.setFilterBitmap(filter);
- }
-
- @Override
- public void setAntiAlias(boolean aa) {
- mPaint.setAntiAlias(aa);
- }
-
- @Override
- public void clear(long mask) {
- if (true) return;
- long m = mask;
- int k = 1;
- while (m > 0) {
- if ((m & 1) == 1L) {
- switch (k) {
-
- case PaintBundle.COLOR_FILTER:
- mPaint.setColorFilter(null);
break;
}
}
- k++;
- m = m >> 1;
- }
- }
- Shader.TileMode[] mTileModes = new Shader.TileMode[]{
- Shader.TileMode.CLAMP,
- Shader.TileMode.REPEAT,
- Shader.TileMode.MIRROR};
+ @Override
+ public void setStrokeWidth(float width) {
+ mPaint.setStrokeWidth(width);
+ }
- @Override
- public void setLinearGradient(int[] colors,
- float[] stops,
- float startX,
- float startY,
- float endX,
- float endY,
- int tileMode) {
- mPaint.setShader(new LinearGradient(startX,
- startY,
- endX,
- endY, colors, stops, mTileModes[tileMode]));
+ @Override
+ public void setColor(int color) {
+ mPaint.setColor(color);
+ }
- }
+ @Override
+ public void setStrokeCap(int cap) {
+ mPaint.setStrokeCap(Paint.Cap.values()[cap]);
+ }
- @Override
- public void setRadialGradient(int[] colors,
- float[] stops,
- float centerX,
- float centerY,
- float radius,
- int tileMode) {
- mPaint.setShader(new RadialGradient(centerX, centerY, radius,
- colors, stops, mTileModes[tileMode]));
- }
+ @Override
+ public void setStyle(int style) {
+ mPaint.setStyle(Paint.Style.values()[style]);
+ }
- @Override
- public void setSweepGradient(int[] colors,
- float[] stops,
- float centerX,
- float centerY) {
- mPaint.setShader(new SweepGradient(centerX, centerY, colors, stops));
+ @Override
+ public void setShader(int shaderId) {
+ // TODO this stuff should check the shader creation
+ if (shaderId == 0) {
+ mPaint.setShader(null);
+ return;
+ }
+ ShaderData data = getShaderData(shaderId);
+ RuntimeShader shader = new RuntimeShader(getText(data.getShaderTextId()));
+ String[] names = data.getUniformFloatNames();
+ for (int i = 0; i < names.length; i++) {
+ String name = names[i];
+ float[] val = data.getUniformFloats(name);
+ shader.setFloatUniform(name, val);
+ }
+ names = data.getUniformIntegerNames();
+ for (int i = 0; i < names.length; i++) {
+ String name = names[i];
+ int[] val = data.getUniformInts(name);
+ shader.setIntUniform(name, val);
+ }
+ names = data.getUniformBitmapNames();
+ for (int i = 0; i < names.length; i++) {
+ String name = names[i];
+ int val = data.getUniformBitmapId(name);
+ }
+ mPaint.setShader(shader);
+ }
- }
+ @Override
+ public void setImageFilterQuality(int quality) {
+ Utils.log(" quality =" + quality);
+ }
- @Override
- public void setColorFilter(int color, int mode) {
- PorterDuff.Mode pmode = origamiToPorterDuffMode(mode);
- if (pmode != null) {
- mPaint.setColorFilter(
- new PorterDuffColorFilter(color, pmode));
- }
- }
- });
+ @Override
+ public void setBlendMode(int mode) {
+ mPaint.setBlendMode(origamiToBlendMode(mode));
+ }
+
+ @Override
+ public void setAlpha(float a) {
+ mPaint.setAlpha((int) (255 * a));
+ }
+
+ @Override
+ public void setStrokeMiter(float miter) {
+ mPaint.setStrokeMiter(miter);
+ }
+
+ @Override
+ public void setStrokeJoin(int join) {
+ mPaint.setStrokeJoin(Paint.Join.values()[join]);
+ }
+
+ @Override
+ public void setFilterBitmap(boolean filter) {
+ mPaint.setFilterBitmap(filter);
+ }
+
+ @Override
+ public void setAntiAlias(boolean aa) {
+ mPaint.setAntiAlias(aa);
+ }
+
+ @Override
+ public void clear(long mask) {
+ if ((mask & (1L << PaintBundle.COLOR_FILTER)) != 0) {
+ mPaint.setColorFilter(null);
+ }
+ }
+
+ Shader.TileMode[] mTileModes =
+ new Shader.TileMode[] {
+ Shader.TileMode.CLAMP,
+ Shader.TileMode.REPEAT,
+ Shader.TileMode.MIRROR
+ };
+
+ @Override
+ public void setLinearGradient(
+ int[] colors,
+ float[] stops,
+ float startX,
+ float startY,
+ float endX,
+ float endY,
+ int tileMode) {
+ mPaint.setShader(
+ new LinearGradient(
+ startX,
+ startY,
+ endX,
+ endY,
+ colors,
+ stops,
+ mTileModes[tileMode]));
+ }
+
+ @Override
+ public void setRadialGradient(
+ int[] colors,
+ float[] stops,
+ float centerX,
+ float centerY,
+ float radius,
+ int tileMode) {
+ mPaint.setShader(
+ new RadialGradient(
+ centerX,
+ centerY,
+ radius,
+ colors,
+ stops,
+ mTileModes[tileMode]));
+ }
+
+ @Override
+ public void setSweepGradient(
+ int[] colors, float[] stops, float centerX, float centerY) {
+ mPaint.setShader(new SweepGradient(centerX, centerY, colors, stops));
+ }
+
+ @Override
+ public void setColorFilter(int color, int mode) {
+ PorterDuff.Mode pmode = origamiToPorterDuffMode(mode);
+ if (pmode != null) {
+ mPaint.setColorFilter(new PorterDuffColorFilter(color, pmode));
+ }
+ }
+ });
}
@Override
- public void matrixScale(float scaleX,
- float scaleY,
- float centerX,
- float centerY) {
+ public void matrixScale(float scaleX, float scaleY, float centerX, float centerY) {
if (Float.isNaN(centerX)) {
mCanvas.scale(scaleX, scaleY);
} else {
@@ -622,7 +600,6 @@
mCanvas.rotate(rotate);
} else {
mCanvas.rotate(rotate, pivotX, pivotY);
-
}
}
@@ -642,15 +619,27 @@
}
@Override
- public void roundedClipRect(float width, float height,
- float topStart, float topEnd,
- float bottomStart, float bottomEnd) {
+ public void roundedClipRect(
+ float width,
+ float height,
+ float topStart,
+ float topEnd,
+ float bottomStart,
+ float bottomEnd) {
Path roundedPath = new Path();
- float[] radii = new float[] { topStart, topStart,
- topEnd, topEnd, bottomEnd, bottomEnd, bottomStart, bottomStart};
+ float[] radii =
+ new float[] {
+ topStart,
+ topStart,
+ topEnd,
+ topEnd,
+ bottomEnd,
+ bottomEnd,
+ bottomStart,
+ bottomStart
+ };
- roundedPath.addRoundRect(0f, 0f, width, height,
- radii, android.graphics.Path.Direction.CW);
+ roundedPath.addRoundRect(0f, 0f, width, height, radii, android.graphics.Path.Direction.CW);
mCanvas.clipPath(roundedPath);
}
@@ -660,7 +649,7 @@
if (regionOp == ClipPath.DIFFERENCE) {
mCanvas.clipOutPath(path); // DIFFERENCE
} else {
- mCanvas.clipPath(path); // INTERSECT
+ mCanvas.clipPath(path); // INTERSECT
}
}
@@ -669,11 +658,7 @@
mPaint.reset();
}
- private Path getPath(int path1Id,
- int path2Id,
- float tween,
- float start,
- float end) {
+ private Path getPath(int path1Id, int path2Id, float tween, float start, float end) {
if (tween == 0.0f) {
return getPath(path1Id, start, end);
}
@@ -681,10 +666,8 @@
return getPath(path2Id, start, end);
}
AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext;
- float[] data1 =
- (float[]) androidContext.mRemoteComposeState.getFromId(path1Id);
- float[] data2 =
- (float[]) androidContext.mRemoteComposeState.getFromId(path2Id);
+ float[] data1 = (float[]) androidContext.mRemoteComposeState.getFromId(path1Id);
+ float[] data2 = (float[]) androidContext.mRemoteComposeState.getFromId(path2Id);
float[] tmp = new float[data2.length];
for (int i = 0; i < tmp.length; i++) {
if (Float.isNaN(data1[i]) || Float.isNaN(data2[i])) {
@@ -702,8 +685,7 @@
AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext;
Path path = new Path();
if (androidContext.mRemoteComposeState.containsId(id)) {
- float[] data =
- (float[]) androidContext.mRemoteComposeState.getFromId(id);
+ float[] data = (float[]) androidContext.mRemoteComposeState.getFromId(id);
FloatsToPath.genPath(path, data, start, end);
}
return path;
@@ -717,4 +699,3 @@
return (ShaderData) mContext.mRemoteComposeState.getFromId(id);
}
}
-
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPlatformServices.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPlatformServices.java
new file mode 100644
index 0000000..f9b22a2
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPlatformServices.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.player.platform;
+
+import android.graphics.Bitmap;
+import android.graphics.Path;
+import android.graphics.PathIterator;
+
+import com.android.internal.widget.remotecompose.core.Platform;
+import com.android.internal.widget.remotecompose.core.operations.PathData;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+
+/** Services that are needed to be provided by the platform during encoding. */
+public class AndroidPlatformServices implements Platform {
+ @Override
+ public byte[] imageToByteArray(Object image) {
+ if (image instanceof Bitmap) {
+ // let's create a bitmap
+ ByteArrayOutputStream byteArrayBitmapStream = new ByteArrayOutputStream();
+ ((Bitmap) image).compress(Bitmap.CompressFormat.PNG, 90, byteArrayBitmapStream);
+ return byteArrayBitmapStream.toByteArray();
+ }
+ return null;
+ }
+
+ @Override
+ public int getImageWidth(Object image) {
+ if (image instanceof Bitmap) {
+ return ((Bitmap) image).getWidth();
+ }
+ return 0;
+ }
+
+ @Override
+ public int getImageHeight(Object image) {
+ if (image instanceof Bitmap) {
+ return ((Bitmap) image).getHeight();
+ }
+ return 0;
+ }
+
+ @Override
+ public float[] pathToFloatArray(Object path) {
+ // if (path is RemotePath) {
+ // return path.createFloatArray()
+ // }
+
+ if (path instanceof Path) {
+ return androidPathToFloatArray((Path) path);
+ }
+
+ return null;
+ }
+
+ private float[] androidPathToFloatArray(Path path) {
+ PathIterator i = path.getPathIterator();
+ int estimatedSize = 0;
+
+ while (i.hasNext()) {
+ i.next();
+ estimatedSize++;
+ }
+
+ PathIterator iter = path.getPathIterator();
+ float[] pathFloat = new float[estimatedSize * 10];
+
+ int count = 0;
+ while (i.hasNext()) {
+ PathIterator.Segment seg = i.next();
+
+ switch (seg.getVerb()) {
+ case PathIterator.VERB_MOVE:
+ pathFloat[count++] = PathData.MOVE_NAN;
+ break;
+ case PathIterator.VERB_LINE:
+ pathFloat[count++] = PathData.LINE_NAN;
+ break;
+ case PathIterator.VERB_QUAD:
+ pathFloat[count++] = PathData.QUADRATIC_NAN;
+ break;
+ case PathIterator.VERB_CONIC:
+ pathFloat[count++] = PathData.CONIC_NAN;
+ break;
+ case PathIterator.VERB_CUBIC:
+ pathFloat[count++] = PathData.CUBIC_NAN;
+ break;
+ case PathIterator.VERB_CLOSE:
+ pathFloat[count++] = PathData.CLOSE_NAN;
+ break;
+ case PathIterator.VERB_DONE:
+ pathFloat[count++] = PathData.DONE_NAN;
+ break;
+ }
+ for (float p : seg.getPoints()) {
+ pathFloat[count++] = p;
+ }
+ if (seg.getVerb() == PathIterator.VERB_CONIC) {
+ pathFloat[count++] = seg.getConicWeight();
+ }
+ }
+
+ return Arrays.copyOf(pathFloat, count);
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
index c989375..e7c0cc8 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
@@ -24,13 +24,14 @@
import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
import com.android.internal.widget.remotecompose.core.operations.ShaderData;
import com.android.internal.widget.remotecompose.core.operations.utilities.ArrayAccess;
+import com.android.internal.widget.remotecompose.core.operations.utilities.DataMap;
import java.util.HashMap;
/**
* An implementation of Context for Android.
- * <p>
- * This is used to play the RemoteCompose operations on Android.
+ *
+ * <p>This is used to play the RemoteCompose operations on Android.
*/
class AndroidRemoteContext extends RemoteContext {
@@ -127,6 +128,16 @@
}
@Override
+ public void putDataMap(int id, DataMap map) {
+ mRemoteComposeState.putDataMap(id, map);
+ }
+
+ @Override
+ public DataMap getDataMap(int id) {
+ return mRemoteComposeState.getDataMap(id);
+ }
+
+ @Override
public void runAction(int id, String metadata) {
mDocument.performClick(id);
}
@@ -140,7 +151,7 @@
/**
* Decode a byte array into an image and cache it using the given imageId
*
- * @param width with of image to be loaded
+ * @param width with of image to be loaded
* @param height height of image to be loaded
* @param bitmap a byte array containing the image information
* @oaram imageId the id of the image
@@ -223,8 +234,18 @@
}
@Override
+ public void putObject(int id, Object value) {
+ mRemoteComposeState.updateObject(id, value);
+ }
+
+ @Override
+ public Object getObject(int id) {
+ return mRemoteComposeState.getObject(id);
+ }
+
+ @Override
public int getInteger(int id) {
- return mRemoteComposeState.getInteger(id);
+ return mRemoteComposeState.getInteger(id);
}
@Override
@@ -252,17 +273,16 @@
///////////////////////////////////////////////////////////////////////////////////////////////
@Override
- public void addClickArea(int id,
- int contentDescriptionId,
- float left,
- float top,
- float right,
- float bottom,
- int metadataId) {
+ public void addClickArea(
+ int id,
+ int contentDescriptionId,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ int metadataId) {
String contentDescription = (String) mRemoteComposeState.getFromId(contentDescriptionId);
String metadata = (String) mRemoteComposeState.getFromId(metadataId);
mDocument.addClickArea(id, contentDescription, left, top, right, bottom, metadata);
}
-
}
-
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java b/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java
index 329178a..fdd9aad 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java
@@ -20,9 +20,7 @@
import android.graphics.Paint;
import android.view.View;
-/**
- * Implementation for the click handling
- */
+/** Implementation for the click handling */
class ClickAreaView extends View {
private int mId;
private String mMetadata;
@@ -30,8 +28,8 @@
private boolean mDebug;
- ClickAreaView(Context context, boolean debug, int id,
- String contentDescription, String metadata) {
+ ClickAreaView(
+ Context context, boolean debug, int id, String contentDescription, String metadata) {
super(context);
this.mId = id;
this.mMetadata = metadata;
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/FloatsToPath.java b/core/java/com/android/internal/widget/remotecompose/player/platform/FloatsToPath.java
index d75232a..8d3440c 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/FloatsToPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/FloatsToPath.java
@@ -23,72 +23,56 @@
import com.android.internal.widget.remotecompose.core.operations.PathData;
public class FloatsToPath {
- public static void genPath(Path retPath,
- float[] floatPath,
- float start,
- float stop) {
+ public static void genPath(Path retPath, float[] floatPath, float start, float stop) {
int i = 0;
Path path = new Path(); // todo this should be cached for performance
while (i < floatPath.length) {
switch (idFromNan(floatPath[i])) {
- case PathData.MOVE: {
+ case PathData.MOVE:
i++;
path.moveTo(floatPath[i + 0], floatPath[i + 1]);
i += 2;
- }
- break;
- case PathData.LINE: {
+ break;
+ case PathData.LINE:
i += 3;
path.lineTo(floatPath[i + 0], floatPath[i + 1]);
i += 2;
- }
- break;
- case PathData.QUADRATIC: {
+ break;
+ case PathData.QUADRATIC:
i += 3;
path.quadTo(
+ floatPath[i + 0], floatPath[i + 1], floatPath[i + 2], floatPath[i + 3]);
+ i += 4;
+ break;
+ case PathData.CONIC:
+ i += 3;
+
+ path.conicTo(
floatPath[i + 0],
floatPath[i + 1],
floatPath[i + 2],
- floatPath[i + 3]
- );
- i += 4;
+ floatPath[i + 3],
+ floatPath[i + 4]);
- }
- break;
- case PathData.CONIC: {
- i += 3;
- path.conicTo(
- floatPath[i + 0], floatPath[i + 1],
- floatPath[i + 2], floatPath[i + 3],
- floatPath[i + 4]
- );
i += 5;
- }
- break;
- case PathData.CUBIC: {
+ break;
+ case PathData.CUBIC:
i += 3;
path.cubicTo(
floatPath[i + 0], floatPath[i + 1],
floatPath[i + 2], floatPath[i + 3],
- floatPath[i + 4], floatPath[i + 5]
- );
+ floatPath[i + 4], floatPath[i + 5]);
i += 6;
- }
- break;
- case PathData.CLOSE: {
-
+ break;
+ case PathData.CLOSE:
path.close();
i++;
- }
- break;
- case PathData.DONE: {
+ break;
+ case PathData.DONE:
i++;
- }
- break;
- default: {
- System.err.println(" Odd command "
- + idFromNan(floatPath[i]));
- }
+ break;
+ default:
+ System.err.println(" Odd command " + idFromNan(floatPath[i]));
}
}
@@ -101,8 +85,7 @@
float len = measure.getLength();
float scaleStart = Math.max(start, 0f) * len;
float scaleStop = Math.min(stop, 1f) * len;
- measure.getSegment(scaleStart, scaleStop, retPath,
- true);
+ measure.getSegment(scaleStart, scaleStop, retPath, true);
}
} else {
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
index f91e158..7de6988 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
@@ -31,9 +31,7 @@
import java.util.Set;
-/**
- * Internal view handling the actual painting / interactions
- */
+/** Internal view handling the actual painting / interactions */
public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachStateChangeListener {
static final boolean USE_VIEW_AREA_CLICK = true; // Use views to represent click areas
@@ -102,13 +100,17 @@
private void updateClickAreas() {
if (USE_VIEW_AREA_CLICK && mDocument != null) {
mHasClickAreas = false;
- Set<CoreDocument.ClickAreaRepresentation> clickAreas = mDocument
- .getDocument().getClickAreas();
+ Set<CoreDocument.ClickAreaRepresentation> clickAreas =
+ mDocument.getDocument().getClickAreas();
removeAllViews();
for (CoreDocument.ClickAreaRepresentation area : clickAreas) {
- ClickAreaView viewArea = new ClickAreaView(getContext(), mDebug,
- area.getId(), area.getContentDescription(),
- area.getMetadata());
+ ClickAreaView viewArea =
+ new ClickAreaView(
+ getContext(),
+ mDebug,
+ area.getId(),
+ area.getContentDescription(),
+ area.getMetadata());
int w = (int) area.width();
int h = (int) area.height();
FrameLayout.LayoutParams param = new FrameLayout.LayoutParams(w, h);
@@ -116,8 +118,8 @@
param.height = h;
param.leftMargin = (int) area.getLeft();
param.topMargin = (int) area.getTop();
- viewArea.setOnClickListener(view1
- -> mDocument.getDocument().performClick(area.getId()));
+ viewArea.setOnClickListener(
+ view1 -> mDocument.getDocument().performClick(area.getId()));
addView(viewArea, param);
}
if (!clickAreas.isEmpty()) {
@@ -201,23 +203,19 @@
return super.onTouchEvent(event);
}
switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN: {
+ case MotionEvent.ACTION_DOWN:
mActionDownPoint.x = (int) event.getX();
mActionDownPoint.y = (int) event.getY();
mInActionDown = true;
return true;
- }
- case MotionEvent.ACTION_CANCEL: {
+ case MotionEvent.ACTION_CANCEL:
mInActionDown = false;
return true;
- }
- case MotionEvent.ACTION_UP: {
+ case MotionEvent.ACTION_UP:
mInActionDown = false;
performClick();
return true;
- }
- case MotionEvent.ACTION_MOVE: {
- }
+ case MotionEvent.ACTION_MOVE:
}
return false;
}
@@ -227,8 +225,9 @@
if (USE_VIEW_AREA_CLICK && mHasClickAreas) {
return super.performClick();
}
- mDocument.getDocument().onClick(mARContext,
- (float) mActionDownPoint.x, (float) mActionDownPoint.y);
+ mDocument
+ .getDocument()
+ .onClick(mARContext, (float) mActionDownPoint.x, (float) mActionDownPoint.y);
super.performClick();
invalidate();
return true;
@@ -305,6 +304,4 @@
invalidate();
}
}
-
}
-
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 816ace2..eb07f7c 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -249,6 +249,7 @@
"android_backup_BackupDataOutput.cpp",
"android_backup_FileBackupHelperBase.cpp",
"android_backup_BackupHelperDispatcher.cpp",
+ "android_app_PropertyInvalidatedCache.cpp",
"android_app_backup_FullBackup.cpp",
"android_content_res_ApkAssets.cpp",
"android_content_res_ObbScanner.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 76f66cd..821861e 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -177,6 +177,7 @@
extern int register_android_app_Activity(JNIEnv *env);
extern int register_android_app_ActivityThread(JNIEnv *env);
extern int register_android_app_NativeActivity(JNIEnv *env);
+extern int register_android_app_PropertyInvalidatedCache(JNIEnv* env);
extern int register_android_media_RemoteDisplay(JNIEnv *env);
extern int register_android_util_jar_StrictJarFile(JNIEnv* env);
extern int register_android_view_InputChannel(JNIEnv* env);
@@ -1659,6 +1660,7 @@
REG_JNI(register_android_app_Activity),
REG_JNI(register_android_app_ActivityThread),
REG_JNI(register_android_app_NativeActivity),
+ REG_JNI(register_android_app_PropertyInvalidatedCache),
REG_JNI(register_android_util_jar_StrictJarFile),
REG_JNI(register_android_view_InputChannel),
REG_JNI(register_android_view_InputEventReceiver),
diff --git a/core/jni/android_app_PropertyInvalidatedCache.cpp b/core/jni/android_app_PropertyInvalidatedCache.cpp
new file mode 100644
index 0000000..ead6666
--- /dev/null
+++ b/core/jni/android_app_PropertyInvalidatedCache.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "CacheNonce"
+
+#include <string.h>
+#include <memory.h>
+
+#include <atomic>
+
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/scoped_primitive_array.h>
+#include <android-base/logging.h>
+
+#include "core_jni_helpers.h"
+#include "android_app_PropertyInvalidatedCache.h"
+
+namespace {
+
+using namespace android::app::PropertyInvalidatedCache;
+
+// Convert a jlong to a nonce block. This is a convenience function that should be inlined by
+// the compiler.
+inline SystemCacheNonce* sysCache(jlong ptr) {
+ return reinterpret_cast<SystemCacheNonce*>(ptr);
+}
+
+// Return the number of nonces in the nonce block.
+jint getMaxNonce(JNIEnv*, jclass, jlong ptr) {
+ return sysCache(ptr)->getMaxNonce();
+}
+
+// Return the number of string bytes in the nonce block.
+jint getMaxByte(JNIEnv*, jclass, jlong ptr) {
+ return sysCache(ptr)->getMaxByte();
+}
+
+// Set the byte block. The first int is the hash to set and the second is the array to copy.
+// This should be synchronized in the Java layer.
+void setByteBlock(JNIEnv* env, jclass, jlong ptr, jint hash, jbyteArray val) {
+ ScopedByteArrayRO value(env, val);
+ if (value.get() == nullptr) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "null byte block");
+ return;
+ }
+ sysCache(ptr)->setByteBlock(hash, value.get(), value.size());
+}
+
+// Fetch the byte block. If the incoming hash is the same as the local hash, the Java layer is
+// presumed to have an up-to-date copy of the byte block; do not copy byte array. The local
+// hash is returned.
+jint getByteBlock(JNIEnv* env, jclass, jlong ptr, jint hash, jbyteArray val) {
+ if (sysCache(ptr)->getHash() == hash) {
+ return hash;
+ }
+ ScopedByteArrayRW value(env, val);
+ return sysCache(ptr)->getByteBlock(value.get(), value.size());
+}
+
+// Fetch the byte block hash.
+//
+// This is a CriticalNative method and therefore does not get the JNIEnv or jclass parameters.
+jint getByteBlockHash(jlong ptr) {
+ return sysCache(ptr)->getHash();
+}
+
+// Get a nonce value. So that this method can be CriticalNative, it returns 0 if the value is
+// out of range, rather than throwing an exception. This is a CriticalNative method and
+// therefore does not get the JNIEnv or jclass parameters.
+//
+// This method is @CriticalNative and does not take a JNIEnv* or jclass argument.
+jlong getNonce(jlong ptr, jint index) {
+ return sysCache(ptr)->getNonce(index);
+}
+
+// Set a nonce value. So that this method can be CriticalNative, it returns a boolean: false if
+// the index is out of range and true otherwise. Callers may test the returned boolean and
+// generate an exception.
+//
+// This method is @CriticalNative and does not take a JNIEnv* or jclass argument.
+jboolean setNonce(jlong ptr, jint index, jlong value) {
+ return sysCache(ptr)->setNonce(index, value);
+}
+
+static const JNINativeMethod gMethods[] = {
+ {"nativeGetMaxNonce", "(J)I", (void*) getMaxNonce },
+ {"nativeGetMaxByte", "(J)I", (void*) getMaxByte },
+ {"nativeSetByteBlock", "(JI[B)V", (void*) setByteBlock },
+ {"nativeGetByteBlock", "(JI[B)I", (void*) getByteBlock },
+ {"nativeGetByteBlockHash", "(J)I", (void*) getByteBlockHash },
+ {"nativeGetNonce", "(JI)J", (void*) getNonce },
+ {"nativeSetNonce", "(JIJ)Z", (void*) setNonce },
+};
+
+static const char* kClassName = "android/app/PropertyInvalidatedCache";
+
+} // anonymous namespace
+
+namespace android {
+
+int register_android_app_PropertyInvalidatedCache(JNIEnv* env) {
+ RegisterMethodsOrDie(env, kClassName, gMethods, NELEM(gMethods));
+ return JNI_OK;
+}
+
+} // namespace android
diff --git a/core/jni/android_app_PropertyInvalidatedCache.h b/core/jni/android_app_PropertyInvalidatedCache.h
new file mode 100644
index 0000000..eefa8fa
--- /dev/null
+++ b/core/jni/android_app_PropertyInvalidatedCache.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <memory.h>
+
+#include <atomic>
+
+namespace android {
+namespace app {
+namespace PropertyInvalidatedCache {
+
+/**
+ * A cache nonce block contains an array of std::atomic<int64_t> and an array of bytes. The
+ * byte array has an associated hash. This class provides methods to read and write the fields
+ * of the block but it does not interpret the fields.
+ *
+ * On initialization, all fields are set to zero.
+ *
+ * In general, methods do not report errors. This allows the methods to be used in
+ * CriticalNative JNI APIs.
+ *
+ * The template is parameterized by the number of nonces it supports and the number of bytes in
+ * the string block.
+ */
+template<int maxNonce, size_t maxByte> class CacheNonce {
+
+ // The value of an unset field.
+ static const int UNSET = 0;
+
+ // A convenient typedef. The jbyteArray element type is jbyte, which the compiler treats as
+ // signed char.
+ typedef signed char block_t;
+
+ // The array of nonces
+ volatile std::atomic<int64_t> mNonce[maxNonce];
+
+ // The byte array. This is not atomic but it is guarded by the mByteHash.
+ volatile block_t mByteBlock[maxByte];
+
+ // The hash that validates the byte block
+ volatile std::atomic<int32_t> mByteHash;
+
+ // Pad the class to a multiple of 8 bytes.
+ int32_t _pad;
+
+ public:
+
+ // The expected size of this instance. This is a compile-time constant and can be used in a
+ // static assertion.
+ static const int expectedSize =
+ maxNonce * sizeof(std::atomic<int64_t>)
+ + sizeof(std::atomic<int32_t>)
+ + maxByte * sizeof(block_t)
+ + sizeof(int32_t);
+
+ // These provide run-time access to the sizing parameters.
+ int getMaxNonce() const {
+ return maxNonce;
+ }
+
+ size_t getMaxByte() const {
+ return maxByte;
+ }
+
+ // Construct and initialize the memory.
+ CacheNonce() {
+ for (int i = 0; i < maxNonce; i++) {
+ mNonce[i] = UNSET;
+ }
+ mByteHash = UNSET;
+ memset((void*) mByteBlock, UNSET, sizeof(mByteBlock));
+ }
+
+ // Fetch a nonce, returning UNSET if the index is out of range. This method specifically
+ // does not throw or generate an error if the index is out of range; this allows the method
+ // to be called in a CriticalNative JNI API.
+ int64_t getNonce(int index) const {
+ if (index < 0 || index >= maxNonce) {
+ return UNSET;
+ } else {
+ return mNonce[index];
+ }
+ }
+
+ // Set a nonce and return true. Return false if the index is out of range. This method
+ // specifically does not throw or generate an error if the index is out of range; this
+ // allows the method to be called in a CriticalNative JNI API.
+ bool setNonce(int index, int64_t value) {
+ if (index < 0 || index >= maxNonce) {
+ return false;
+ } else {
+ mNonce[index] = value;
+ return true;
+ }
+ }
+
+ // Fetch just the byte-block hash
+ int32_t getHash() const {
+ return mByteHash;
+ }
+
+ // Copy the byte block to the target and return the current hash.
+ int32_t getByteBlock(block_t* block, size_t len) const {
+ memcpy(block, (void*) mByteBlock, std::min(maxByte, len));
+ return mByteHash;
+ }
+
+ // Set the byte block and the hash.
+ void setByteBlock(int hash, const block_t* block, size_t len) {
+ memcpy((void*) mByteBlock, block, len = std::min(maxByte, len));
+ mByteHash = hash;
+ }
+};
+
+/**
+ * Sizing parameters for the system_server PropertyInvalidatedCache support. A client can
+ * retrieve the values through the accessors in CacheNonce instances.
+ */
+static const int MAX_NONCE = 64;
+static const int BYTE_BLOCK_SIZE = 8192;
+
+// The CacheNonce for system server holds 64 nonces with a string block of 8192 bytes.
+typedef CacheNonce<MAX_NONCE, BYTE_BLOCK_SIZE> SystemCacheNonce;
+
+// The goal of this assertion is to ensure that the data structure is the same size across 32-bit
+// and 64-bit systems.
+static_assert(sizeof(SystemCacheNonce) == SystemCacheNonce::expectedSize,
+ "Unexpected SystemCacheNonce size");
+
+} // namespace PropertyInvalidatedCache
+} // namespace app
+} // namespace android
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 10e49ef..50252c1 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -27,6 +27,7 @@
#include <camera/StringUtils.h>
#include <com_android_internal_camera_flags.h>
#include <cutils/properties.h>
+#include <gui/Flags.h>
#include <gui/GLConsumer.h>
#include <gui/Surface.h>
#include <nativehelper/JNIHelp.h>
@@ -715,16 +716,20 @@
sp<Camera> camera = get_native_camera(env, thiz, NULL);
if (camera == 0) return;
- sp<IGraphicBufferProducer> gbp;
sp<Surface> surface;
if (jSurface) {
surface = android_view_Surface_getSurface(env, jSurface);
- if (surface != NULL) {
- gbp = surface->getIGraphicBufferProducer();
- }
}
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+ if (camera->setPreviewTarget(surface) != NO_ERROR) {
+#else
+ sp<IGraphicBufferProducer> gbp;
+ if (surface != NULL) {
+ gbp = surface->getIGraphicBufferProducer();
+ }
if (camera->setPreviewTarget(gbp) != NO_ERROR) {
+#endif
jniThrowException(env, "java/io/IOException", "setPreviewTexture failed");
}
}
@@ -736,6 +741,9 @@
sp<Camera> camera = get_native_camera(env, thiz, NULL);
if (camera == 0) return;
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+ sp<Surface> surface;
+#endif
sp<IGraphicBufferProducer> producer = NULL;
if (jSurfaceTexture != NULL) {
producer = SurfaceTexture_getProducer(env, jSurfaceTexture);
@@ -744,10 +752,16 @@
"SurfaceTexture already released in setPreviewTexture");
return;
}
-
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+ surface = new Surface(producer);
+#endif
}
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+ if (camera->setPreviewTarget(surface) != NO_ERROR) {
+#else
if (camera->setPreviewTarget(producer) != NO_ERROR) {
+#endif
jniThrowException(env, "java/io/IOException",
"setPreviewTexture failed");
}
@@ -761,18 +775,32 @@
sp<Camera> camera = get_native_camera(env, thiz, &context);
if (camera == 0) return;
+#if !WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
sp<IGraphicBufferProducer> gbp;
+#endif
sp<Surface> surface;
if (jSurface) {
surface = android_view_Surface_getSurface(env, jSurface);
+ if (surface == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "android_view_Surface_getSurface failed");
+ return;
+ }
+
+#if !WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
if (surface != NULL) {
gbp = surface->getIGraphicBufferProducer();
}
+#endif
}
// Clear out normal preview callbacks
context->setCallbackMode(env, false, false);
// Then set up callback surface
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+ if (camera->setPreviewCallbackTarget(surface) != NO_ERROR) {
+#else
if (camera->setPreviewCallbackTarget(gbp) != NO_ERROR) {
+#endif
jniThrowException(env, "java/io/IOException", "setPreviewCallbackTarget failed");
}
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 755704a..f162b74 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -35,6 +35,7 @@
#include <android_runtime/android_view_SurfaceSession.h>
#include <cutils/ashmem.h>
#include <gui/ISurfaceComposer.h>
+#include <gui/JankInfo.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <jni.h>
@@ -2161,9 +2162,30 @@
jobjectArray jJankDataArray = env->NewObjectArray(jankData.size(),
gJankDataClassInfo.clazz, nullptr);
for (size_t i = 0; i < jankData.size(); i++) {
+ // The exposed constants in SurfaceControl are simplified, so we need to translate the
+ // jank type we get from SF to what is exposed in Java.
+ int sfJankType = jankData[i].jankType;
+ int javaJankType = 0x0; // SurfaceControl.JankData.JANK_NONE
+ if (sfJankType &
+ (JankType::DisplayHAL | JankType::SurfaceFlingerCpuDeadlineMissed |
+ JankType::SurfaceFlingerGpuDeadlineMissed | JankType::PredictionError |
+ JankType::SurfaceFlingerScheduling)) {
+ javaJankType |= 0x1; // SurfaceControl.JankData.JANK_COMPOSER
+ }
+ if (sfJankType & JankType::AppDeadlineMissed) {
+ javaJankType |= 0x2; // SurfaceControl.JankData.JANK_APPLICATION
+ }
+ if (sfJankType &
+ ~(JankType::DisplayHAL | JankType::SurfaceFlingerCpuDeadlineMissed |
+ JankType::SurfaceFlingerGpuDeadlineMissed | JankType::AppDeadlineMissed |
+ JankType::PredictionError | JankType::SurfaceFlingerScheduling |
+ JankType::BufferStuffing | JankType::SurfaceFlingerStuffing)) {
+ javaJankType |= 0x4; // SurfaceControl.JankData.JANK_OTHER
+ }
+
jobject jJankData =
env->NewObject(gJankDataClassInfo.clazz, gJankDataClassInfo.ctor,
- jankData[i].frameVsyncId, jankData[i].jankType,
+ jankData[i].frameVsyncId, javaJankType,
jankData[i].frameIntervalNs, jankData[i].scheduledAppFrameTimeNs,
jankData[i].actualAppFrameTimeNs);
env->SetObjectArrayElement(jJankDataArray, i, jJankData);
diff --git a/core/jni/com_android_internal_os_ApplicationSharedMemory.cpp b/core/jni/com_android_internal_os_ApplicationSharedMemory.cpp
index 453e539..cc1687c 100644
--- a/core/jni/com_android_internal_os_ApplicationSharedMemory.cpp
+++ b/core/jni/com_android_internal_os_ApplicationSharedMemory.cpp
@@ -29,8 +29,12 @@
#include "core_jni_helpers.h"
+#include "android_app_PropertyInvalidatedCache.h"
+
namespace {
+using namespace android::app::PropertyInvalidatedCache;
+
// Atomics should be safe to use across processes if they are lock free.
static_assert(std::atomic<int64_t>::is_always_lock_free == true,
"atomic<int64_t> is not always lock free");
@@ -64,12 +68,15 @@
void setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(int64_t offset) {
latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis = offset;
}
+
+ // The nonce storage for pic. The sizing is suitable for the system server module.
+ SystemCacheNonce systemPic;
};
// Update the expected value when modifying the members of SharedMemory.
// The goal of this assertion is to ensure that the data structure is the same size across 32-bit
// and 64-bit systems.
-static_assert(sizeof(SharedMemory) == 8, "Unexpected SharedMemory size");
+static_assert(sizeof(SharedMemory) == 8 + sizeof(SystemCacheNonce), "Unexpected SharedMemory size");
static jint nativeCreate(JNIEnv* env, jclass) {
// Create anonymous shared memory region
@@ -133,6 +140,12 @@
return sharedMemory->getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis();
}
+// This is a FastNative method. It takes the usual JNIEnv* and jclass* arguments.
+static jlong nativeGetSystemNonceBlock(JNIEnv*, jclass*, jlong ptr) {
+ SharedMemory* sharedMemory = reinterpret_cast<SharedMemory*>(ptr);
+ return reinterpret_cast<jlong>(&sharedMemory->systemPic);
+}
+
static const JNINativeMethod gMethods[] = {
{"nativeCreate", "()I", (void*)nativeCreate},
{"nativeMap", "(IZ)J", (void*)nativeMap},
@@ -143,16 +156,17 @@
(void*)nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis},
{"nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis", "(J)J",
(void*)nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis},
+ {"nativeGetSystemNonceBlock", "(J)J", (void*) nativeGetSystemNonceBlock},
};
-} // anonymous namespace
-
-namespace android {
-
static const char kApplicationSharedMemoryClassName[] =
"com/android/internal/os/ApplicationSharedMemory";
static jclass gApplicationSharedMemoryClass;
+} // anonymous namespace
+
+namespace android {
+
int register_com_android_internal_os_ApplicationSharedMemory(JNIEnv* env) {
gApplicationSharedMemoryClass =
MakeGlobalRefOrDie(env, FindClassOrDie(env, kApplicationSharedMemoryClassName));
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index fb06e96..4c38246 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -8483,6 +8483,27 @@
<permission android:name="android.permission.RESERVED_FOR_TESTING_SIGNATURE"
android:protectionLevel="signature"/>
+ <!--
+ @SystemApi
+ @FlaggedApi("android.media.tv.flags.media_quality_fw")
+ Allows an application to access its picture profile from the media quality database.
+ <p> Protection level: signature|privileged|vendor privileged
+ @hide
+ -->
+ <permission android:name="android.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE"
+ android:protectionLevel="signature|privileged|vendorPrivileged"
+ android:featureFlag="android.media.tv.flags.media_quality_fw"/>
+
+ <!--
+ @SystemApi
+ @FlaggedApi("android.media.tv.flags.media_quality_fw")
+ Allows an application to access its sound profile from the media quality database.
+ <p> Protection level: signature|privileged|vendor privileged
+ @hide
+ -->
+ <permission android:name="android.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE"
+ android:protectionLevel="signature|privileged|vendorPrivileged"
+ android:featureFlag="android.media.tv.flags.media_quality_fw"/>
<!-- @SystemApi
@FlaggedApi("android.content.pm.verification_service")
Allows app to be the verification agent to verify packages.
diff --git a/core/res/res/color-watch-v36/btn_material_filled_background_color.xml b/core/res/res/color-watch-v36/btn_material_filled_background_color.xml
index 8b2afa8..70aace4 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_background_color.xml
+++ b/core/res/res/color-watch-v36/btn_material_filled_background_color.xml
@@ -18,6 +18,5 @@
<item android:state_enabled="false"
android:alpha="?attr/disabledAlpha"
android:color="?attr/materialColorOnSurface" />
- <item android:state_enabled="true"
- android:color="?attr/materialColorPrimary" />
+ <item android:color="?attr/materialColorPrimary" />
</selector>
\ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml b/core/res/res/color-watch-v36/btn_material_filled_content_color.xml
similarity index 89%
rename from core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
rename to core/res/res/color-watch-v36/btn_material_filled_content_color.xml
index 94e50fb..4cc8fe5 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
+++ b/core/res/res/color-watch-v36/btn_material_filled_content_color.xml
@@ -18,6 +18,5 @@
<item android:state_enabled="false"
android:alpha="?attr/primaryContentAlpha"
android:color="?attr/materialColorOnSurface" />
- <item android:state_enabled="true"
- android:color="?attr/materialColorOnSurface" />
+ <item android:color="?attr/materialColorOnPrimary" />
</selector>
\ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_text_color.xml b/core/res/res/color-watch-v36/btn_material_filled_text_color.xml
deleted file mode 100644
index cefc912..0000000
--- a/core/res/res/color-watch-v36/btn_material_filled_text_color.xml
+++ /dev/null
@@ -1,23 +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.
- -->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false"
- android:alpha="?attr/primaryContentAlpha"
- android:color="?attr/materialColorOnSurface" />
- <item android:state_enabled="true"
- android:color="?attr/materialColorOnPrimary" />
-</selector>
\ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml b/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml
index eaf9e7d..b2a25af 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml
+++ b/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml
@@ -18,6 +18,5 @@
<item android:state_enabled="false"
android:alpha="?attr/disabledAlpha"
android:color="?attr/materialColorOnSurface" />
- <item android:state_enabled="true"
- android:color="?attr/materialColorSurfaceContainer" />
+ <item android:color="?attr/materialColorSurfaceContainer" />
</selector>
\ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml b/core/res/res/color-watch-v36/btn_material_filled_tonal_content_color.xml
similarity index 89%
copy from core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
copy to core/res/res/color-watch-v36/btn_material_filled_tonal_content_color.xml
index 94e50fb..59810356 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
+++ b/core/res/res/color-watch-v36/btn_material_filled_tonal_content_color.xml
@@ -18,6 +18,5 @@
<item android:state_enabled="false"
android:alpha="?attr/primaryContentAlpha"
android:color="?attr/materialColorOnSurface" />
- <item android:state_enabled="true"
- android:color="?attr/materialColorOnSurface" />
+ <item android:color="?attr/materialColorOnSurface" />
</selector>
\ No newline at end of file
diff --git a/core/res/res/drawable-watch-v36/dialog_alert_button_background_negative.xml b/core/res/res/drawable-watch-v36/dialog_alert_button_background_negative.xml
new file mode 100644
index 0000000..b6b8eac3
--- /dev/null
+++ b/core/res/res/drawable-watch-v36/dialog_alert_button_background_negative.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="@color/btn_material_filled_tonal_background_color"/>
+ <corners android:radius="@dimen/config_bottomDialogCornerRadius" />
+ <size
+ android:width="@dimen/dialog_btn_negative_width"
+ android:height="@dimen/dialog_btn_negative_height" />
+</shape>
diff --git a/core/res/res/drawable-watch-v36/dialog_alert_button_background_positive.xml b/core/res/res/drawable-watch-v36/dialog_alert_button_background_positive.xml
new file mode 100644
index 0000000..92262fb
--- /dev/null
+++ b/core/res/res/drawable-watch-v36/dialog_alert_button_background_positive.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<rotate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromDegrees="-45"
+ android:toDegrees="-45"
+ android:pivotX="50%"
+ android:pivotY="50%">
+ <shape android:shape="rectangle">
+ <solid android:color="@color/btn_material_filled_background_color"/>
+ <corners android:radius="200dp" />
+ <size
+ android:width="63dp"
+ android:height="54dp" />
+ </shape>
+</rotate>
\ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml b/core/res/res/drawable-watch-v36/dialog_alert_button_negative.xml
similarity index 65%
copy from core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
copy to core/res/res/drawable-watch-v36/dialog_alert_button_negative.xml
index 94e50fb..c155ba1 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
+++ b/core/res/res/drawable-watch-v36/dialog_alert_button_negative.xml
@@ -14,10 +14,9 @@
~ limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false"
- android:alpha="?attr/primaryContentAlpha"
- android:color="?attr/materialColorOnSurface" />
- <item android:state_enabled="true"
- android:color="?attr/materialColorOnSurface" />
-</selector>
\ No newline at end of file
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/dialog_alert_button_background_negative"/>
+ <item
+ android:drawable="@drawable/ic_close"
+ android:gravity="center" />
+</layer-list>
\ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml b/core/res/res/drawable-watch-v36/dialog_alert_button_positive.xml
similarity index 65%
copy from core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
copy to core/res/res/drawable-watch-v36/dialog_alert_button_positive.xml
index 94e50fb..01ab073 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
+++ b/core/res/res/drawable-watch-v36/dialog_alert_button_positive.xml
@@ -14,10 +14,9 @@
~ limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false"
- android:alpha="?attr/primaryContentAlpha"
- android:color="?attr/materialColorOnSurface" />
- <item android:state_enabled="true"
- android:color="?attr/materialColorOnSurface" />
-</selector>
\ No newline at end of file
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/dialog_alert_button_background_positive"/>
+ <item
+ android:drawable="@drawable/ic_check"
+ android:gravity="center" />
+</layer-list>
\ No newline at end of file
diff --git a/core/res/res/drawable-watch-v36/ic_check.xml b/core/res/res/drawable-watch-v36/ic_check.xml
new file mode 100644
index 0000000..7b01e64
--- /dev/null
+++ b/core/res/res/drawable-watch-v36/ic_check.xml
@@ -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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="28dp"
+ android:height="28dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="@color/btn_material_filled_content_color">
+ <path android:fillColor="@color/btn_material_filled_content_color"
+ android:pathData="M382,597.87L716.7,263.17Q730.37,249.5 748.76,249.5Q767.15,249.5 780.83,263.17Q794.5,276.85 794.5,295.62Q794.5,314.39 780.83,328.07L414.07,695.59Q400.39,709.26 382,709.26Q363.61,709.26 349.93,695.59L178.41,524.07Q164.74,510.39 165.12,491.62Q165.5,472.85 179.17,459.17Q192.85,445.5 211.62,445.5Q230.39,445.5 244.07,459.17L382,597.87Z"/>
+</vector>
diff --git a/core/res/res/drawable-watch-v36/ic_close.xml b/core/res/res/drawable-watch-v36/ic_close.xml
new file mode 100644
index 0000000..1f3da36
--- /dev/null
+++ b/core/res/res/drawable-watch-v36/ic_close.xml
@@ -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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="28dp"
+ android:height="28dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="@color/btn_material_filled_tonal_content_color">
+ <path android:fillColor="@color/btn_material_filled_tonal_content_color"
+ android:pathData="M480,543.65L287.83,735.83Q275.15,748.5 256,748.5Q236.85,748.5 224.17,735.83Q211.5,723.15 211.5,704Q211.5,684.85 224.17,672.17L416.35,480L224.17,287.83Q211.5,275.15 211.5,256Q211.5,236.85 224.17,224.17Q236.85,211.5 256,211.5Q275.15,211.5 287.83,224.17L480,416.35L672.17,224.17Q684.85,211.5 704,211.5Q723.15,211.5 735.83,224.17Q748.5,236.85 748.5,256Q748.5,275.15 735.83,287.83L543.65,480L735.83,672.17Q748.5,684.85 748.5,704Q748.5,723.15 735.83,735.83Q723.15,748.5 704,748.5Q684.85,748.5 672.17,735.83L480,543.65Z"/>
+</vector>
diff --git a/core/res/res/layout-watch-v36/alert_dialog_material.xml b/core/res/res/layout-watch-v36/alert_dialog_material.xml
new file mode 100644
index 0000000..900102f
--- /dev/null
+++ b/core/res/res/layout-watch-v36/alert_dialog_material.xml
@@ -0,0 +1,123 @@
+<!--
+ ~ 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.
+ -->
+
+<!-- This layout is the AlertDialog template. It overrides the system layout with the same name.
+ Make sure to include all the existing id of the overridden alert_dialog_material.-->
+<com.android.internal.widget.WatchListDecorLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/parentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <ScrollView
+ android:id="@+id/scrollView"
+ android:fillViewport="true"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <!-- Top Panel -->
+ <FrameLayout
+ android:paddingLeft="?dialogPreferredPadding"
+ android:paddingRight="?dialogPreferredPadding"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/topPanel"
+ android:minHeight="@dimen/dialog_list_padding_top_no_title">
+ <include android:id="@+id/title_template"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ layout="@layout/alert_dialog_title_material"/>
+ </FrameLayout>
+
+ <!-- Content Panel -->
+ <FrameLayout android:id="@+id/contentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false">
+ <TextView android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal|top"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Body1"
+ android:paddingStart="?dialogPreferredPadding"
+ android:paddingEnd="?dialogPreferredPadding"
+ android:paddingTop="8dip"
+ android:paddingBottom="8dip"/>
+ </FrameLayout>
+
+ <!-- Custom Panel, to replace content panel if needed -->
+ <FrameLayout android:id="@+id/customPanel"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:minHeight="64dp">
+ <FrameLayout android:id="@+android:id/custom"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </FrameLayout>
+
+ <!-- Button Panel -->
+ <FrameLayout
+ android:id="@+id/buttonPanel"
+ android:minHeight="@dimen/dialog_list_padding_bottom_no_buttons"
+ android:layout_weight="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center">
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ android:orientation="horizontal"
+ android:paddingBottom="?dialogPreferredPadding"
+ style="?android:attr/buttonBarStyle"
+ android:measureWithLargestChild="true">
+ <Button android:id="@+id/button2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:layout_weight="1"
+ style="@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog.Negative" />
+ <Button android:id="@+id/button3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:layout_weight="1"
+ style="?android:attr/buttonBarButtonStyle"/>
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <Button android:id="@+id/button1"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:layout_weight="1"
+ style="@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog.Confirm"/>
+ <!-- This works as background. -->
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:src="@drawable/dialog_alert_button_positive"/>
+ </FrameLayout>
+ </LinearLayout>
+ </FrameLayout>
+ </LinearLayout>
+ </ScrollView>
+</com.android.internal.widget.WatchListDecorLayout>
diff --git a/core/res/res/values-watch-v36/colors.xml b/core/res/res/values-watch-v36/colors.xml
index 4bc2a66..6cb9b85 100644
--- a/core/res/res/values-watch-v36/colors.xml
+++ b/core/res/res/values-watch-v36/colors.xml
@@ -15,4 +15,9 @@
-->
<!-- TODO(b/372524566): update color token's value to match material3 design. -->
<resources>
-</resources>
\ No newline at end of file
+ <color name="system_primary_dark">#E9DDFF</color>
+ <color name="system_primary_fixed_dim">#D0BCFF</color>
+ <color name="system_on_primary_dark">#210F48</color>
+ <color name="system_primary_container_dark">#4D3D76</color>
+ <color name="system_on_primary_container_dark">#F6EDFF</color>
+</resources>
diff --git a/core/res/res/values-watch-v36/config.xml b/core/res/res/values-watch-v36/config.xml
index c8f347af..bb9da17 100644
--- a/core/res/res/values-watch-v36/config.xml
+++ b/core/res/res/values-watch-v36/config.xml
@@ -17,4 +17,5 @@
<resources>
<!-- Overrides system value -->
<dimen name="config_buttonCornerRadius">26dp</dimen>
+ <dimen name="config_bottomDialogCornerRadius">18dp</dimen>
</resources>
diff --git a/core/res/res/values-watch-v36/dimens_material.xml b/core/res/res/values-watch-v36/dimens_material.xml
index ad3c1a3..ffa3b9c 100644
--- a/core/res/res/values-watch-v36/dimens_material.xml
+++ b/core/res/res/values-watch-v36/dimens_material.xml
@@ -22,6 +22,12 @@
<dimen name="btn_lineHeight">18sp</dimen>
<dimen name="btn_textSize">15sp</dimen>
+ <!-- values for material3 AlertDialog -->
+ <dimen name="dialog_btn_negative_width">60dp</dimen>
+ <dimen name="dialog_btn_negative_height">60dp</dimen>
+ <dimen name="dialog_btn_confirm_width">62dp</dimen>
+ <dimen name="dialog_btn_confirm_height">60dp</dimen>
+
<!-- Opacity factor for disabled material3 widget -->
<dimen name="disabled_alpha_device_default">0.12</dimen>
<dimen name="primary_content_alpha_device_default">0.38</dimen>
diff --git a/core/res/res/values-watch-v36/styles_material.xml b/core/res/res/values-watch-v36/styles_material.xml
index 32a22bb..b2760e7 100644
--- a/core/res/res/values-watch-v36/styles_material.xml
+++ b/core/res/res/values-watch-v36/styles_material.xml
@@ -44,7 +44,7 @@
<!-- Text Styles -->
<!-- TextAppearance for Material Button - Filled -->
<style name="TextAppearance.Widget.Button.Material.Filled" parent="TextAppearance.Widget.Button.Material">
- <item name="textColor">@color/btn_material_filled_text_color</item>
+ <item name="textColor">@color/btn_material_filled_content_color</item>
</style>
<!-- TextAppearance for Material Button - Filled Tonal -->
@@ -52,7 +52,31 @@
<item name="android:fontFamily">font-family-flex-device-default</item>
<item name="android:fontVariationSettings">"'wdth' 90, 'wght' 500, 'ROND' 100, 'opsz' 15, 'GRAD' 0"</item>
<item name="textSize">@dimen/btn_textSize</item>
- <item name="textColor">@color/btn_material_filled_tonal_text_color</item>
+ <item name="textColor">@color/btn_material_filled_tonal_content_color</item>
<item name="lineHeight">@dimen/btn_lineHeight</item>
</style>
+
+ <!-- AlertDialog Styles -->
+ <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog" parent="Widget.DeviceDefault.Button">
+ <item name="android:textSize">0sp</item>
+ <item name="android:gravity">center</item>
+ <item name="android:paddingStart">0dp</item>
+ <item name="android:paddingEnd">0dp</item>
+ <item name="android:drawablePadding">0dp</item>
+ </style>
+
+ <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog.Confirm" parent="Widget.DeviceDefault.Button.ButtonBar.AlertDialog">
+ <!-- Use a ImageView as background -->
+ <item name="background">@android:color/transparent</item>
+ <item name="minWidth">@dimen/dialog_btn_confirm_width</item>
+ <item name="minHeight">@dimen/dialog_btn_confirm_height</item>
+ </style>
+
+ <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog.Negative" parent="Widget.DeviceDefault.Button.ButtonBar.AlertDialog">
+ <item name="background">@drawable/dialog_alert_button_negative</item>
+ <item name="minWidth">@dimen/dialog_btn_negative_width</item>
+ <item name="minHeight">@dimen/dialog_btn_negative_height</item>
+ <item name="maxWidth">@dimen/dialog_btn_negative_width</item>
+ <item name="maxHeight">@dimen/dialog_btn_negative_height</item>
+ </style>
</resources>
\ No newline at end of file
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 02f9f3c..d750ff6 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5731,6 +5731,14 @@
</attr>
</declare-styleable>
+ <!-- @hide internal use only -->
+ <declare-styleable name="NotificationProgressBar">
+ <!-- Draws the tracker on a NotificationProgressBar. -->
+ <attr name="tracker" format="reference" />
+ <!-- Height of the tracker. -->
+ <attr name="trackerHeight" format="dimension" />
+ </declare-styleable>
+
<declare-styleable name="StackView">
<!-- Color of the res-out outline. -->
<attr name="resOutColor" format="color" />
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index cb8e4aa..73681d2 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -504,6 +504,7 @@
<style name="Widget.Material.Notification.NotificationProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal">
<item name="progressDrawable">@drawable/notification_progress</item>
+ <item name="trackerHeight">@dimen/notification_progress_tracker_height</item>
</style>
<style name="Widget.Material.Notification.Text" parent="Widget.Material.Light.TextView">
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioAlertUnitTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioAlertUnitTest.java
new file mode 100644
index 0000000..7afdde2
--- /dev/null
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioAlertUnitTest.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.radio;
+
+import static org.junit.Assert.assertThrows;
+
+import android.os.Parcel;
+import android.platform.test.annotations.EnableFlags;
+
+import com.google.common.truth.Expect;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@EnableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
+public final class RadioAlertUnitTest {
+
+ private static final int TEST_FLAGS = 0;
+ private static final int CREATOR_ARRAY_SIZE = 3;
+ private static final String TEST_GEOCODE_VALUE_NAME = "SAME";
+ private static final String TEST_GEOCODE_VALUE_1 = "006109";
+ private static final String TEST_GEOCODE_VALUE_2 = "006009";
+ private static final double TEST_POLYGON_LATITUDE_START = -38.47;
+ private static final double TEST_POLYGON_LONGITUDE_START = -120.14;
+ private static final RadioAlert.Coordinate TEST_POLYGON_COORDINATE_START =
+ new RadioAlert.Coordinate(TEST_POLYGON_LATITUDE_START, TEST_POLYGON_LONGITUDE_START);
+ private static final List<RadioAlert.Coordinate> TEST_COORDINATES = List.of(
+ TEST_POLYGON_COORDINATE_START, new RadioAlert.Coordinate(38.34, -119.95),
+ new RadioAlert.Coordinate(38.52, -119.74), new RadioAlert.Coordinate(38.62, -119.89),
+ TEST_POLYGON_COORDINATE_START);
+ private static final RadioAlert.Polygon TEST_POLYGON = new RadioAlert.Polygon(TEST_COORDINATES);
+ private static final RadioAlert.Geocode TEST_GEOCODE_1 = new RadioAlert.Geocode(
+ TEST_GEOCODE_VALUE_NAME, TEST_GEOCODE_VALUE_1);
+ private static final RadioAlert.Geocode TEST_GEOCODE_2 = new RadioAlert.Geocode(
+ TEST_GEOCODE_VALUE_NAME, TEST_GEOCODE_VALUE_2);
+ private static final RadioAlert.AlertArea TEST_AREA_1 = new RadioAlert.AlertArea(
+ List.of(TEST_POLYGON), List.of(TEST_GEOCODE_1));
+ private static final RadioAlert.AlertArea TEST_AREA_2 = new RadioAlert.AlertArea(
+ new ArrayList<>(), List.of(TEST_GEOCODE_1, TEST_GEOCODE_2));
+
+ @Rule
+ public final Expect mExpect = Expect.create();
+
+ @Test
+ public void constructor_withNullValueName_forGeocode_fails() {
+ NullPointerException thrown = assertThrows(NullPointerException.class, () ->
+ new RadioAlert.Geocode(/* valueName= */ null, TEST_GEOCODE_VALUE_1));
+
+ mExpect.withMessage("Exception for geocode constructor with null value name")
+ .that(thrown).hasMessageThat()
+ .contains("Geocode value name can not be null");
+ }
+
+ @Test
+ public void constructor_withNullValue_forGeocode_fails() {
+ NullPointerException thrown = assertThrows(NullPointerException.class, () ->
+ new RadioAlert.Geocode(TEST_GEOCODE_VALUE_NAME, /* value= */ null));
+
+ mExpect.withMessage("Exception for geocode constructor with null value")
+ .that(thrown).hasMessageThat()
+ .contains("Geocode value can not be null");
+ }
+
+ @Test
+ public void getValueName_forGeocode() {
+ mExpect.withMessage("Value name of geocode").that(TEST_GEOCODE_1.getValueName())
+ .isEqualTo(TEST_GEOCODE_VALUE_NAME);
+ }
+
+ @Test
+ public void getValue_forGeocode() {
+ mExpect.withMessage("Value of geocode").that(TEST_GEOCODE_1.getValue())
+ .isEqualTo(TEST_GEOCODE_VALUE_1);
+ }
+
+ @Test
+ public void describeContents_forGeocode() {
+ mExpect.withMessage("Contents of geocode")
+ .that(TEST_GEOCODE_1.describeContents()).isEqualTo(0);
+ }
+
+ @Test
+ public void writeToParcel_forGeocode() {
+ Parcel parcel = Parcel.obtain();
+
+ TEST_GEOCODE_1.writeToParcel(parcel, TEST_FLAGS);
+
+ parcel.setDataPosition(0);
+ RadioAlert.Geocode geocodeFromParcel = RadioAlert.Geocode.CREATOR.createFromParcel(parcel);
+ mExpect.withMessage("Geocode from parcel").that(geocodeFromParcel)
+ .isEqualTo(TEST_GEOCODE_1);
+ }
+
+ @Test
+ public void newArray_forGeocodeCreator() {
+ RadioAlert.Geocode[] geocodes = RadioAlert.Geocode.CREATOR.newArray(CREATOR_ARRAY_SIZE);
+
+ mExpect.withMessage("Geocodes").that(geocodes).hasLength(CREATOR_ARRAY_SIZE);
+ }
+
+ @Test
+ public void hashCode_withSameGeocodes() {
+ RadioAlert.Geocode geocodeCompared = new RadioAlert.Geocode(TEST_GEOCODE_VALUE_NAME,
+ TEST_GEOCODE_VALUE_1);
+
+ mExpect.withMessage("Hash code of the same gecode")
+ .that(geocodeCompared.hashCode()).isEqualTo(TEST_GEOCODE_1.hashCode());
+ }
+
+ @Test
+ public void equals_withDifferentGeocodes() {
+ mExpect.withMessage("Different geocode").that(TEST_GEOCODE_1)
+ .isNotEqualTo(TEST_GEOCODE_2);
+ }
+
+ @Test
+ @SuppressWarnings("TruthIncompatibleType")
+ public void equals_withDifferentTypeObject_forGeocode() {
+ mExpect.withMessage("Non-geocode object").that(TEST_GEOCODE_1)
+ .isNotEqualTo(TEST_POLYGON_COORDINATE_START);
+ }
+
+ @Test
+ public void constructor_withInvalidLatitude_forCoordinate_fails() {
+ IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
+ new RadioAlert.Coordinate(/* latitude= */ -92.0, TEST_POLYGON_LONGITUDE_START));
+
+ mExpect.withMessage("Exception for coordinate constructor with invalid latitude")
+ .that(thrown).hasMessageThat().contains("Latitude");
+ }
+
+ @Test
+ public void constructor_withInvalidLongitude_forCoordinate_fails() {
+ IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
+ new RadioAlert.Coordinate(TEST_POLYGON_LATITUDE_START, /* longitude= */ 200.0));
+
+ mExpect.withMessage("Exception for coordinate constructor with invalid longitude")
+ .that(thrown).hasMessageThat().contains("Longitude");
+ }
+
+ @Test
+ public void getLatitude_forCoordinate() {
+ mExpect.withMessage("Latitude of coordinate")
+ .that(TEST_POLYGON_COORDINATE_START.getLatitude())
+ .isEqualTo(TEST_POLYGON_LATITUDE_START);
+ }
+
+ @Test
+ public void getLongitude_forCoordinate() {
+ mExpect.withMessage("Longitude of coordinate")
+ .that(TEST_POLYGON_COORDINATE_START.getLongitude())
+ .isEqualTo(TEST_POLYGON_LONGITUDE_START);
+ }
+
+ @Test
+ public void describeContents_forCoordinate() {
+ mExpect.withMessage("Contents of coordinate")
+ .that(TEST_POLYGON_COORDINATE_START.describeContents()).isEqualTo(0);
+ }
+
+ @Test
+ public void writeToParcel_forCoordinate() {
+ Parcel parcel = Parcel.obtain();
+
+ TEST_POLYGON_COORDINATE_START.writeToParcel(parcel, TEST_FLAGS);
+
+ parcel.setDataPosition(0);
+ RadioAlert.Coordinate coordinateFromParcel = RadioAlert.Coordinate.CREATOR
+ .createFromParcel(parcel);
+ mExpect.withMessage("Coordinate from parcel").that(coordinateFromParcel)
+ .isEqualTo(TEST_POLYGON_COORDINATE_START);
+ }
+
+ @Test
+ public void newArray_forCoordinateCreator() {
+ RadioAlert.Coordinate[] coordinates = RadioAlert.Coordinate.CREATOR
+ .newArray(CREATOR_ARRAY_SIZE);
+
+ mExpect.withMessage("Coordinates").that(coordinates).hasLength(CREATOR_ARRAY_SIZE);
+ }
+
+ @Test
+ public void hashCode_withSameCoordinates() {
+ RadioAlert.Coordinate coordinateCompared = new RadioAlert.Coordinate(
+ TEST_POLYGON_LATITUDE_START, TEST_POLYGON_LONGITUDE_START);
+
+ mExpect.withMessage("Hash code of the same coordinate")
+ .that(coordinateCompared.hashCode())
+ .isEqualTo(TEST_POLYGON_COORDINATE_START.hashCode());
+ }
+
+ @Test
+ public void equals_withDifferentCoordinates() {
+ mExpect.withMessage("Different coordinate").that(TEST_POLYGON_COORDINATE_START)
+ .isNotEqualTo(TEST_COORDINATES.get(1));
+ }
+
+ @Test
+ @SuppressWarnings("TruthIncompatibleType")
+ public void equals_withDifferentTypeObject_forCoordinate() {
+ mExpect.withMessage("Non-coordinate object").that(TEST_POLYGON_COORDINATE_START)
+ .isNotEqualTo(TEST_GEOCODE_1);
+ }
+
+ @Test
+ public void constructor_withNullCoordinates_forPolygon_fails() {
+ NullPointerException thrown = assertThrows(NullPointerException.class, () ->
+ new RadioAlert.Polygon(/* coordinates= */ null));
+
+ mExpect.withMessage("Exception for polygon constructor with null coordinates")
+ .that(thrown).hasMessageThat().contains("Coordinates can not be null");
+ }
+
+ @Test
+ public void constructor_withLessThanFourCoordinates_forPolygon_fails() {
+ IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
+ new RadioAlert.Polygon(List.of(TEST_POLYGON_COORDINATE_START,
+ TEST_POLYGON_COORDINATE_START)));
+
+ mExpect.withMessage("Exception for polygon constructor with less than four coordinates")
+ .that(thrown).hasMessageThat().contains("Number of coordinates must be at least 4");
+ }
+
+ @Test
+ public void constructor_withDifferentFirstAndLastCoordinates_forPolygon_fails() {
+ IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
+ new RadioAlert.Polygon(List.of(TEST_POLYGON_COORDINATE_START,
+ new RadioAlert.Coordinate(38.34, -119.95),
+ new RadioAlert.Coordinate(38.52, -119.74),
+ new RadioAlert.Coordinate(38.62, -119.89))));
+
+ mExpect.withMessage(
+ "Exception for polygon constructor with different first and last coordinates")
+ .that(thrown).hasMessageThat().contains(
+ "last and first coordinates must be the same");
+ }
+
+ @Test
+ public void getCoordinates_forPolygon() {
+ mExpect.withMessage("Coordinates in polygon").that(TEST_POLYGON.getCoordinates())
+ .containsExactlyElementsIn(TEST_COORDINATES);
+ }
+
+ @Test
+ public void describeContents_forPolygon() {
+ mExpect.withMessage("Contents of polygon")
+ .that(TEST_POLYGON.describeContents()).isEqualTo(0);
+ }
+
+ @Test
+ public void writeToParcel_forPolygon() {
+ Parcel parcel = Parcel.obtain();
+
+ TEST_POLYGON.writeToParcel(parcel, TEST_FLAGS);
+
+ parcel.setDataPosition(0);
+ RadioAlert.Polygon polygonFromParcel = RadioAlert.Polygon.CREATOR.createFromParcel(parcel);
+ mExpect.withMessage("Polygon from parcel").that(polygonFromParcel)
+ .isEqualTo(TEST_POLYGON);
+ }
+
+ @Test
+ public void newArray_forPolygonCreator() {
+ RadioAlert.Polygon[] polygons = RadioAlert.Polygon.CREATOR.newArray(CREATOR_ARRAY_SIZE);
+
+ mExpect.withMessage("Polygons").that(polygons).hasLength(CREATOR_ARRAY_SIZE);
+ }
+
+ @Test
+ public void hashCode_withSamePolygons() {
+ RadioAlert.Polygon polygonCompared = new RadioAlert.Polygon(TEST_COORDINATES);
+
+ mExpect.withMessage("Hash code of the same polygon")
+ .that(polygonCompared.hashCode()).isEqualTo(TEST_POLYGON.hashCode());
+ }
+
+ @Test
+ @SuppressWarnings("TruthIncompatibleType")
+ public void equals_withDifferentTypeObject_forPolygon() {
+ mExpect.withMessage("Non-polygon object").that(TEST_POLYGON)
+ .isNotEqualTo(TEST_GEOCODE_1);
+ }
+
+ @Test
+ public void constructor_withNullPolygons_forAlertArea_fails() {
+ NullPointerException thrown = assertThrows(NullPointerException.class, () ->
+ new RadioAlert.AlertArea(/* polygons= */ null, List.of(TEST_GEOCODE_1)));
+
+ mExpect.withMessage("Exception for alert area constructor with null polygon list")
+ .that(thrown).hasMessageThat().contains("Polygons can not be null");
+ }
+
+ @Test
+ public void constructor_withNullGeocodes_forAlertArea_fails() {
+ NullPointerException thrown = assertThrows(NullPointerException.class, () ->
+ new RadioAlert.AlertArea(List.of(TEST_POLYGON), /* geocodes= */ null));
+
+ mExpect.withMessage("Exception for alert area constructor with null geocode list")
+ .that(thrown).hasMessageThat().contains("Geocodes can not be null");
+ }
+
+ @Test
+ public void getPolygons_forAlertArea() {
+ mExpect.withMessage("Polygons in alert area").that(TEST_AREA_1.getPolygons())
+ .containsExactly(TEST_POLYGON);
+ }
+
+ @Test
+ public void getGeocodes_forAlertArea() {
+ mExpect.withMessage("Polygons in alert area").that(TEST_AREA_2.getGeocodes())
+ .containsExactly(TEST_GEOCODE_1, TEST_GEOCODE_2);
+ }
+
+ @Test
+ public void describeContents_forAlertArea() {
+ mExpect.withMessage("Contents of alert area")
+ .that(TEST_AREA_1.describeContents()).isEqualTo(0);
+ }
+
+ @Test
+ public void writeToParcel_forAlertArea() {
+ Parcel parcel = Parcel.obtain();
+
+ TEST_AREA_1.writeToParcel(parcel, TEST_FLAGS);
+
+ parcel.setDataPosition(0);
+ RadioAlert.AlertArea areaFromParcel = RadioAlert.AlertArea.CREATOR.createFromParcel(parcel);
+ mExpect.withMessage("Alert area from parcel").that(areaFromParcel)
+ .isEqualTo(TEST_AREA_1);
+ }
+
+ @Test
+ public void newArray_forAlertAreaCreator() {
+ RadioAlert.AlertArea[] alertAreas = RadioAlert.AlertArea.CREATOR
+ .newArray(CREATOR_ARRAY_SIZE);
+
+ mExpect.withMessage("Alert areas").that(alertAreas).hasLength(CREATOR_ARRAY_SIZE);
+ }
+
+ @Test
+ public void hashCode_withSameAlertAreas() {
+ RadioAlert.AlertArea alertAreaCompared = new RadioAlert.AlertArea(List.of(TEST_POLYGON),
+ List.of(TEST_GEOCODE_1));
+
+ mExpect.withMessage("Hash code of the same alert area")
+ .that(alertAreaCompared.hashCode()).isEqualTo(TEST_AREA_1.hashCode());
+ }
+
+ @Test
+ public void equals_withDifferentAlertAreas() {
+ mExpect.withMessage("Different alert area").that(TEST_AREA_1).isNotEqualTo(TEST_AREA_2);
+ }
+
+ @Test
+ @SuppressWarnings("TruthIncompatibleType")
+ public void equals_withDifferentTypeObject_forAlertArea() {
+ mExpect.withMessage("Non-alert-area object").that(TEST_AREA_1)
+ .isNotEqualTo(TEST_GEOCODE_1);
+ }
+}
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 56e18e6..aee1c3b 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -146,6 +146,10 @@
":BinderProxyCountingTestService",
":AppThatUsesAppOps",
":AppThatCallsBinderMethods",
+ ":HelloWorldSdk1",
+ ":HelloWorldUsingSdk1AndSdk1",
+ ":HelloWorldUsingSdk1And2",
+ ":HelloWorldUsingSdkMalformedNegativeVersion",
],
}
diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml
index 05ab783..3bc8172 100644
--- a/core/tests/coretests/AndroidTest.xml
+++ b/core/tests/coretests/AndroidTest.xml
@@ -29,6 +29,18 @@
<option name="test-file-name" value="AppThatCallsBinderMethods.apk" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true"/>
+ <option name="push-file" key="HelloWorldUsingSdk1And2.apk"
+ value="/data/local/tmp/tests/coretests/pm/HelloWorldUsingSdk1And2.apk"/>
+ <option name="push-file" key="HelloWorldUsingSdk1AndSdk1.apk"
+ value="/data/local/tmp/tests/coretests/pm/HelloWorldUsingSdk1AndSdk1.apk"/>
+ <option name="push-file" key="HelloWorldUsingSdkMalformedNegativeVersion.apk"
+ value="/data/local/tmp/tests/coretests/pm/HelloWorldUsingSdkMalformedNegativeVersion.apk"/>
+ <option name="push-file" key="HelloWorldSdk1.apk"
+ value="/data/local/tmp/tests/coretests/pm/HelloWorldSdk1.apk"/>
+ </target_preparer>
+
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<!-- TODO(b/254155965): Design a mechanism to finally remove this command. -->
<option name="run-command" value="settings put global device_config_sync_disabled 0" />
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index dcea5b2..65153f5 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -16,13 +16,23 @@
package android.app;
+import static android.app.PropertyInvalidatedCache.NONCE_UNSET;
+import static android.app.PropertyInvalidatedCache.NonceStore.INVALID_NONCE_INDEX;
+import static com.android.internal.os.Flags.FLAG_APPLICATION_SHARED_MEMORY_ENABLED;
+
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import com.android.internal.os.ApplicationSharedMemory;
+
import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
@@ -47,6 +57,9 @@
@Rule
public final RavenwoodRule mRavenwood = new RavenwoodRule();
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
// Configuration for creating caches
private static final String MODULE = PropertyInvalidatedCache.MODULE_TEST;
private static final String API = "testApi";
@@ -423,4 +436,54 @@
// Re-enable test mode (so that the cleanup for the test does not throw).
PropertyInvalidatedCache.setTestMode(true);
}
+
+ // Verify the behavior of shared memory nonce storage. This does not directly test the cache
+ // storing nonces in shared memory.
+ @RequiresFlagsEnabled(FLAG_APPLICATION_SHARED_MEMORY_ENABLED)
+ @Test
+ public void testSharedMemoryStorage() {
+ // Fetch a shared memory instance for testing.
+ ApplicationSharedMemory shmem = ApplicationSharedMemory.create();
+
+ // Create a server-side store and a client-side store. The server's store is mutable and
+ // the client's store is not mutable.
+ PropertyInvalidatedCache.NonceStore server =
+ new PropertyInvalidatedCache.NonceStore(shmem.getSystemNonceBlock(), true);
+ PropertyInvalidatedCache.NonceStore client =
+ new PropertyInvalidatedCache.NonceStore(shmem.getSystemNonceBlock(), false);
+
+ final String name1 = "name1";
+ assertEquals(server.getHandleForName(name1), INVALID_NONCE_INDEX);
+ assertEquals(client.getHandleForName(name1), INVALID_NONCE_INDEX);
+ final int index1 = server.storeName(name1);
+ assertNotEquals(index1, INVALID_NONCE_INDEX);
+ assertEquals(server.getHandleForName(name1), index1);
+ assertEquals(client.getHandleForName(name1), index1);
+ assertEquals(server.storeName(name1), index1);
+
+ assertEquals(server.getNonce(index1), NONCE_UNSET);
+ assertEquals(client.getNonce(index1), NONCE_UNSET);
+ final int value1 = 4;
+ server.setNonce(index1, value1);
+ assertEquals(server.getNonce(index1), value1);
+ assertEquals(client.getNonce(index1), value1);
+ final int value2 = 8;
+ server.setNonce(index1, value2);
+ assertEquals(server.getNonce(index1), value2);
+ assertEquals(client.getNonce(index1), value2);
+
+ final String name2 = "name2";
+ assertEquals(server.getHandleForName(name2), INVALID_NONCE_INDEX);
+ assertEquals(client.getHandleForName(name2), INVALID_NONCE_INDEX);
+ final int index2 = server.storeName(name2);
+ assertNotEquals(index2, INVALID_NONCE_INDEX);
+ assertEquals(server.getHandleForName(name2), index2);
+ assertEquals(client.getHandleForName(name2), index2);
+ assertEquals(server.storeName(name2), index2);
+
+ // The names are different, so the indices must be different.
+ assertNotEquals(index1, index2);
+
+ shmem.close();
+ }
}
diff --git a/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java b/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java
new file mode 100644
index 0000000..f9b481f
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.parsing;
+
+import static android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES;
+import static android.content.pm.PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.SuppressLint;
+import android.app.UiAutomation;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
+import android.util.PackageUtils;
+
+import androidx.annotation.NonNull;
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.pm.parsing.PackageParser2;
+import com.android.server.pm.pkg.AndroidPackage;
+
+import libcore.util.HexEncoding;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+@Presubmit
+public class ApkLiteParseUtilsTest {
+
+ @Rule
+ public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+ private static final String PUSH_FILE_DIR = "/data/local/tmp/tests/coretests/pm/";
+ private static final String TEST_APP_USING_SDK1_AND_SDK2 = "HelloWorldUsingSdk1And2.apk";
+ private static final String TEST_APP_USING_SDK1_AND_SDK1 = "HelloWorldUsingSdk1AndSdk1.apk";
+ private static final String TEST_APP_USING_SDK_MALFORMED_VERSION =
+ "HelloWorldUsingSdkMalformedNegativeVersion.apk";
+ private static final String TEST_SDK1 = "HelloWorldSdk1.apk";
+ private static final String TEST_SDK1_PACKAGE = "com.test.sdk1_1";
+ private static final String TEST_SDK1_NAME = "com.test.sdk1";
+ private static final long TEST_SDK1_VERSION = 1;
+ private static final String TEST_SDK2_NAME = "com.test.sdk2";
+ private static final long TEST_SDK2_VERSION = 2;
+
+ private final PackageParser2 mPackageParser2 = new PackageParser2(
+ null, null, null, new FakePackageParser2Callback());
+
+ private File mTmpDir = null;
+
+ @Before
+ public void setUp() throws IOException {
+ mTmpDir = mTemporaryFolder.newFolder("DexMetadataHelperTest");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ setSystemProperty("debug.pm.uses_sdk_library_default_cert_digest", "invalid");
+ uninstallPackageSilently(TEST_SDK1_NAME);
+ }
+
+ @SuppressLint("CheckResult")
+ @Test
+ public void testParseApkLite_getUsesSdkLibrary() throws Exception {
+ File apkFile = copyApkToTmpDir(TEST_APP_USING_SDK1_AND_SDK2);
+ ParseResult<ApkLite> result = ApkLiteParseUtils
+ .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0);
+ assertThat(result.isError()).isFalse();
+
+ ApkLite baseApk = result.getResult();
+ assertThat(baseApk.getUsesSdkLibraries()).containsExactly(TEST_SDK1_NAME, TEST_SDK2_NAME);
+ assertThat(baseApk.getUsesSdkLibrariesVersionsMajor()).asList().containsExactly(
+ TEST_SDK1_VERSION, TEST_SDK2_VERSION
+ );
+ for (String[] certDigests: baseApk.getUsesSdkLibrariesCertDigests()) {
+ assertThat(certDigests).asList().containsExactly("");
+ }
+ }
+
+ @SuppressLint("CheckResult")
+ @Test
+ public void testParseApkLite_getUsesSdkLibrary_overrideCertDigest() throws Exception {
+ installPackage(TEST_SDK1);
+ String certDigest = getPackageCertDigest(TEST_SDK1_PACKAGE);
+ overrideUsesSdkLibraryCertificateDigest(certDigest);
+
+ File apkFile = copyApkToTmpDir(TEST_APP_USING_SDK1_AND_SDK2);
+ ParseResult<ApkLite> result = ApkLiteParseUtils
+ .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0);
+ ApkLite baseApk = result.getResult();
+
+ String[][] liteCerts = baseApk.getUsesSdkLibrariesCertDigests();
+ assertThat(liteCerts).isNotNull();
+ for (String[] certDigests: liteCerts) {
+ assertThat(certDigests).asList().containsExactly(certDigest);
+ }
+
+ // Same for package parser
+ AndroidPackage pkg = mPackageParser2.parsePackage(apkFile, 0, true).hideAsFinal();
+ String[][] pkgCerts = pkg.getUsesSdkLibrariesCertDigests();
+ assertThat(pkgCerts).isNotNull();
+ for (int i = 0; i < liteCerts.length; i++) {
+ assertThat(liteCerts[i]).isEqualTo(pkgCerts[i]);
+ }
+ }
+
+
+ @SuppressLint("CheckResult")
+ @Test
+ public void testParseApkLite_getUsesSdkLibrary_sameAsPackageParser() throws Exception {
+ File apkFile = copyApkToTmpDir(TEST_APP_USING_SDK1_AND_SDK2);
+ ParseResult<ApkLite> result = ApkLiteParseUtils
+ .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0);
+ assertThat(result.isError()).isFalse();
+ ApkLite baseApk = result.getResult();
+
+ AndroidPackage pkg = mPackageParser2.parsePackage(apkFile, 0, true).hideAsFinal();
+ assertThat(baseApk.getUsesSdkLibraries())
+ .containsExactlyElementsIn(pkg.getUsesSdkLibraries());
+ List<Long> versionsBoxed = Arrays.stream(pkg.getUsesSdkLibrariesVersionsMajor()).boxed()
+ .toList();
+ assertThat(baseApk.getUsesSdkLibrariesVersionsMajor()).asList()
+ .containsExactlyElementsIn(versionsBoxed);
+
+ String[][] liteCerts = baseApk.getUsesSdkLibrariesCertDigests();
+ String[][] pkgCerts = pkg.getUsesSdkLibrariesCertDigests();
+ for (int i = 0; i < liteCerts.length; i++) {
+ assertThat(liteCerts[i]).isEqualTo(pkgCerts[i]);
+ }
+ }
+
+ @SuppressLint("CheckResult")
+ @Test
+ public void testParseApkLite_malformedUsesSdkLibrary_duplicate() throws Exception {
+ File apkFile = copyApkToTmpDir(TEST_APP_USING_SDK1_AND_SDK1);
+ ParseResult<ApkLite> result = ApkLiteParseUtils
+ .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0);
+ assertThat(result.isError()).isTrue();
+ assertThat(result.getErrorMessage()).contains("Bad uses-sdk-library declaration");
+ assertThat(result.getErrorMessage()).contains(
+ "Depending on multiple versions of SDK library");
+ }
+
+ @SuppressLint("CheckResult")
+ @Test
+ public void testParseApkLite_malformedUsesSdkLibrary_missingVersion() throws Exception {
+ File apkFile = copyApkToTmpDir(TEST_APP_USING_SDK_MALFORMED_VERSION);
+ ParseResult<ApkLite> result = ApkLiteParseUtils
+ .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0);
+ assertThat(result.isError()).isTrue();
+ assertThat(result.getErrorMessage()).contains("Bad uses-sdk-library declaration");
+ }
+
+ private String getPackageCertDigest(String packageName) throws Exception {
+ getUiAutomation().adoptShellPermissionIdentity();
+ try {
+ PackageInfo sdkPackageInfo = getPackageManager().getPackageInfo(packageName,
+ PackageManager.PackageInfoFlags.of(
+ GET_SIGNING_CERTIFICATES | MATCH_STATIC_SHARED_AND_SDK_LIBRARIES));
+ SigningInfo signingInfo = sdkPackageInfo.signingInfo;
+ Signature[] signatures =
+ signingInfo != null ? signingInfo.getSigningCertificateHistory() : null;
+ byte[] digest = PackageUtils.computeSha256DigestBytes(signatures[0].toByteArray());
+ return new String(HexEncoding.encode(digest));
+ } finally {
+ getUiAutomation().dropShellPermissionIdentity();
+ }
+ }
+
+ private static PackageManager getPackageManager() {
+ return InstrumentationRegistry.getContext().getPackageManager();
+ }
+
+ /**
+ * SDK package is signed by build system. In theory we could try to extract the signature,
+ * and patch the app manifest. This property allows us to override in runtime, which is much
+ * easier.
+ */
+ private void overrideUsesSdkLibraryCertificateDigest(String sdkCertDigest) throws Exception {
+ setSystemProperty("debug.pm.uses_sdk_library_default_cert_digest", sdkCertDigest);
+ }
+
+ private void setSystemProperty(String name, String value) throws Exception {
+ assertThat(executeShellCommand("setprop " + name + " " + value)).isEmpty();
+ }
+
+ private static String executeShellCommand(String command) throws IOException {
+ final ParcelFileDescriptor stdout = getUiAutomation().executeShellCommand(command);
+ try (InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(stdout)) {
+ return readFullStream(inputStream);
+ }
+ }
+
+ private static String readFullStream(InputStream inputStream) throws IOException {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ writeFullStream(inputStream, result, -1);
+ return result.toString("UTF-8");
+ }
+
+ private static void writeFullStream(InputStream inputStream, OutputStream outputStream,
+ long expected) throws IOException {
+ byte[] buffer = new byte[1024];
+ long total = 0;
+ int length;
+ while ((length = inputStream.read(buffer)) != -1) {
+ outputStream.write(buffer, 0, length);
+ total += length;
+ }
+ if (expected > 0) {
+ assertThat(expected).isEqualTo(total);
+ }
+ }
+
+ private static UiAutomation getUiAutomation() {
+ return InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ }
+
+ private File copyApkToTmpDir(String apkFileName) throws Exception {
+ File outFile = new File(mTmpDir, apkFileName);
+ String apkFilePath = PUSH_FILE_DIR + apkFileName;
+ File apkFile = new File(apkFilePath);
+ assertThat(apkFile.exists()).isTrue();
+ try (InputStream is = new FileInputStream(apkFile)) {
+ FileUtils.copyToFileOrThrow(is, outFile);
+ }
+ return outFile;
+ }
+
+ static String createApkPath(String baseName) {
+ return PUSH_FILE_DIR + baseName;
+ }
+
+ /* Install for all the users */
+ private void installPackage(String baseName) throws IOException {
+ File file = new File(createApkPath(baseName));
+ assertThat(executeShellCommand("pm install -t -g " + file.getPath()))
+ .isEqualTo("Success\n");
+ }
+
+ private static String uninstallPackageSilently(String packageName) throws IOException {
+ return executeShellCommand("pm uninstall " + packageName);
+ }
+
+ static class FakePackageParser2Callback extends PackageParser2.Callback {
+
+ @Override
+ public boolean isChangeEnabled(long changeId, @NonNull ApplicationInfo appInfo) {
+ return true;
+ }
+
+ @Override
+ public boolean hasFeature(String feature) {
+ return true;
+ }
+
+ @Override
+ public @NonNull Set<String> getHiddenApiWhitelistedApps() {
+ return new ArraySet<>();
+ }
+
+ @Override
+ public @NonNull Set<String> getInstallConstraintsAllowlist() {
+ return new ArraySet<>();
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java b/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java
index 90ae306..f1e1df5 100644
--- a/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java
+++ b/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java
@@ -142,10 +142,10 @@
new VerificationStatus.Builder().setVerified(true).build();
mTestSession.reportVerificationComplete(status);
verify(mTestSessionInterface, times(1)).reportVerificationComplete(
- eq(TEST_ID), eq(status));
+ eq(TEST_ID), eq(status), eq(null));
mTestSession.reportVerificationComplete(status, response);
verify(mTestSessionInterface, times(1))
- .reportVerificationCompleteWithExtensionResponse(
+ .reportVerificationComplete(
eq(TEST_ID), eq(status), eq(response));
final int reason = VerificationSession.VERIFICATION_INCOMPLETE_UNKNOWN;
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index da202b6..930e03d 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -46,7 +46,7 @@
// The number of fields tested in the corresponding CTS AccessibilityNodeInfoTest:
// See fullyPopulateAccessibilityNodeInfo, assertEqualsAccessibilityNodeInfo,
// and assertAccessibilityNodeInfoCleared in that class.
- private static final int NUM_MARSHALLED_PROPERTIES = 45;
+ private static final int NUM_MARSHALLED_PROPERTIES = 46;
/**
* The number of properties that are purposely not marshalled
diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
index 1cbc7d6..60b5a42 100644
--- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
@@ -16,9 +16,9 @@
package com.android.internal.jank;
-import static android.view.SurfaceControl.JankData.JANK_APP_DEADLINE_MISSED;
+import static android.view.SurfaceControl.JankData.JANK_APPLICATION;
+import static android.view.SurfaceControl.JankData.JANK_COMPOSER;
import static android.view.SurfaceControl.JankData.JANK_NONE;
-import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_DEADLINE_MISSED;
import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
import static com.android.internal.jank.FrameTracker.ViewRootWrapper;
@@ -164,7 +164,7 @@
verify(mRenderer, only()).addObserver(any());
// send first frame with a long duration - should not be taken into account
- sendFirstWindowFrame(tracker, 100, JANK_APP_DEADLINE_MISSED, 100L);
+ sendFirstWindowFrame(tracker, 100, JANK_APPLICATION, 100L);
// send another frame with a short duration - should not be considered janky
sendFrame(tracker, 5, JANK_NONE, 101L);
@@ -173,7 +173,7 @@
when(mChoreographer.getVsyncId()).thenReturn(102L);
tracker.end(FrameTracker.REASON_END_NORMAL);
sendFrame(tracker, 5, JANK_NONE, 102L);
- sendFrame(tracker, 500, JANK_APP_DEADLINE_MISSED, 103L);
+ sendFrame(tracker, 500, JANK_APPLICATION, 103L);
verify(tracker).removeObservers();
verify(mTrackerListener, never()).triggerPerfetto(any());
@@ -202,7 +202,7 @@
sendFrame(tracker, 4, JANK_NONE, 100L);
// send another frame - should be considered janky
- sendFrame(tracker, 40, JANK_SURFACEFLINGER_DEADLINE_MISSED, 101L);
+ sendFrame(tracker, 40, JANK_COMPOSER, 101L);
// end the trace session
when(mChoreographer.getVsyncId()).thenReturn(102L);
@@ -236,7 +236,7 @@
verify(mRenderer, only()).addObserver(any());
// send first frame - janky
- sendFrame(tracker, 40, JANK_APP_DEADLINE_MISSED, 100L);
+ sendFrame(tracker, 40, JANK_APPLICATION, 100L);
// send another frame - not jank
sendFrame(tracker, 4, JANK_NONE, 101L);
@@ -275,7 +275,7 @@
sendFrame(tracker, 4, JANK_NONE, 100L);
// send another frame - should be considered janky
- sendFrame(tracker, 40, JANK_APP_DEADLINE_MISSED, 101L);
+ sendFrame(tracker, 40, JANK_APPLICATION, 101L);
// end the trace session
when(mChoreographer.getVsyncId()).thenReturn(102L);
@@ -317,7 +317,7 @@
// end the trace session, simulate one more valid callback came after the end call.
when(mChoreographer.getVsyncId()).thenReturn(102L);
tracker.end(FrameTracker.REASON_END_NORMAL);
- sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 102L);
+ sendFrame(tracker, 50, JANK_APPLICATION, 102L);
// One more callback with VSYNC after the end() vsync id.
sendFrame(tracker, 4, JANK_NONE, 103L);
@@ -365,7 +365,7 @@
sendSfFrame(tracker, 4, 102L, JANK_NONE);
// Send janky but complete callbck fo 103L
- sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 103L);
+ sendFrame(tracker, 50, JANK_APPLICATION, 103L);
verify(tracker).removeObservers();
verify(mTrackerListener, never()).triggerPerfetto(any());
@@ -397,7 +397,7 @@
sendFrame(tracker, 4, JANK_NONE, 101L);
// a janky frame
- sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 102L);
+ sendFrame(tracker, 50, JANK_APPLICATION, 102L);
tracker.cancel(FrameTracker.REASON_CANCEL_NORMAL);
verify(tracker).removeObservers();
@@ -481,7 +481,7 @@
// normal frame - not janky
sendFrame(tracker, JANK_NONE, 101L);
// a janky frame
- sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 102L);
+ sendFrame(tracker, JANK_APPLICATION, 102L);
when(mChoreographer.getVsyncId()).thenReturn(102L);
tracker.end(FrameTracker.REASON_CANCEL_NORMAL);
@@ -514,7 +514,7 @@
verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
// First frame - janky
- sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 100L);
+ sendFrame(tracker, JANK_APPLICATION, 100L);
// normal frame - not janky
sendFrame(tracker, JANK_NONE, 101L);
// normal frame - not janky
@@ -561,7 +561,7 @@
tracker.end(FrameTracker.REASON_CANCEL_NORMAL);
// janky frame, should be ignored, trigger finish
- sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 103L);
+ sendFrame(tracker, JANK_APPLICATION, 103L);
verify(mJankStatsRegistration).removeAfter(anyLong());
verify(mTrackerListener, never()).triggerPerfetto(any());
@@ -623,16 +623,16 @@
tracker.begin();
mRunnableArgumentCaptor.getValue().run();
verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
- sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 100L);
- sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 101L);
- sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 102L);
+ sendFrame(tracker, JANK_COMPOSER, 100L);
+ sendFrame(tracker, JANK_COMPOSER, 101L);
+ sendFrame(tracker, JANK_APPLICATION, 102L);
sendFrame(tracker, JANK_NONE, 103L);
- sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 104L);
- sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 105L);
+ sendFrame(tracker, JANK_APPLICATION, 104L);
+ sendFrame(tracker, JANK_APPLICATION, 105L);
when(mChoreographer.getVsyncId()).thenReturn(106L);
tracker.end(FrameTracker.REASON_END_NORMAL);
- sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 106L);
- sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 107L);
+ sendFrame(tracker, JANK_COMPOSER, 106L);
+ sendFrame(tracker, JANK_COMPOSER, 107L);
verify(mJankStatsRegistration).removeAfter(anyLong());
verify(mTrackerListener).triggerPerfetto(any());
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
diff --git a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
index 79a478a..5613caf 100644
--- a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
@@ -41,9 +41,26 @@
List<ProgressStyle.Segment> segments = new ArrayList<>();
List<ProgressStyle.Point> points = new ArrayList<>();
int progress = 50;
+ int progressMax = 100;
boolean isStyledByProgress = true;
NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+ progressMax,
+ isStyledByProgress);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void processAndConvertToDrawableParts_segmentsLengthNotMatchingProgressMax() {
+ List<ProgressStyle.Segment> segments = new ArrayList<>();
+ segments.add(new ProgressStyle.Segment(50));
+ segments.add(new ProgressStyle.Segment(100));
+ List<ProgressStyle.Point> points = new ArrayList<>();
+ int progress = 50;
+ int progressMax = 100;
+ boolean isStyledByProgress = true;
+
+ NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+ progressMax,
isStyledByProgress);
}
@@ -51,11 +68,14 @@
public void processAndConvertToDrawableParts_segmentLengthIsNegative() {
List<ProgressStyle.Segment> segments = new ArrayList<>();
segments.add(new ProgressStyle.Segment(-50));
+ segments.add(new ProgressStyle.Segment(150));
List<ProgressStyle.Point> points = new ArrayList<>();
int progress = 50;
+ int progressMax = 100;
boolean isStyledByProgress = true;
NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+ progressMax,
isStyledByProgress);
}
@@ -63,11 +83,14 @@
public void processAndConvertToDrawableParts_segmentLengthIsZero() {
List<ProgressStyle.Segment> segments = new ArrayList<>();
segments.add(new ProgressStyle.Segment(0));
+ segments.add(new ProgressStyle.Segment(100));
List<ProgressStyle.Point> points = new ArrayList<>();
int progress = 50;
+ int progressMax = 100;
boolean isStyledByProgress = true;
NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+ progressMax,
isStyledByProgress);
}
@@ -77,9 +100,11 @@
segments.add(new ProgressStyle.Segment(100));
List<ProgressStyle.Point> points = new ArrayList<>();
int progress = -50;
+ int progressMax = 100;
boolean isStyledByProgress = true;
NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+ progressMax,
isStyledByProgress);
}
@@ -89,10 +114,11 @@
segments.add(new ProgressStyle.Segment(100).setColor(Color.RED));
List<ProgressStyle.Point> points = new ArrayList<>();
int progress = 0;
+ int progressMax = 100;
boolean isStyledByProgress = true;
List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
- segments, points, progress, isStyledByProgress);
+ segments, points, progress, progressMax, isStyledByProgress);
int fadedRed = 0x7FFF0000;
List<Part> expected = new ArrayList<>(List.of(new Segment(1f, fadedRed, true)));
@@ -106,10 +132,11 @@
segments.add(new ProgressStyle.Segment(100).setColor(Color.RED));
List<ProgressStyle.Point> points = new ArrayList<>();
int progress = 100;
+ int progressMax = 100;
boolean isStyledByProgress = true;
List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
- segments, points, progress, isStyledByProgress);
+ segments, points, progress, progressMax, isStyledByProgress);
List<Part> expected = new ArrayList<>(List.of(new Segment(1f, Color.RED)));
@@ -122,10 +149,11 @@
segments.add(new ProgressStyle.Segment(100));
List<ProgressStyle.Point> points = new ArrayList<>();
int progress = 150;
+ int progressMax = 100;
boolean isStyledByProgress = true;
NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
- isStyledByProgress);
+ progressMax, isStyledByProgress);
}
@Test(expected = IllegalArgumentException.class)
@@ -135,35 +163,11 @@
List<ProgressStyle.Point> points = new ArrayList<>();
points.add(new ProgressStyle.Point(-50).setColor(Color.RED));
int progress = 50;
+ int progressMax = 100;
boolean isStyledByProgress = true;
NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
- isStyledByProgress);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void processAndConvertToDrawableParts_pointPositionIsZero() {
- List<ProgressStyle.Segment> segments = new ArrayList<>();
- segments.add(new ProgressStyle.Segment(100));
- List<ProgressStyle.Point> points = new ArrayList<>();
- points.add(new ProgressStyle.Point(0).setColor(Color.RED));
- int progress = 50;
- boolean isStyledByProgress = true;
-
- NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
- isStyledByProgress);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void processAndConvertToDrawableParts_pointPositionAtMax() {
- List<ProgressStyle.Segment> segments = new ArrayList<>();
- segments.add(new ProgressStyle.Segment(100));
- List<ProgressStyle.Point> points = new ArrayList<>();
- points.add(new ProgressStyle.Point(100).setColor(Color.RED));
- int progress = 50;
- boolean isStyledByProgress = true;
-
- NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+ progressMax,
isStyledByProgress);
}
@@ -174,9 +178,11 @@
List<ProgressStyle.Point> points = new ArrayList<>();
points.add(new ProgressStyle.Point(150).setColor(Color.RED));
int progress = 50;
+ int progressMax = 100;
boolean isStyledByProgress = true;
NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+ progressMax,
isStyledByProgress);
}
@@ -187,10 +193,11 @@
segments.add(new ProgressStyle.Segment(50).setColor(Color.GREEN));
List<ProgressStyle.Point> points = new ArrayList<>();
int progress = 60;
+ int progressMax = 100;
boolean isStyledByProgress = true;
List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
- segments, points, progress, isStyledByProgress);
+ segments, points, progress, progressMax, isStyledByProgress);
// Colors with 50% opacity
int fadedGreen = 0x7F00FF00;
@@ -213,6 +220,7 @@
points.add(new ProgressStyle.Point(60).setColor(Color.BLUE));
points.add(new ProgressStyle.Point(75).setColor(Color.YELLOW));
int progress = 60;
+ int progressMax = 100;
boolean isStyledByProgress = true;
// Colors with 50% opacity
@@ -231,7 +239,7 @@
new Segment(0.25f, fadedBlue, true)));
List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
- segments, points, progress, isStyledByProgress);
+ segments, points, progress, progressMax, isStyledByProgress);
assertThat(parts).isEqualTo(expected);
}
@@ -247,10 +255,11 @@
points.add(new ProgressStyle.Point(60).setColor(Color.BLUE));
points.add(new ProgressStyle.Point(75).setColor(Color.YELLOW));
int progress = 60;
+ int progressMax = 100;
boolean isStyledByProgress = true;
List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
- segments, points, progress, isStyledByProgress);
+ segments, points, progress, progressMax, isStyledByProgress);
// Colors with 50% opacity
int fadedGreen = 0x7F00FF00;
@@ -281,10 +290,11 @@
points.add(new ProgressStyle.Point(25).setColor(Color.BLUE));
points.add(new ProgressStyle.Point(75).setColor(Color.YELLOW));
int progress = 60;
+ int progressMax = 100;
boolean isStyledByProgress = false;
List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
- segments, points, progress, isStyledByProgress);
+ segments, points, progress, progressMax, isStyledByProgress);
List<Part> expected = new ArrayList<>(List.of(
new Segment(0.15f, Color.RED),
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index f36dff0..42188de 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -157,6 +157,13 @@
}
filegroup {
+ name: "wm_shell-shared-utils",
+ srcs: [
+ "shared/src/com/android/wm/shell/shared/TransitionUtil.java",
+ ],
+}
+
+filegroup {
name: "wm_shell-shared-aidls",
srcs: [
diff --git a/libs/WindowManager/Shell/multivalentTests/Android.bp b/libs/WindowManager/Shell/multivalentTests/Android.bp
index ee0d5bb..41d1b5c 100644
--- a/libs/WindowManager/Shell/multivalentTests/Android.bp
+++ b/libs/WindowManager/Shell/multivalentTests/Android.bp
@@ -53,6 +53,8 @@
"mockito-robolectric-prebuilt",
"mockito-kotlin2",
"truth",
+ "flag-junit-base",
+ "flag-junit",
],
auto_gen_config: true,
}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewTest.kt
index 398fd55..4214e6f 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewTest.kt
@@ -18,14 +18,19 @@
import android.content.ComponentName
import android.content.Context
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.wm.shell.Flags
import com.android.wm.shell.taskview.TaskView
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
@@ -36,6 +41,9 @@
@RunWith(AndroidJUnit4::class)
class BubbleTaskViewTest {
+ @get:Rule
+ val setFlagsRule = SetFlagsRule()
+
private lateinit var bubbleTaskView: BubbleTaskView
private val context = ApplicationProvider.getApplicationContext<Context>()
private lateinit var taskView: TaskView
@@ -75,14 +83,33 @@
assertThat(actualComponentName).isEqualTo(componentName)
}
+ @DisableFlags(Flags.FLAG_ENABLE_TASK_VIEW_CONTROLLER_CLEANUP)
@Test
- fun cleanup_invalidTaskId_doesNotRemoveTask() {
+ fun cleanup_flagOff_invalidTaskId_doesNotRemoveTask() {
bubbleTaskView.cleanup()
verify(taskView, never()).removeTask()
}
+ @EnableFlags(Flags.FLAG_ENABLE_TASK_VIEW_CONTROLLER_CLEANUP)
@Test
- fun cleanup_validTaskId_removesTask() {
+ fun cleanup_flagOn_invalidTaskId_removesTask() {
+ bubbleTaskView.cleanup()
+ verify(taskView).removeTask()
+ }
+
+ @DisableFlags(Flags.FLAG_ENABLE_TASK_VIEW_CONTROLLER_CLEANUP)
+ @Test
+ fun cleanup_flagOff_validTaskId_removesTask() {
+ val componentName = ComponentName(context, "TestClass")
+ bubbleTaskView.listener.onTaskCreated(123, componentName)
+
+ bubbleTaskView.cleanup()
+ verify(taskView).removeTask()
+ }
+
+ @EnableFlags(Flags.FLAG_ENABLE_TASK_VIEW_CONTROLLER_CLEANUP)
+ @Test
+ fun cleanup_flagOn_validTaskId_removesTask() {
val componentName = ComponentName(context, "TestClass")
bubbleTaskView.listener.onTaskCreated(123, componentName)
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
index 712cc7c..776ea92 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
@@ -25,6 +25,7 @@
import android.os.UserManager
import android.view.IWindowManager
import android.view.WindowManager
+import android.widget.FrameLayout
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -56,7 +57,9 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.any
import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
/** Test inflating bubbles with [BubbleViewInfoTask]. */
@SmallTest
@@ -158,18 +161,23 @@
mock<BubbleProperties>()
)
- val bubbleStackViewManager = BubbleStackViewManager.fromBubbleController(bubbleController)
- bubbleStackView =
- BubbleStackView(
- context,
- bubbleStackViewManager,
- bubblePositioner,
- bubbleData,
- surfaceSynchronizer,
- FloatingContentCoordinator(),
- bubbleController,
- mainExecutor
- )
+ // TODO: (b/371829099) - when optional overflow is no longer flagged we can enable this
+ // again, something about the overflow being added uncovers an issue with Robolectric and
+ // bitmaps; this is switched to a mock to work around that (b/375513387).
+// val bubbleStackViewManager = BubbleStackViewManager.fromBubbleController(bubbleController)
+// bubbleStackView = BubbleStackView(
+// context,
+// bubbleStackViewManager,
+// bubblePositioner,
+// bubbleData,
+// surfaceSynchronizer,
+// FloatingContentCoordinator(),
+// bubbleController,
+// mainExecutor
+// )
+ bubbleStackView = mock<BubbleStackView>()
+ whenever(bubbleStackView.generateLayoutParams(any()))
+ .thenReturn(FrameLayout.LayoutParams(1000, 1000))
expandedViewManager = BubbleExpandedViewManager.fromBubbleController(bubbleController)
}
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
index 3759618..5e41865 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
@@ -17,20 +17,53 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/maximize_menu"
- android:layout_width="@dimen/desktop_mode_maximize_menu_width"
+ android:layout_width="wrap_content"
android:layout_height="@dimen/desktop_mode_maximize_menu_height"
android:background="@drawable/desktop_mode_maximize_menu_background"
android:elevation="1dp">
<LinearLayout
android:id="@+id/container"
- android:layout_width="@dimen/desktop_mode_maximize_menu_width"
+ android:layout_width="wrap_content"
android:layout_height="@dimen/desktop_mode_maximize_menu_height"
android:orientation="horizontal"
android:padding="16dp"
android:gravity="center">
<LinearLayout
+ android:id="@+id/maximize_menu_immersive_toggle_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <Button
+ android:layout_width="94dp"
+ android:layout_height="60dp"
+ android:id="@+id/maximize_menu_immersive_toggle_button"
+ style="?android:attr/buttonBarButtonStyle"
+ android:stateListAnimator="@null"
+ android:importantForAccessibility="yes"
+ android:contentDescription="@string/desktop_mode_maximize_menu_immersive_button_text"
+ android:layout_marginEnd="8dp"
+ android:layout_marginBottom="4dp"
+ android:alpha="0"/>
+
+ <TextView
+ android:id="@+id/maximize_menu_immersive_toggle_button_text"
+ android:layout_width="94dp"
+ android:layout_height="18dp"
+ android:textSize="11sp"
+ android:layout_marginBottom="76dp"
+ android:gravity="center"
+ android:fontFamily="google-sans-text"
+ android:importantForAccessibility="no"
+ android:text="@string/desktop_mode_maximize_menu_immersive_button_text"
+ android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:alpha="0"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/maximize_menu_size_toggle_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
@@ -43,7 +76,6 @@
android:stateListAnimator="@null"
android:importantForAccessibility="yes"
android:contentDescription="@string/desktop_mode_maximize_menu_maximize_button_text"
- android:layout_marginRight="8dp"
android:layout_marginBottom="4dp"
android:alpha="0"/>
@@ -62,6 +94,7 @@
</LinearLayout>
<LinearLayout
+ android:id="@+id/maximize_menu_snap_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
@@ -73,6 +106,7 @@
android:padding="4dp"
android:background="@drawable/desktop_mode_maximize_menu_layout_background"
android:layout_marginBottom="4dp"
+ android:layout_marginStart="8dp"
android:alpha="0">
<Button
android:id="@+id/maximize_menu_snap_left_button"
@@ -115,7 +149,7 @@
used to monitor input events over the entire menu. -->
<View
android:id="@+id/maximize_menu_overlay"
- android:layout_width="@dimen/desktop_mode_maximize_menu_width"
+ android:layout_width="match_parent"
android:layout_height="@dimen/desktop_mode_maximize_menu_height"/>
</FrameLayout>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index d02c77e..6cd380d 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -479,8 +479,10 @@
<!-- The default minimum allowed window height when resizing a window in desktop mode. -->
<dimen name="desktop_mode_minimum_window_height">352dp</dimen>
- <!-- The width of the maximize menu in desktop mode. -->
- <dimen name="desktop_mode_maximize_menu_width">228dp</dimen>
+ <!-- The width of the maximize menu in desktop mode, depending on the number of options -->
+ <dimen name="desktop_mode_maximize_menu_width_one_options">126dp</dimen>
+ <dimen name="desktop_mode_maximize_menu_width_two_options">228dp</dimen>
+ <dimen name="desktop_mode_maximize_menu_width_three_options">330dp</dimen>
<!-- The height of the maximize menu in desktop mode. -->
<dimen name="desktop_mode_maximize_menu_height">114dp</dimen>
@@ -502,10 +504,14 @@
<dimen name="desktop_mode_maximize_menu_buttons_fill_radius">4dp</dimen>
<!-- The padding between the outline and fill of the maximize menu snap and maximize buttons. -->
<dimen name="desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding">4dp</dimen>
+ <!-- The padding between the outline and fill of the maximize menu snap and maximize buttons. -->
+ <dimen name="desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding_bottom">8dp</dimen>
<!-- The vertical padding between the outline and fill of the maximize menu restore button. -->
<dimen name="desktop_mode_maximize_menu_restore_button_fill_vertical_padding">13dp</dimen>
<!-- The horizontal padding between the outline and fill of the maximize menu restore button. -->
<dimen name="desktop_mode_maximize_menu_restore_button_fill_horizontal_padding">21dp</dimen>
+ <!-- The padding between the outline and fill of the maximize menu immersive button. -->
+ <dimen name="desktop_mode_maximize_menu_immersive_button_fill_padding">4dp</dimen>
<!-- The corner radius of the maximize menu. -->
<dimen name="desktop_mode_maximize_menu_corner_radius">8dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index afac9f6..ef0386a 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -317,7 +317,10 @@
<string name="desktop_mode_maximize_menu_snap_text">Snap Screen</string>
<!-- Snap resizing non-resizable string. -->
<string name="desktop_mode_non_resizable_snap_text">App can\'t be moved here</string>
- <!-- Accessibility text for the Maximize Menu's maximize button [CHAR LIMIT=NONE] -->
+ <!-- Accessibility text for the Maximize Menu's immersive button [CHAR LIMIT=NONE] -->
+ <string name="desktop_mode_maximize_menu_immersive_button_text">Immersive</string>
+ <!-- Accessibility text for the Maximize Menu's immersive restore button [CHAR LIMIT=NONE] -->
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text">Restore</string>
<string name="desktop_mode_maximize_menu_maximize_button_text">Maximize</string>
<!-- Accessibility text for the Maximize Menu's restore button [CHAR LIMIT=NONE] -->
<string name="desktop_mode_maximize_menu_restore_button_text">Restore</string>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 4607a8e..f59fed9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -583,9 +583,8 @@
}
final boolean windowModeChanged =
data.getTaskInfo().getWindowingMode() != taskInfo.getWindowingMode();
- final boolean visibilityChanged = data.getTaskInfo().isVisible != taskInfo.isVisible;
- if (windowModeChanged || (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
- && visibilityChanged)) {
+ if (windowModeChanged
+ || hasFreeformConfigurationChanged(data.getTaskInfo(), taskInfo)) {
mRecentTasks.ifPresent(recentTasks ->
recentTasks.onTaskRunningInfoChanged(taskInfo));
}
@@ -606,6 +605,17 @@
}
}
+ private boolean hasFreeformConfigurationChanged(RunningTaskInfo oldTaskInfo,
+ RunningTaskInfo newTaskInfo) {
+ if (newTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
+ return false;
+ }
+ return oldTaskInfo.isVisible != newTaskInfo.isVisible
+ || !oldTaskInfo.positionInParent.equals(newTaskInfo.positionInParent)
+ || !Objects.equals(oldTaskInfo.configuration.windowConfiguration.getAppBounds(),
+ newTaskInfo.configuration.windowConfiguration.getAppBounds());
+ }
+
@Override
public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
synchronized (mLock) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 73bc426..5eb5d89 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -95,6 +95,7 @@
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
@@ -1277,42 +1278,42 @@
}
// Copy initial changes to final transition
final TransitionInfo init = mOpenTransitionInfo;
- // find prepare open target
+ // Find prepare open target
boolean openShowWallpaper = false;
- ComponentName openComponent = null;
+ final ArrayList<OpenChangeInfo> targets = new ArrayList<>();
int tmpSize;
- int openTaskId = INVALID_TASK_ID;
- WindowContainerToken openToken = null;
for (int j = init.getChanges().size() - 1; j >= 0; --j) {
final TransitionInfo.Change change = init.getChanges().get(j);
- if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
- openComponent = findComponentName(change);
- openTaskId = findTaskId(change);
- openToken = findToken(change);
+ if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)
+ && TransitionUtil.isOpeningMode(change.getMode())) {
+ final ComponentName openComponent = findComponentName(change);
+ final int openTaskId = findTaskId(change);
+ final WindowContainerToken openToken = findToken(change);
+ if (openComponent == null && openTaskId == INVALID_TASK_ID
+ && openToken == null) {
+ continue;
+ }
+ targets.add(new OpenChangeInfo(openComponent, openTaskId, openToken));
if (change.hasFlags(FLAG_SHOW_WALLPAPER)) {
openShowWallpaper = true;
}
- break;
}
}
- if (openComponent == null && openTaskId == INVALID_TASK_ID && openToken == null) {
+ if (targets.isEmpty()) {
// This shouldn't happen, but if that happen, consume the initial transition anyway.
Log.e(TAG, "Unable to merge following transition, cannot find the gesture "
+ "animated target from the open transition=" + mOpenTransitionInfo);
mOpenTransitionInfo = null;
return;
}
- // find first non-prepare open target
+ // Find first non-prepare open target
boolean isOpen = false;
tmpSize = info.getChanges().size();
for (int j = 0; j < tmpSize; ++j) {
final TransitionInfo.Change change = info.getChanges().get(j);
- final ComponentName firstNonOpen = findComponentName(change);
- final int firstTaskId = findTaskId(change);
- if ((firstNonOpen != null && firstNonOpen != openComponent)
- || (firstTaskId != INVALID_TASK_ID && firstTaskId != openTaskId)) {
- // this is original close target, potential be close, but cannot determine from
- // it
+ if (isOpenChangeMatched(targets, change)) {
+ // This is original close target, potential be close, but cannot determine
+ // from it.
if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
isOpen = !TransitionUtil.isClosingMode(change.getMode());
} else {
@@ -1321,33 +1322,44 @@
}
}
}
-
if (!isOpen) {
// Close transition, the transition info should be:
// init info(open A & wallpaper)
// current info(close B target)
// remove init info(open/change A target & wallpaper)
boolean moveToTop = false;
+ boolean excludeOpenTarget = false;
+ boolean mergePredictive = false;
for (int j = info.getChanges().size() - 1; j >= 0; --j) {
final TransitionInfo.Change change = info.getChanges().get(j);
- if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) {
+ if (isOpenChangeMatched(targets, change)) {
+ if (TransitionUtil.isClosingMode(change.getMode())) {
+ excludeOpenTarget = true;
+ }
moveToTop = change.hasFlags(FLAG_MOVED_TO_TOP);
info.getChanges().remove(j);
} else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))
|| !change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
info.getChanges().remove(j);
+ } else if (!mergePredictive && TransitionUtil.isClosingMode(change.getMode())) {
+ mergePredictive = true;
}
}
// Ignore merge if there is no close target
- if (!info.getChanges().isEmpty()) {
+ if (!info.getChanges().isEmpty() && mergePredictive) {
tmpSize = init.getChanges().size();
for (int i = 0; i < tmpSize; ++i) {
final TransitionInfo.Change change = init.getChanges().get(i);
if (change.hasFlags(FLAG_IS_WALLPAPER)) {
continue;
}
- if (moveToTop) {
- if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) {
+ if (isOpenChangeMatched(targets, change)) {
+ if (excludeOpenTarget) {
+ // App has triggered another change during predictive back
+ // transition, filter out predictive back target.
+ continue;
+ }
+ if (moveToTop) {
change.setFlags(change.getFlags() | FLAG_MOVED_TO_TOP);
}
}
@@ -1376,7 +1388,7 @@
if (nonBackClose && nonBackOpen) {
for (int j = info.getChanges().size() - 1; j >= 0; --j) {
final TransitionInfo.Change change = info.getChanges().get(j);
- if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) {
+ if (isOpenChangeMatched(targets, change)) {
info.getChanges().remove(j);
} else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))) {
info.getChanges().remove(j);
@@ -1659,9 +1671,21 @@
final ComponentName openChange = findComponentName(change);
final int firstTaskId = findTaskId(change);
final WindowContainerToken openToken = findToken(change);
- return (openChange != null && openChange == topActivity)
+ return (openChange != null && openChange.equals(topActivity))
|| (firstTaskId != INVALID_TASK_ID && firstTaskId == taskId)
- || (openToken != null && token == openToken);
+ || (openToken != null && openToken.equals(token));
+ }
+
+ static boolean isOpenChangeMatched(@NonNull ArrayList<OpenChangeInfo> targets,
+ TransitionInfo.Change change) {
+ for (int i = targets.size() - 1; i >= 0; --i) {
+ final OpenChangeInfo next = targets.get(i);
+ if (isSameChangeTarget(next.mOpenComponent, next.mOpenTaskId, next.mOpenToken,
+ change)) {
+ return true;
+ }
+ }
+ return false;
}
private static boolean canBeTransitionTarget(TransitionInfo.Change change) {
@@ -1721,4 +1745,16 @@
}
}
}
+
+ static class OpenChangeInfo {
+ final ComponentName mOpenComponent;
+ final int mOpenTaskId;
+ final WindowContainerToken mOpenToken;
+ OpenChangeInfo(ComponentName openComponent, int openTaskId,
+ WindowContainerToken openToken) {
+ mOpenComponent = openComponent;
+ mOpenTaskId = openTaskId;
+ mOpenToken = openToken;
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt
index 65f8e48..68fc0c9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt
@@ -16,14 +16,11 @@
package com.android.wm.shell.bubbles
-import android.app.ActivityTaskManager
import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.content.ComponentName
-import android.os.RemoteException
-import android.util.Log
import androidx.annotation.VisibleForTesting
+import com.android.wm.shell.Flags
import com.android.wm.shell.taskview.TaskView
-import com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS
import java.util.concurrent.Executor
/**
@@ -89,21 +86,8 @@
* This should be called after all other cleanup animations have finished.
*/
fun cleanup() {
- if (taskId != INVALID_TASK_ID) {
- // Ensure the task is removed from WM
- if (ENABLE_SHELL_TRANSITIONS) {
- taskView.removeTask()
- } else {
- try {
- ActivityTaskManager.getService().removeTask(taskId)
- } catch (e: RemoteException) {
- Log.w(TAG, e.message ?: "")
- }
- }
+ if (Flags.enableTaskViewControllerCleanup() || taskId != INVALID_TASK_ID) {
+ taskView.removeTask()
}
}
-
- private companion object {
- const val TAG = "BubbleTaskView"
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
index 9fd255d..7adec39 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
@@ -20,6 +20,7 @@
import android.graphics.Point
import android.graphics.Rect
import android.util.Log
+import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -102,14 +103,17 @@
hideEducation(animated = false)
log { "showStackEducation at: $position" }
+ val rootBounds = Rect()
+ // Get root bounds on screen as position is in screen coordinates
+ root.getBoundsOnScreen(rootBounds)
educationView =
createEducationView(R.layout.bubble_bar_stack_education, root).apply {
setArrowDirection(BubblePopupDrawable.ArrowDirection.DOWN)
- setArrowPosition(BubblePopupDrawable.ArrowPosition.End)
- updateEducationPosition(view = this, position, root)
+ updateEducationPosition(view = this, position, rootBounds)
val arrowToEdgeOffset = popupDrawable?.config?.cornerRadius ?: 0f
doOnLayout {
- it.pivotX = it.width - arrowToEdgeOffset
+ it.pivotX = if (position.x < rootBounds.centerX())
+ arrowToEdgeOffset else it.width - arrowToEdgeOffset
it.pivotY = it.height.toFloat()
}
setOnClickListener { educationClickHandler() }
@@ -218,12 +222,9 @@
*
* @param view the user education view to layout
* @param position the reference position in Screen coordinates
- * @param root the root view to use for the layout
+ * @param rootBounds bounds of the parent the education view is placed in
*/
- private fun updateEducationPosition(view: BubblePopupView, position: Point, root: ViewGroup) {
- val rootBounds = Rect()
- // Get root bounds on screen as position is in screen coordinates
- root.getBoundsOnScreen(rootBounds)
+ private fun updateEducationPosition(view: BubblePopupView, position: Point, rootBounds: Rect) {
// Get the offset to the arrow from the edge of the education view
val arrowToEdgeOffset =
view.popupDrawable?.config?.let { it.cornerRadius + it.arrowWidth / 2f }?.roundToInt()
@@ -231,7 +232,15 @@
// Calculate education view margins
val params = view.layoutParams as FrameLayout.LayoutParams
params.bottomMargin = rootBounds.bottom - position.y
- params.rightMargin = rootBounds.right - position.x - arrowToEdgeOffset
+ if (position.x < rootBounds.centerX()) {
+ params.leftMargin = position.x - arrowToEdgeOffset
+ params.gravity = Gravity.LEFT or Gravity.BOTTOM
+ view.setArrowPosition(BubblePopupDrawable.ArrowPosition.Start)
+ } else {
+ params.rightMargin = rootBounds.right - position.x - arrowToEdgeOffset
+ params.gravity = Gravity.RIGHT or Gravity.BOTTOM
+ view.setArrowPosition(BubblePopupDrawable.ArrowPosition.End)
+ }
view.layoutParams = params
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
index 5998dc8..6f7b716 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
@@ -129,19 +129,24 @@
DesktopModeStatus.getMaxTaskLimit(context).takeIf { it > 0 }
?: desktop.zOrderedTasksCount
+ var visibleTasksCount = 0
desktop.zOrderedTasksList
// Reverse it so we initialize the repo from bottom to top.
.reversed()
- .mapNotNull { taskId ->
- desktop.tasksByTaskIdMap[taskId]?.takeIf {
- it.desktopTaskState == DesktopTaskState.VISIBLE
- }
- }
- .take(maxTasks)
+ .mapNotNull { taskId -> desktop.tasksByTaskIdMap[taskId] }
.forEach { task ->
- addOrMoveFreeformTaskToTop(desktop.displayId, task.taskId)
- addActiveTask(desktop.displayId, task.taskId)
- updateTaskVisibility(desktop.displayId, task.taskId, visible = false)
+ if (task.desktopTaskState == DesktopTaskState.VISIBLE
+ && visibleTasksCount < maxTasks
+ ) {
+ visibleTasksCount++
+ addOrMoveFreeformTaskToTop(desktop.displayId, task.taskId)
+ addActiveTask(desktop.displayId, task.taskId)
+ updateTaskVisibility(desktop.displayId, task.taskId, visible = false)
+ } else {
+ addActiveTask(desktop.displayId, task.taskId)
+ updateTaskVisibility(desktop.displayId, task.taskId, visible = false)
+ minimizeTask(desktop.displayId, task.taskId)
+ }
}
}
}
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 77fb4b4..bc78e43 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
@@ -373,7 +373,7 @@
}
logV("moveBackgroundTaskToDesktop with taskId=%d", taskId)
// TODO(342378842): Instead of using default display, support multiple displays
- val taskToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(
+ val taskIdToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(
DEFAULT_DISPLAY, wct, taskId)
val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable(
wct = wct,
@@ -388,7 +388,7 @@
)
// TODO(343149901): Add DPI changes for task launch
val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource)
- addPendingMinimizeTransition(transition, taskToMinimize)
+ taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
runOnTransit?.invoke(transition)
return true
}
@@ -412,12 +412,12 @@
excludeTaskId = task.taskId,
)
// Bring other apps to front first
- val taskToMinimize =
+ val taskIdToMinimize =
bringDesktopAppsToFrontBeforeShowingNewTask(task.displayId, wct, task.taskId)
addMoveToDesktopChanges(wct, task)
val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource)
- addPendingMinimizeTransition(transition, taskToMinimize)
+ taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
runOnTransit?.invoke(transition)
}
@@ -452,14 +452,14 @@
val wct = WindowContainerTransaction()
exitSplitIfApplicable(wct, taskInfo)
moveHomeTask(wct, toTop = true)
- val taskToMinimize =
+ val taskIdToMinimize =
bringDesktopAppsToFrontBeforeShowingNewTask(taskInfo.displayId, wct, taskInfo.taskId)
addMoveToDesktopChanges(wct, taskInfo)
val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable(
wct, taskInfo.displayId)
val transition = dragToDesktopTransitionHandler.finishDragToDesktopTransition(wct)
transition?.let {
- addPendingMinimizeTransition(it, taskToMinimize)
+ taskIdToMinimize?.let { taskId -> addPendingMinimizeTransition(it, taskId) }
runOnTransit?.invoke(transition)
}
}
@@ -651,7 +651,13 @@
excludeTaskId = taskInfo.taskId,
)
val transition =
- startLaunchTransition(TRANSIT_TO_FRONT, wct, taskInfo.taskId, remoteTransition)
+ startLaunchTransition(
+ TRANSIT_TO_FRONT,
+ wct,
+ taskInfo.taskId,
+ remoteTransition,
+ taskInfo.displayId
+ )
runOnTransit?.invoke(transition)
}
@@ -660,15 +666,15 @@
wct: WindowContainerTransaction,
taskId: Int,
remoteTransition: RemoteTransition?,
+ displayId: Int = DEFAULT_DISPLAY,
): IBinder {
- val taskToMinimize: RunningTaskInfo? =
- addAndGetMinimizeChanges(DEFAULT_DISPLAY, wct, taskId)
+ val taskIdToMinimize = addAndGetMinimizeChanges(displayId, wct, taskId)
if (remoteTransition == null) {
val t = transitions.startTransition(transitionType, wct, null /* handler */)
- addPendingMinimizeTransition(t, taskToMinimize)
+ taskIdToMinimize?.let { addPendingMinimizeTransition(t, it) }
return t
}
- if (taskToMinimize == null) {
+ if (taskIdToMinimize == null) {
val remoteTransitionHandler = OneShotRemoteHandler(mainExecutor, remoteTransition)
val t = transitions.startTransition(transitionType, wct, remoteTransitionHandler)
remoteTransitionHandler.setTransition(t)
@@ -676,10 +682,10 @@
}
val remoteTransitionHandler =
DesktopWindowLimitRemoteHandler(
- mainExecutor, rootTaskDisplayAreaOrganizer, remoteTransition, taskToMinimize.taskId)
+ mainExecutor, rootTaskDisplayAreaOrganizer, remoteTransition, taskIdToMinimize)
val t = transitions.startTransition(transitionType, wct, remoteTransitionHandler)
remoteTransitionHandler.setTransition(t)
- addPendingMinimizeTransition(t, taskToMinimize)
+ taskIdToMinimize?.let { addPendingMinimizeTransition(t, it) }
return t
}
@@ -1032,13 +1038,13 @@
displayId: Int,
wct: WindowContainerTransaction,
newTaskIdInFront: Int
- ): RunningTaskInfo? = bringDesktopAppsToFront(displayId, wct, newTaskIdInFront)
+ ): Int? = bringDesktopAppsToFront(displayId, wct, newTaskIdInFront)
private fun bringDesktopAppsToFront(
displayId: Int,
wct: WindowContainerTransaction,
newTaskIdInFront: Int? = null
- ): RunningTaskInfo? {
+ ): Int? {
logV("bringDesktopAppsToFront, newTaskId=%d", newTaskIdInFront)
// Move home to front, ensures that we go back home when all desktop windows are closed
moveHomeTask(wct, toTop = true)
@@ -1054,11 +1060,10 @@
taskRepository.getExpandedTasksOrdered(displayId)
// If we're adding a new Task we might need to minimize an old one
// TODO(b/365725441): Handle non running task minimization
- val taskToMinimize: RunningTaskInfo? =
+ val taskIdToMinimize: Int? =
if (newTaskIdInFront != null && desktopTasksLimiter.isPresent) {
- desktopTasksLimiter
- .get()
- .getTaskToMinimize(
+ desktopTasksLimiter.get()
+ .getTaskIdToMinimize(
expandedTasksOrderedFrontToBack,
newTaskIdInFront
)
@@ -1068,7 +1073,7 @@
expandedTasksOrderedFrontToBack
// If there is a Task to minimize, let it stay behind the Home Task
- .filter { taskId -> taskId != taskToMinimize?.taskId }
+ .filter { taskId -> taskId != taskIdToMinimize }
.reversed() // Start from the back so the front task is brought forward last
.forEach { taskId ->
val runningTaskInfo = shellTaskOrganizer.getRunningTaskInfo(taskId)
@@ -1089,7 +1094,7 @@
taskbarDesktopTaskListener?.
onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding(displayId))
- return taskToMinimize
+ return taskIdToMinimize
}
private fun moveHomeTask(wct: WindowContainerTransaction, toTop: Boolean) {
@@ -1341,7 +1346,7 @@
val options = createNewWindowOptions(callingTask)
if (options.launchWindowingMode == WINDOWING_MODE_FREEFORM) {
wct.startTask(requestedTaskId, options.toBundle())
- val taskToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(
+ val taskIdToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(
callingTask.displayId, wct, requestedTaskId)
val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable(
wct = wct,
@@ -1349,7 +1354,7 @@
excludeTaskId = requestedTaskId,
)
val transition = transitions.startTransition(TRANSIT_OPEN, wct, null)
- addPendingMinimizeTransition(transition, taskToMinimize)
+ taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
runOnTransit?.invoke(transition)
} else {
val splitPosition = splitScreenController.determineNewInstancePosition(callingTask)
@@ -1489,9 +1494,9 @@
// 1) Exit immersive if needed.
desktopImmersiveController.exitImmersiveIfApplicable(transition, wct, task.displayId)
// 2) minimize a Task if needed.
- val taskToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
- if (taskToMinimize != null) {
- addPendingMinimizeTransition(transition, taskToMinimize)
+ val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
+ if (taskIdToMinimize != null) {
+ addPendingMinimizeTransition(transition, taskIdToMinimize)
return wct
}
return if (wct.isEmpty) null else wct
@@ -1515,9 +1520,8 @@
// Desktop Mode is already showing and we're launching a new Task - we might need to
// minimize another Task.
- val taskToMinimize =
- addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
- addPendingMinimizeTransition(transition, taskToMinimize)
+ val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
+ taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
desktopImmersiveController.exitImmersiveIfApplicable(
transition, wct, task.displayId
)
@@ -1683,7 +1687,7 @@
displayId: Int,
wct: WindowContainerTransaction,
newTaskId: Int
- ): RunningTaskInfo? {
+ ): Int? {
if (!desktopTasksLimiter.isPresent) return null
return desktopTasksLimiter
.get()
@@ -1692,9 +1696,9 @@
private fun addPendingMinimizeTransition(
transition: IBinder,
- taskToMinimize: RunningTaskInfo?
+ taskIdToMinimize: Int,
) {
- if (taskToMinimize == null) return
+ val taskToMinimize = shellTaskOrganizer.getRunningTaskInfo(taskIdToMinimize) ?: return
desktopTasksLimiter.ifPresent {
it.addPendingMinimizeChange(transition, taskToMinimize.displayId, taskToMinimize.taskId)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
index cd28a4f..7bcc5d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.desktopmode
-import android.app.ActivityManager.RunningTaskInfo
import android.content.Context
import android.os.Handler
import android.os.IBinder
@@ -190,17 +189,19 @@
displayId: Int,
wct: WindowContainerTransaction,
newFrontTaskId: Int,
- ): RunningTaskInfo? {
+ ): Int? {
logV("addAndGetMinimizeTaskChanges, newFrontTask=%d", newFrontTaskId)
- // This list is ordered from front to back.
- val newTaskOrderedList = createOrderedTaskListWithNewTask(
- taskRepository.getExpandedTasksOrdered(displayId), newFrontTaskId)
- val taskToMinimize = getTaskToMinimize(newTaskOrderedList)
- if (taskToMinimize != null) {
- wct.reorder(taskToMinimize.token, false /* onTop */)
- return taskToMinimize
+
+ val taskIdToMinimize =
+ getTaskIdToMinimize(
+ taskRepository.getExpandedTasksOrdered(displayId),
+ newFrontTaskId
+ )
+ // If it's a running task, reorder it to back.
+ taskIdToMinimize?.let { shellTaskOrganizer.getRunningTaskInfo(it) }?.let {
+ wct.reorder(it.token, false /* onTop */)
}
- return null
+ return taskIdToMinimize
}
/**
@@ -216,31 +217,33 @@
* Returns the minimized task from the list of visible tasks ordered from front to back with
* the new task placed in front of other tasks.
*/
- fun getTaskToMinimize(
+ fun getTaskIdToMinimize(
visibleOrderedTasks: List<Int>,
- newTaskIdInFront: Int
- ): RunningTaskInfo? =
- getTaskToMinimize(createOrderedTaskListWithNewTask(visibleOrderedTasks, newTaskIdInFront))
-
- /** Returns the Task to minimize given a list of visible tasks ordered from front to back. */
- fun getTaskToMinimize(visibleOrderedTasks: List<Int>): RunningTaskInfo? {
- if (visibleOrderedTasks.size <= maxTasksLimit) {
- logV("No need to minimize; tasks below limit")
- return null
- }
- val taskIdToMinimize = visibleOrderedTasks.last()
- val taskToMinimize =
- shellTaskOrganizer.getRunningTaskInfo(taskIdToMinimize)
- if (taskToMinimize == null) {
- logE("taskToMinimize(taskId = %d) == null", taskIdToMinimize)
- return null
- }
- return taskToMinimize
+ newTaskIdInFront: Int? = null
+ ): Int? {
+ return getTaskIdToMinimize(
+ createOrderedTaskListWithGivenTaskInFront(
+ visibleOrderedTasks, newTaskIdInFront))
}
- private fun createOrderedTaskListWithNewTask(
- orderedTaskIds: List<Int>, newTaskId: Int): List<Int> =
- listOf(newTaskId) + orderedTaskIds.filter { taskId -> taskId != newTaskId }
+ /** Returns the Task to minimize given a list of visible tasks ordered from front to back. */
+ private fun getTaskIdToMinimize(visibleOrderedTasks: List<Int>): Int? {
+ if (visibleOrderedTasks.size <= maxTasksLimit) {
+ logV("No need to minimize; tasks below limit")
+ // No need to minimize anything
+ return null
+ }
+ return visibleOrderedTasks.last()
+ }
+
+ private fun createOrderedTaskListWithGivenTaskInFront(
+ existingTaskIdsOrderedFrontToBack: List<Int>,
+ newTaskId: Int?
+ ): List<Int> {
+ return if (newTaskId == null) existingTaskIdsOrderedFrontToBack
+ else listOf(newTaskId) +
+ existingTaskIdsOrderedFrontToBack.filter { taskId -> taskId != newTaskId }
+ }
@VisibleForTesting
fun getTransitionObserver(): TransitionObserver = minimizeTransitionObserver
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java
index 22cfa32..248a112 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java
@@ -125,25 +125,35 @@
StringJoiner str = new StringJoiner("|");
if ((dragFlags & DRAG_FLAG_GLOBAL) != 0) {
str.add("GLOBAL");
- } else if ((dragFlags & DRAG_FLAG_GLOBAL_URI_READ) != 0) {
+ }
+ if ((dragFlags & DRAG_FLAG_GLOBAL_URI_READ) != 0) {
str.add("GLOBAL_URI_READ");
- } else if ((dragFlags & DRAG_FLAG_GLOBAL_URI_WRITE) != 0) {
+ }
+ if ((dragFlags & DRAG_FLAG_GLOBAL_URI_WRITE) != 0) {
str.add("GLOBAL_URI_WRITE");
- } else if ((dragFlags & DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION) != 0) {
+ }
+ if ((dragFlags & DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION) != 0) {
str.add("GLOBAL_PERSISTABLE_URI_PERMISSION");
- } else if ((dragFlags & DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION) != 0) {
+ }
+ if ((dragFlags & DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION) != 0) {
str.add("GLOBAL_PREFIX_URI_PERMISSION");
- } else if ((dragFlags & DRAG_FLAG_OPAQUE) != 0) {
+ }
+ if ((dragFlags & DRAG_FLAG_OPAQUE) != 0) {
str.add("OPAQUE");
- } else if ((dragFlags & DRAG_FLAG_ACCESSIBILITY_ACTION) != 0) {
+ }
+ if ((dragFlags & DRAG_FLAG_ACCESSIBILITY_ACTION) != 0) {
str.add("ACCESSIBILITY_ACTION");
- } else if ((dragFlags & DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION) != 0) {
+ }
+ if ((dragFlags & DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION) != 0) {
str.add("REQUEST_SURFACE_FOR_RETURN_ANIMATION");
- } else if ((dragFlags & DRAG_FLAG_GLOBAL_SAME_APPLICATION) != 0) {
+ }
+ if ((dragFlags & DRAG_FLAG_GLOBAL_SAME_APPLICATION) != 0) {
str.add("GLOBAL_SAME_APPLICATION");
- } else if ((dragFlags & DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG) != 0) {
+ }
+ if ((dragFlags & DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG) != 0) {
str.add("START_INTENT_SENDER_ON_UNHANDLED_DRAG");
- } else if ((dragFlags & DRAG_FLAG_HIDE_CALLING_TASK_ON_DRAG_START) != 0) {
+ }
+ if ((dragFlags & DRAG_FLAG_HIDE_CALLING_TASK_ON_DRAG_START) != 0) {
str.add("HIDE_CALLING_TASK_ON_DRAG_START");
}
return str.toString();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 89d3dd6..242f7fa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -15,7 +15,12 @@
*/
package com.android.wm.shell.pip.phone;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT;
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_RIGHT;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_TOP;
+import static com.android.wm.shell.pip.phone.PipMenuView.ANIM_TYPE_NONE;
import android.annotation.Nullable;
import android.content.Context;
@@ -23,6 +28,7 @@
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.graphics.Region;
import android.hardware.input.InputManager;
import android.os.Looper;
import android.view.BatchedInputEventReceiver;
@@ -36,6 +42,8 @@
import androidx.annotation.VisibleForTesting;
+import com.android.internal.policy.TaskResizingAlgorithm;
+import com.android.window.flags.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
@@ -48,6 +56,7 @@
import java.io.PrintWriter;
import java.util.function.Consumer;
+import java.util.function.Function;
/**
* Helper on top of PipTouchHandler that handles inputs OUTSIDE of the PIP window, which is used to
@@ -71,6 +80,7 @@
private final PipPinchResizingAlgorithm mPinchResizingAlgorithm;
private final int mDisplayId;
private final ShellExecutor mMainExecutor;
+ private final Region mTmpRegion = new Region();
private final PointF mDownPoint = new PointF();
private final PointF mDownSecondPoint = new PointF();
@@ -81,15 +91,24 @@
private final Rect mLastResizeBounds = new Rect();
private final Rect mUserResizeBounds = new Rect();
private final Rect mDownBounds = new Rect();
+ private final Rect mDragCornerSize = new Rect();
+ private final Rect mTmpTopLeftCorner = new Rect();
+ private final Rect mTmpTopRightCorner = new Rect();
+ private final Rect mTmpBottomLeftCorner = new Rect();
+ private final Rect mTmpBottomRightCorner = new Rect();
+ private final Rect mDisplayBounds = new Rect();
+ private final Function<Rect, Rect> mMovementBoundsSupplier;
private final Runnable mUpdateMovementBoundsRunnable;
private final Consumer<Rect> mUpdateResizeBoundsCallback;
+ private int mDelta;
private float mTouchSlop;
private boolean mAllowGesture;
private boolean mIsAttached;
private boolean mIsEnabled;
private boolean mEnablePinchResize;
+ private boolean mEnableDragCornerResize;
private boolean mIsSysUiStateValid;
private boolean mThresholdCrossed;
private boolean mOngoingPinchToResize = false;
@@ -113,7 +132,7 @@
PipBoundsState pipBoundsState, PipMotionHelper motionHelper,
PipTouchState pipTouchState, PipTaskOrganizer pipTaskOrganizer,
PipDismissTargetHandler pipDismissTargetHandler,
- Runnable updateMovementBoundsRunnable,
+ Function<Rect, Rect> movementBoundsSupplier, Runnable updateMovementBoundsRunnable,
PipUiEventLogger pipUiEventLogger, PhonePipMenuController menuActivityController,
ShellExecutor mainExecutor, @Nullable PipPerfHintController pipPerfHintController) {
mContext = context;
@@ -126,6 +145,7 @@
mPipTouchState = pipTouchState;
mPipTaskOrganizer = pipTaskOrganizer;
mPipDismissTargetHandler = pipDismissTargetHandler;
+ mMovementBoundsSupplier = movementBoundsSupplier;
mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
mPhonePipMenuController = menuActivityController;
mPipUiEventLogger = pipUiEventLogger;
@@ -161,9 +181,20 @@
}
private void reloadResources() {
+ final Resources res = mContext.getResources();
+ mDelta = res.getDimensionPixelSize(R.dimen.pip_resize_edge_size);
+ mEnableDragCornerResize = Flags.enableDesktopWindowingPip();
mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
}
+ private void resetDragCorners() {
+ mDragCornerSize.set(0, 0, mDelta, mDelta);
+ mTmpTopLeftCorner.set(mDragCornerSize);
+ mTmpTopRightCorner.set(mDragCornerSize);
+ mTmpBottomLeftCorner.set(mDragCornerSize);
+ mTmpBottomRightCorner.set(mDragCornerSize);
+ }
+
private void disposeInputChannel() {
if (mInputEventReceiver != null) {
mInputEventReceiver.dispose();
@@ -211,7 +242,7 @@
@VisibleForTesting
void onInputEvent(InputEvent ev) {
- if (!mEnablePinchResize) {
+ if (!mEnableDragCornerResize && !mEnablePinchResize) {
// No need to handle anything if neither form of resizing is enabled.
return;
}
@@ -239,6 +270,8 @@
if (mEnablePinchResize && mOngoingPinchToResize) {
onPinchResize(mv);
+ } else if (mEnableDragCornerResize) {
+ onDragCornerResize(mv);
}
}
}
@@ -250,6 +283,48 @@
return mCtrlType != CTRL_NONE || mOngoingPinchToResize;
}
+ /**
+ * Check whether the current x,y coordinate is within the region in which drag-resize should
+ * start.
+ * This consists of 4 small squares on the 4 corners of the PIP window, a quarter of which
+ * overlaps with the PIP window while the rest goes outside of the PIP window.
+ * _ _ _ _
+ * |_|_|_________|_|_|
+ * |_|_| |_|_|
+ * | PIP |
+ * | WINDOW |
+ * _|_ _|_
+ * |_|_|_________|_|_|
+ * |_|_| |_|_|
+ */
+ public boolean isWithinDragResizeRegion(int x, int y) {
+ if (!mEnableDragCornerResize) {
+ return false;
+ }
+
+ final Rect currentPipBounds = mPipBoundsState.getBounds();
+ if (currentPipBounds == null) {
+ return false;
+ }
+ resetDragCorners();
+ mTmpTopLeftCorner.offset(currentPipBounds.left - mDelta / 2,
+ currentPipBounds.top - mDelta / 2);
+ mTmpTopRightCorner.offset(currentPipBounds.right - mDelta / 2,
+ currentPipBounds.top - mDelta / 2);
+ mTmpBottomLeftCorner.offset(currentPipBounds.left - mDelta / 2,
+ currentPipBounds.bottom - mDelta / 2);
+ mTmpBottomRightCorner.offset(currentPipBounds.right - mDelta / 2,
+ currentPipBounds.bottom - mDelta / 2);
+
+ mTmpRegion.setEmpty();
+ mTmpRegion.op(mTmpTopLeftCorner, Region.Op.UNION);
+ mTmpRegion.op(mTmpTopRightCorner, Region.Op.UNION);
+ mTmpRegion.op(mTmpBottomLeftCorner, Region.Op.UNION);
+ mTmpRegion.op(mTmpBottomRightCorner, Region.Op.UNION);
+
+ return mTmpRegion.contains(x, y);
+ }
+
public boolean isUsingPinchToZoom() {
return mEnablePinchResize;
}
@@ -260,17 +335,62 @@
public boolean willStartResizeGesture(MotionEvent ev) {
if (isInValidSysUiState()) {
- if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
- if (mEnablePinchResize && ev.getPointerCount() == 2) {
- onPinchResize(ev);
- mOngoingPinchToResize = mAllowGesture;
- return mAllowGesture;
- }
+ switch (ev.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ if (isWithinDragResizeRegion((int) ev.getRawX(), (int) ev.getRawY())) {
+ return true;
+ }
+ break;
+
+ case MotionEvent.ACTION_POINTER_DOWN:
+ if (mEnablePinchResize && ev.getPointerCount() == 2) {
+ onPinchResize(ev);
+ mOngoingPinchToResize = mAllowGesture;
+ return mAllowGesture;
+ }
+ break;
+
+ default:
+ break;
}
}
return false;
}
+ private void setCtrlType(int x, int y) {
+ final Rect currentPipBounds = mPipBoundsState.getBounds();
+
+ Rect movementBounds = mMovementBoundsSupplier.apply(currentPipBounds);
+
+ mDisplayBounds.set(movementBounds.left,
+ movementBounds.top,
+ movementBounds.right + currentPipBounds.width(),
+ movementBounds.bottom + currentPipBounds.height());
+
+ if (mTmpTopLeftCorner.contains(x, y) && currentPipBounds.top != mDisplayBounds.top
+ && currentPipBounds.left != mDisplayBounds.left) {
+ mCtrlType |= CTRL_LEFT;
+ mCtrlType |= CTRL_TOP;
+ }
+ if (mTmpTopRightCorner.contains(x, y) && currentPipBounds.top != mDisplayBounds.top
+ && currentPipBounds.right != mDisplayBounds.right) {
+ mCtrlType |= CTRL_RIGHT;
+ mCtrlType |= CTRL_TOP;
+ }
+ if (mTmpBottomRightCorner.contains(x, y)
+ && currentPipBounds.bottom != mDisplayBounds.bottom
+ && currentPipBounds.right != mDisplayBounds.right) {
+ mCtrlType |= CTRL_RIGHT;
+ mCtrlType |= CTRL_BOTTOM;
+ }
+ if (mTmpBottomLeftCorner.contains(x, y)
+ && currentPipBounds.bottom != mDisplayBounds.bottom
+ && currentPipBounds.left != mDisplayBounds.left) {
+ mCtrlType |= CTRL_LEFT;
+ mCtrlType |= CTRL_BOTTOM;
+ }
+ }
+
private boolean isInValidSysUiState() {
return mIsSysUiStateValid;
}
@@ -364,6 +484,59 @@
}
}
+ private void onDragCornerResize(MotionEvent ev) {
+ int action = ev.getActionMasked();
+ float x = ev.getX();
+ float y = ev.getY() - mOhmOffset;
+ if (action == MotionEvent.ACTION_DOWN) {
+ mLastResizeBounds.setEmpty();
+ mAllowGesture = isInValidSysUiState() && isWithinDragResizeRegion((int) x, (int) y);
+ if (mAllowGesture) {
+ setCtrlType((int) x, (int) y);
+ mDownPoint.set(x, y);
+ mDownBounds.set(mPipBoundsState.getBounds());
+ }
+ } else if (mAllowGesture) {
+ switch (action) {
+ case MotionEvent.ACTION_POINTER_DOWN:
+ // We do not support multi touch for resizing via drag
+ mAllowGesture = false;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ // Capture inputs
+ if (!mThresholdCrossed
+ && Math.hypot(x - mDownPoint.x, y - mDownPoint.y) > mTouchSlop) {
+ mThresholdCrossed = true;
+ // Reset the down to begin resizing from this point
+ mDownPoint.set(x, y);
+ mInputMonitor.pilferPointers();
+ }
+ if (mThresholdCrossed) {
+ if (mPhonePipMenuController.isMenuVisible()) {
+ mPhonePipMenuController.hideMenu(ANIM_TYPE_NONE,
+ false /* resize */);
+ }
+ final Rect currentPipBounds = mPipBoundsState.getBounds();
+ mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(x, y,
+ mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
+ mMinSize.y, mMaxSize, true,
+ mDownBounds.width() > mDownBounds.height()));
+ mPipBoundsAlgorithm.transformBoundsToAspectRatio(mLastResizeBounds,
+ mPipBoundsState.getAspectRatio(), false /* useCurrentMinEdgeSize */,
+ true /* useCurrentSize */);
+ mPipTaskOrganizer.scheduleUserResizePip(mDownBounds, mLastResizeBounds,
+ null);
+ mPipBoundsState.setHasUserResizedPip(true);
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ finishResize();
+ break;
+ }
+ }
+ }
+
private void snapToMovementBoundsEdge(Rect bounds, Rect movementBounds) {
final int leftEdge = bounds.left;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 9c4e723..f4c2a33 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -213,7 +213,7 @@
mPipResizeGestureHandler =
new PipResizeGestureHandler(context, pipBoundsAlgorithm, pipBoundsState,
mMotionHelper, mTouchState, pipTaskOrganizer, mPipDismissTargetHandler,
- this::updateMovementBounds, pipUiEventLogger,
+ this::getMovementBounds, this::updateMovementBounds, pipUiEventLogger,
menuController, mainExecutor, mPipPerfHintController);
mConnection = new PipAccessibilityInteractionConnection(mContext, pipBoundsState,
mMotionHelper, pipTaskOrganizer, mPipBoundsAlgorithm.getSnapAlgorithm(),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 0ed5079..8ac7f89 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -476,6 +476,7 @@
}
mPipTaskOrganizer.removePip();
mTvPipMenuController.closeMenu();
+ mPipNotificationController.dismiss();
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/OWNERS
new file mode 100644
index 0000000..892d150
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/OWNERS
@@ -0,0 +1,5 @@
+# Bubbles team
+madym@google.com
+atsjenk@google.com
+liranb@google.com
+mpodolian@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index e74342e..eaeedba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -40,6 +40,7 @@
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -525,8 +526,13 @@
*/
void removeTask() {
if (mTaskToken == null) {
- // Call to remove task before we have one, do nothing
- Slog.w(TAG, "Trying to remove a task that was never added? (no taskToken)");
+ if (Flags.enableTaskViewControllerCleanup()) {
+ // We don't have a task yet. Only clean up the controller
+ mTaskViewTransitions.removeTaskView(this);
+ } else {
+ // Call to remove task before we have one, do nothing
+ Slog.w(TAG, "Trying to remove a task that was never added? (no taskToken)");
+ }
return;
}
mShellExecutor.execute(() -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index 39648f6..ae75cb5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -37,11 +37,14 @@
import androidx.annotation.VisibleForTesting;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.transition.Transitions;
import java.util.ArrayList;
+import java.util.Map;
import java.util.Objects;
+import java.util.WeakHashMap;
/**
* Handles Shell Transitions that involve TaskView tasks.
@@ -49,8 +52,15 @@
public class TaskViewTransitions implements Transitions.TransitionHandler {
static final String TAG = "TaskViewTransitions";
- private final ArrayMap<TaskViewTaskController, TaskViewRequestedState> mTaskViews =
- new ArrayMap<>();
+ /**
+ * Map of {@link TaskViewTaskController} to {@link TaskViewRequestedState}.
+ * <p>
+ * {@link TaskView} keeps a reference to the {@link TaskViewTaskController} instance and
+ * manages its lifecycle.
+ * Only keep a weak reference to the controller instance here to allow for it to be cleaned
+ * up when its TaskView is no longer used.
+ */
+ private final Map<TaskViewTaskController, TaskViewRequestedState> mTaskViews;
private final ArrayList<PendingTransition> mPending = new ArrayList<>();
private final Transitions mTransitions;
private final boolean[] mRegistered = new boolean[]{false};
@@ -95,6 +105,11 @@
public TaskViewTransitions(Transitions transitions) {
mTransitions = transitions;
+ if (Flags.enableTaskViewControllerCleanup()) {
+ mTaskViews = new WeakHashMap<>();
+ } else {
+ mTaskViews = new ArrayMap<>();
+ }
// Defer registration until the first TaskView because we want this to be the "first" in
// priority when handling requests.
// TODO(210041388): register here once we have an explicit ordering mechanism.
@@ -208,10 +223,21 @@
}
private TaskViewTaskController findTaskView(ActivityManager.RunningTaskInfo taskInfo) {
- for (int i = 0; i < mTaskViews.size(); ++i) {
- if (mTaskViews.keyAt(i).getTaskInfo() == null) continue;
- if (taskInfo.token.equals(mTaskViews.keyAt(i).getTaskInfo().token)) {
- return mTaskViews.keyAt(i);
+ if (Flags.enableTaskViewControllerCleanup()) {
+ for (TaskViewTaskController controller : mTaskViews.keySet()) {
+ if (controller.getTaskInfo() == null) continue;
+ if (taskInfo.token.equals(controller.getTaskInfo().token)) {
+ return controller;
+ }
+ }
+ } else {
+ ArrayMap<TaskViewTaskController, TaskViewRequestedState> taskViews =
+ (ArrayMap<TaskViewTaskController, TaskViewRequestedState>) mTaskViews;
+ for (int i = 0; i < taskViews.size(); ++i) {
+ if (taskViews.keyAt(i).getTaskInfo() == null) continue;
+ if (taskInfo.token.equals(taskViews.keyAt(i).getTaskInfo().token)) {
+ return taskViews.keyAt(i);
+ }
}
}
return null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index a775cbc..f404326 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -130,6 +130,7 @@
import com.android.wm.shell.transition.FocusTransitionObserver;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.ExclusionRegionListener;
+import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer;
import com.android.wm.shell.windowdecor.extension.InsetsStateKt;
import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
@@ -180,6 +181,7 @@
private boolean mTransitionDragActive;
private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
+ private DesktopStatusBarInputLayerSupplier mStatusBarInputLayerSupplier;
private final ExclusionRegionListener mExclusionRegionListener =
new ExclusionRegionListenerImpl();
@@ -418,7 +420,11 @@
return Unit.INSTANCE;
});
}
- mFocusTransitionObserver.setLocalFocusTransitionListener(this, mMainExecutor);
+ if (Flags.enableHandleInputFix()) {
+ mStatusBarInputLayerSupplier =
+ new DesktopStatusBarInputLayerSupplier(mContext, mMainHandler);
+ mFocusTransitionObserver.setLocalFocusTransitionListener(this, mMainExecutor);
+ }
}
@Override
@@ -474,6 +480,7 @@
removeTaskFromEventReceiver(oldTaskInfo.displayId);
incrementEventReceiverTasks(taskInfo.displayId);
}
+ decoration.setStatusBarInputLayer(getStatusBarInputLayer(taskInfo));
decoration.relayout(taskInfo, decoration.mHasGlobalFocus);
mActivityOrientationChangeHandler.ifPresent(handler ->
handler.handleActivityOrientationChange(oldTaskInfo, taskInfo));
@@ -512,6 +519,7 @@
if (decoration == null) {
createWindowDecoration(taskInfo, taskSurface, startT, finishT);
} else {
+ decoration.setStatusBarInputLayer(getStatusBarInputLayer(taskInfo));
decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
false /* shouldSetTaskPositionAndCrop */,
mFocusTransitionObserver.hasGlobalFocus(taskInfo));
@@ -578,6 +586,7 @@
return;
}
mDesktopTasksController.toggleDesktopTaskFullImmersiveState(decoration.mTaskInfo);
+ decoration.closeMaximizeMenu();
}
private void onSnapResize(int taskId, boolean left, MotionEvent motionEvent) {
@@ -663,7 +672,7 @@
decoration.closeHandleMenu();
// When the app enters split-select, the handle will no longer be visible, meaning
// we shouldn't receive input for it any longer.
- decoration.disposeStatusBarInputLayer();
+ decoration.detachStatusBarInputLayer();
mDesktopTasksController.requestSplit(decoration.mTaskInfo, false /* leftOrTop */);
}
@@ -1303,8 +1312,8 @@
// should not be receiving any input.
if (resultType == TO_SPLIT_LEFT_INDICATOR
|| resultType == TO_SPLIT_RIGHT_INDICATOR) {
- relevantDecor.disposeStatusBarInputLayer();
- // We should also dispose the other split task's input layer if
+ relevantDecor.detachStatusBarInputLayer();
+ // We should also detach the other split task's input layer if
// applicable.
final int splitPosition = mSplitScreenController
.getSplitPosition(relevantDecor.mTaskInfo.taskId);
@@ -1317,7 +1326,7 @@
mSplitScreenController.getTaskInfo(oppositePosition);
if (oppositeTaskInfo != null) {
mWindowDecorByTaskId.get(oppositeTaskInfo.taskId)
- .disposeStatusBarInputLayer();
+ .detachStatusBarInputLayer();
}
}
}
@@ -1529,6 +1538,10 @@
touchEventListener.mMotionEvent);
return Unit.INSTANCE;
});
+ windowDecoration.setOnImmersiveOrRestoreClickListener(() -> {
+ onEnterOrExitImmersive(taskInfo.taskId);
+ return Unit.INSTANCE;
+ });
windowDecoration.setOnLeftSnapClickListener(() -> {
onSnapResize(taskInfo.taskId, /* isLeft= */ true, touchEventListener.mMotionEvent);
return Unit.INSTANCE;
@@ -1563,6 +1576,7 @@
touchEventListener, touchEventListener, touchEventListener, touchEventListener);
windowDecoration.setExclusionRegionListener(mExclusionRegionListener);
windowDecoration.setDragPositioningCallback(taskPositioner);
+ windowDecoration.setStatusBarInputLayer(getStatusBarInputLayer(taskInfo));
windowDecoration.relayout(taskInfo, startT, finishT,
false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */,
mFocusTransitionObserver.hasGlobalFocus(taskInfo));
@@ -1571,6 +1585,18 @@
}
}
+ /** Decide which cached status bar input layer should be used for a decoration. */
+ private AdditionalSystemViewContainer getStatusBarInputLayer(
+ RunningTaskInfo taskInfo
+ ) {
+ if (mStatusBarInputLayerSupplier == null) return null;
+ return mStatusBarInputLayerSupplier.getStatusBarInputLayer(
+ taskInfo,
+ mSplitScreenController.getSplitPosition(taskInfo.taskId),
+ mSplitScreenController.isLeftRightSplit()
+ );
+ }
+
private RunningTaskInfo getOtherSplitTask(int taskId) {
@SplitPosition int remainingTaskPosition = mSplitScreenController
.getSplitPosition(taskId) == SPLIT_POSITION_BOTTOM_OR_RIGHT
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index d94f3a9..8865112 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -102,6 +102,7 @@
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
import com.android.wm.shell.shared.desktopmode.ManageWindowsViewContainer;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer;
import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder;
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
@@ -142,6 +143,7 @@
private View.OnLongClickListener mOnCaptionLongClickListener;
private View.OnGenericMotionListener mOnCaptionGenericMotionListener;
private Function0<Unit> mOnMaximizeOrRestoreClickListener;
+ private Function0<Unit> mOnImmersiveOrRestoreClickListener;
private Function0<Unit> mOnLeftSnapClickListener;
private Function0<Unit> mOnRightSnapClickListener;
private Consumer<DesktopModeTransitionSource> mOnToDesktopClickListener;
@@ -198,6 +200,7 @@
private final MultiInstanceHelper mMultiInstanceHelper;
private final WindowDecorCaptionHandleRepository mWindowDecorCaptionHandleRepository;
private final DesktopRepository mDesktopRepository;
+ private AdditionalSystemViewContainer mStatusBarInputLayer;
DesktopModeWindowDecoration(
Context context,
@@ -291,6 +294,14 @@
mOnMaximizeOrRestoreClickListener = listener;
}
+ /**
+ * Registers a listener to be called back when one of the tasks' immersive/restore action is
+ * triggered.
+ */
+ void setOnImmersiveOrRestoreClickListener(Function0<Unit> listener) {
+ mOnImmersiveOrRestoreClickListener = listener;
+ }
+
/** Registers a listener to be called when the decoration's snap-left action is triggered.*/
void setOnLeftSnapClickListener(Function0<Unit> listener) {
mOnLeftSnapClickListener = listener;
@@ -497,13 +508,13 @@
notifyNoCaptionHandle();
}
mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
- disposeStatusBarInputLayer();
+ detachStatusBarInputLayer();
Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces
return;
}
if (oldRootView != mResult.mRootView) {
- disposeStatusBarInputLayer();
+ detachStatusBarInputLayer();
mWindowDecorViewHolder = createViewHolder();
}
Trace.beginSection("DesktopModeWindowDecoration#relayout-binding");
@@ -521,6 +532,9 @@
mTaskInfo, position, mResult.mCaptionWidth, mResult.mCaptionHeight,
isCaptionVisible()
));
+ if (mStatusBarInputLayer != null) {
+ asAppHandle(mWindowDecorViewHolder).bindStatusBarInputLayer(mStatusBarInputLayer);
+ }
} else {
mWindowDecorViewHolder.bindData(new AppHeaderViewHolder.HeaderData(
mTaskInfo,
@@ -713,7 +727,8 @@
if (!mTaskInfo.isVisible()) {
closeMaximizeMenu();
} else {
- mMaximizeMenu.positionMenu(calculateMaximizeMenuPosition(), startT);
+ final int menuWidth = calculateMaximizeMenuWidth();
+ mMaximizeMenu.positionMenu(calculateMaximizeMenuPosition(menuWidth), startT);
}
}
@@ -732,15 +747,15 @@
}
/**
- * Dispose of the view used to forward inputs in status bar region. Intended to be
+ * Detach the status bar input layer from this decoration. Intended to be
* used any time handle is no longer visible.
*/
- void disposeStatusBarInputLayer() {
+ void detachStatusBarInputLayer() {
if (!isAppHandle(mWindowDecorViewHolder)
|| !Flags.enableHandleInputFix()) {
return;
}
- asAppHandle(mWindowDecorViewHolder).disposeStatusBarInputLayer();
+ asAppHandle(mWindowDecorViewHolder).detachStatusBarInputLayer();
}
private WindowDecorationViewHolder createViewHolder() {
@@ -934,8 +949,27 @@
return Resources.ID_NULL;
}
+ private int calculateMaximizeMenuWidth() {
+ final boolean showImmersive = Flags.enableFullyImmersiveInDesktop()
+ && TaskInfoKt.getRequestingImmersive(mTaskInfo);
+ final boolean showMaximize = true;
+ final boolean showSnaps = mTaskInfo.isResizeable;
+ int showCount = 0;
+ if (showImmersive) showCount++;
+ if (showMaximize) showCount++;
+ if (showSnaps) showCount++;
+ return switch (showCount) {
+ case 1 -> loadDimensionPixelSize(mContext.getResources(),
+ R.dimen.desktop_mode_maximize_menu_width_one_options);
+ case 2 -> loadDimensionPixelSize(mContext.getResources(),
+ R.dimen.desktop_mode_maximize_menu_width_two_options);
+ case 3 -> loadDimensionPixelSize(mContext.getResources(),
+ R.dimen.desktop_mode_maximize_menu_width_three_options);
+ default -> throw new IllegalArgumentException("");
+ };
+ }
- private PointF calculateMaximizeMenuPosition() {
+ private PointF calculateMaximizeMenuPosition(int menuWidth) {
final PointF position = new PointF();
final Resources resources = mContext.getResources();
final DisplayLayout displayLayout =
@@ -951,8 +985,6 @@
final int[] maximizeButtonLocation = new int[2];
maximizeWindowButton.getLocationInWindow(maximizeButtonLocation);
- final int menuWidth = loadDimensionPixelSize(
- resources, R.dimen.desktop_mode_maximize_menu_width);
final int menuHeight = loadDimensionPixelSize(
resources, R.dimen.desktop_mode_maximize_menu_height);
@@ -1183,11 +1215,19 @@
* Create and display maximize menu window
*/
void createMaximizeMenu() {
+ final int menuWidth = calculateMaximizeMenuWidth();
mMaximizeMenu = mMaximizeMenuFactory.create(mSyncQueue, mRootTaskDisplayAreaOrganizer,
mDisplayController, mTaskInfo, mContext,
- calculateMaximizeMenuPosition(), mSurfaceControlTransactionSupplier);
+ calculateMaximizeMenuPosition(menuWidth), mSurfaceControlTransactionSupplier);
mMaximizeMenu.show(
+ /* isTaskInImmersiveMode= */ Flags.enableFullyImmersiveInDesktop()
+ && mDesktopRepository.isTaskInFullImmersiveState(mTaskInfo.taskId),
+ /* menuWidth= */ menuWidth,
+ /* showImmersiveOption= */ Flags.enableFullyImmersiveInDesktop()
+ && TaskInfoKt.getRequestingImmersive(mTaskInfo),
+ /* showSnapOptions= */ mTaskInfo.isResizeable,
mOnMaximizeOrRestoreClickListener,
+ mOnImmersiveOrRestoreClickListener,
mOnLeftSnapClickListener,
mOnRightSnapClickListener,
hovered -> {
@@ -1428,19 +1468,6 @@
}
}
- /**
- * Close an open maximize menu if input is outside of menu coordinates
- *
- * @param ev the tapped point to compare against
- */
- void closeMaximizeMenuIfNeeded(MotionEvent ev) {
- if (!isMaximizeMenuActive()) return;
-
- if (!mMaximizeMenu.isValidMenuInput(ev)) {
- closeMaximizeMenu();
- }
- }
-
boolean isFocused() {
return mHasGlobalFocus;
}
@@ -1568,7 +1595,7 @@
closeManageWindowsMenu();
mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
disposeResizeVeil();
- disposeStatusBarInputLayer();
+ detachStatusBarInputLayer();
clearCurrentViewHostRunnable();
if (canEnterDesktopMode(mContext) && Flags.enableDesktopWindowingAppHandleEducation()) {
notifyNoCaptionHandle();
@@ -1685,6 +1712,16 @@
+ "}";
}
+ /**
+ * Set the view container to be used to forward input through status bar. Null in cases
+ * where input forwarding isn't needed.
+ */
+ public void setStatusBarInputLayer(
+ @Nullable AdditionalSystemViewContainer additionalSystemViewContainer
+ ) {
+ mStatusBarInputLayer = additionalSystemViewContainer;
+ }
+
static class Factory {
DesktopModeWindowDecoration create(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopStatusBarInputLayerSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopStatusBarInputLayerSupplier.kt
new file mode 100644
index 0000000..025bb403
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopStatusBarInputLayerSupplier.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.windowdecor
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration
+import android.content.Context
+import android.graphics.PixelFormat
+import android.os.Handler
+import android.view.Gravity
+import android.view.View
+import android.view.WindowManager
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.shared.split.SplitScreenConstants
+import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
+
+/**
+ * Supplier for [AdditionalSystemViewContainer] objects to be used for forwarding input
+ * events through status bar to an app handle. Currently supports two simultaneous input layers.
+ *
+ * The supplier will pick one of two input layer view containers to use: one for tasks in
+ * fullscreen or top/left split stage, and one for tasks in right split stage.
+ */
+class DesktopStatusBarInputLayerSupplier(
+ private val context: Context,
+ @ShellMainThread handler: Handler
+) {
+ private val inputLayers: MutableList<AdditionalSystemViewContainer> = mutableListOf()
+
+ init {
+ // Post this as creation of the input layer views is a relatively expensive operation.
+ handler.post {
+ repeat(TOTAL_INPUT_LAYERS) {
+ inputLayers.add(createInputLayer())
+ }
+ }
+ }
+
+ private fun createInputLayer(): AdditionalSystemViewContainer {
+ val lp = WindowManager.LayoutParams(
+ WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSPARENT
+ )
+ lp.title = "Desktop status bar input layer"
+ lp.gravity = Gravity.LEFT or Gravity.TOP
+ lp.setTrustedOverlay()
+
+ // Make this window a spy window to enable it to pilfer pointers from the system-wide
+ // gesture listener that receives events before window. This is to prevent notification
+ // shade gesture when we swipe down to enter desktop.
+ lp.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY
+ lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ val view = View(context)
+ view.visibility = View.INVISIBLE
+ return AdditionalSystemViewContainer(
+ WindowManagerWrapper(
+ context.getSystemService<WindowManager>(WindowManager::class.java)
+ ),
+ view,
+ lp
+ )
+ }
+
+ /**
+ * Decide which cached status bar input layer should be used for a decoration, if any.
+ *
+ * [splitPosition] and [isLeftRightSplit] are used to determine which input layer we use.
+ * The first one is reserved for fullscreen tasks or tasks in top/left split,
+ * while the second one is exclusively used for tasks in right split stage. Note we care about
+ * left-right vs top-bottom split as the bottom stage should not use an input layer.
+ */
+ fun getStatusBarInputLayer(
+ taskInfo: RunningTaskInfo,
+ @SplitScreenConstants.SplitPosition splitPosition: Int,
+ isLeftRightSplit: Boolean
+ ): AdditionalSystemViewContainer? {
+ if (!taskInfo.isVisibleRequested) return null
+ // Fullscreen and top/left split tasks will use the first input layer.
+ if (taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+ || splitPosition == SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
+ ) {
+ return inputLayers[LEFT_TOP_INPUT_LAYER]
+ }
+ // Right split tasks will use the second one.
+ if (isLeftRightSplit && splitPosition == SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
+ ) {
+ return inputLayers[RIGHT_SPLIT_INPUT_LAYER]
+ }
+ // Which leaves bottom split and freeform tasks, which do not need an input layer
+ // as the status bar is not blocking them.
+ return null
+ }
+
+ companion object {
+ private const val TOTAL_INPUT_LAYERS = 2
+ // Input layer index for fullscreen tasks and tasks in top-left split
+ private const val LEFT_TOP_INPUT_LAYER = 0
+ // Input layer index for tasks in right split stage. Does not include bottom split as that
+ // stage is not blocked by status bar.
+ private const val RIGHT_SPLIT_INPUT_LAYER = 1
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
index 3ae5a1a..4bb1e7b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
@@ -36,7 +36,6 @@
import android.graphics.drawable.shapes.RoundRectShape
import android.util.StateSet
import android.view.LayoutInflater
-import android.view.MotionEvent
import android.view.MotionEvent.ACTION_HOVER_ENTER
import android.view.MotionEvent.ACTION_HOVER_EXIT
import android.view.MotionEvent.ACTION_HOVER_MOVE
@@ -58,6 +57,8 @@
import androidx.compose.material3.ColorScheme
import androidx.compose.ui.graphics.toArgb
import androidx.core.animation.addListener
+import androidx.core.view.isGone
+import androidx.core.view.isVisible
import com.android.wm.shell.R
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.common.DisplayController
@@ -93,7 +94,6 @@
private val cornerRadius = loadDimensionPixelSize(
R.dimen.desktop_mode_maximize_menu_corner_radius
).toFloat()
- private val menuWidth = loadDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_width)
private val menuHeight = loadDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_height)
private val menuPadding = loadDimensionPixelSize(R.dimen.desktop_mode_menu_padding)
@@ -105,7 +105,12 @@
/** Creates and shows the maximize window. */
fun show(
+ isTaskInImmersiveMode: Boolean,
+ menuWidth: Int,
+ showImmersiveOption: Boolean,
+ showSnapOptions: Boolean,
onMaximizeOrRestoreClickListener: () -> Unit,
+ onImmersiveOrRestoreClickListener: () -> Unit,
onLeftSnapClickListener: () -> Unit,
onRightSnapClickListener: () -> Unit,
onHoverListener: (Boolean) -> Unit,
@@ -113,7 +118,12 @@
) {
if (maximizeMenu != null) return
createMaximizeMenu(
+ isTaskInImmersiveMode = isTaskInImmersiveMode,
+ menuWidth = menuWidth,
+ showImmersiveOption = showImmersiveOption,
+ showSnapOptions = showSnapOptions,
onMaximizeClickListener = onMaximizeOrRestoreClickListener,
+ onImmersiveOrRestoreClickListener = onImmersiveOrRestoreClickListener,
onLeftSnapClickListener = onLeftSnapClickListener,
onRightSnapClickListener = onRightSnapClickListener,
onHoverListener = onHoverListener,
@@ -144,7 +154,12 @@
/** Create a maximize menu that is attached to the display area. */
private fun createMaximizeMenu(
+ isTaskInImmersiveMode: Boolean,
+ menuWidth: Int,
+ showImmersiveOption: Boolean,
+ showSnapOptions: Boolean,
onMaximizeClickListener: () -> Unit,
+ onImmersiveOrRestoreClickListener: () -> Unit,
onLeftSnapClickListener: () -> Unit,
onRightSnapClickListener: () -> Unit,
onHoverListener: (Boolean) -> Unit,
@@ -179,11 +194,20 @@
maximizeMenuView = MaximizeMenuView(
context = decorWindowContext,
sizeToggleDirection = getSizeToggleDirection(),
+ immersiveConfig = if (showImmersiveOption) {
+ MaximizeMenuView.ImmersiveConfig.Visible(
+ getImmersiveToggleDirection(isTaskInImmersiveMode)
+ )
+ } else {
+ MaximizeMenuView.ImmersiveConfig.Hidden
+ },
+ showSnapOptions = showSnapOptions,
menuHeight = menuHeight,
menuPadding = menuPadding,
).also { menuView ->
menuView.bind(taskInfo)
menuView.onMaximizeClickListener = onMaximizeClickListener
+ menuView.onImmersiveOrRestoreClickListener = onImmersiveOrRestoreClickListener
menuView.onLeftSnapClickListener = onLeftSnapClickListener
menuView.onRightSnapClickListener = onRightSnapClickListener
menuView.onMenuHoverListener = onHoverListener
@@ -217,6 +241,15 @@
MaximizeMenuView.SizeToggleDirection.MAXIMIZE
}
+ private fun getImmersiveToggleDirection(
+ isTaskImmersive: Boolean
+ ): MaximizeMenuView.ImmersiveToggleDirection =
+ if (isTaskImmersive) {
+ MaximizeMenuView.ImmersiveToggleDirection.EXIT
+ } else {
+ MaximizeMenuView.ImmersiveToggleDirection.ENTER
+ }
+
private fun loadDimensionPixelSize(resourceId: Int): Int {
return if (resourceId == Resources.ID_NULL) {
0
@@ -226,33 +259,14 @@
}
/**
- * A valid menu input is one of the following:
- * An input that happens in the menu views.
- * Any input before the views have been laid out.
- *
- * @param inputPoint the input to compare against.
- */
- fun isValidMenuInput(ev: MotionEvent): Boolean {
- val x = ev.rawX
- val y = ev.rawY
- return !viewsLaidOut() || (menuPosition.x <= x && menuPosition.x + menuWidth >= x &&
- menuPosition.y <= y && menuPosition.y + menuHeight >= y)
- }
-
- /**
- * Check if the views for maximize menu can be seen.
- */
- private fun viewsLaidOut(): Boolean {
- return maximizeMenu?.view?.isLaidOut ?: false
- }
-
- /**
* The view within the Maximize Menu, presents maximize, restore and snap-to-side options for
* resizing a Task.
*/
class MaximizeMenuView(
- private val context: Context,
+ context: Context,
private val sizeToggleDirection: SizeToggleDirection,
+ immersiveConfig: ImmersiveConfig,
+ showSnapOptions: Boolean,
private val menuHeight: Int,
private val menuPadding: Int
) {
@@ -260,10 +274,20 @@
.inflate(R.layout.desktop_mode_window_decor_maximize_menu, null /* root */) as ViewGroup
private val container = requireViewById(R.id.container)
private val overlay = requireViewById(R.id.maximize_menu_overlay)
+ private val immersiveToggleContainer =
+ requireViewById(R.id.maximize_menu_immersive_toggle_container) as View
+ private val immersiveToggleButtonText =
+ requireViewById(R.id.maximize_menu_immersive_toggle_button_text) as TextView
+ private val immersiveToggleButton =
+ requireViewById(R.id.maximize_menu_immersive_toggle_button) as Button
+ private val sizeToggleContainer =
+ requireViewById(R.id.maximize_menu_size_toggle_container) as View
private val sizeToggleButtonText =
requireViewById(R.id.maximize_menu_size_toggle_button_text) as TextView
private val sizeToggleButton =
requireViewById(R.id.maximize_menu_size_toggle_button) as Button
+ private val snapContainer =
+ requireViewById(R.id.maximize_menu_snap_container) as View
private val snapWindowText =
requireViewById(R.id.maximize_menu_snap_window_text) as TextView
private val snapRightButton =
@@ -282,6 +306,35 @@
private val fillRadius = context.resources
.getDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_buttons_fill_radius)
+ private val immersiveFillPadding = context.resources.getDimensionPixelSize(R.dimen
+ .desktop_mode_maximize_menu_immersive_button_fill_padding)
+ private val maximizeFillPaddingDefault = context.resources.getDimensionPixelSize(R.dimen
+ .desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding)
+ private val maximizeFillPaddingBottom = context.resources.getDimensionPixelSize(R.dimen
+ .desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding_bottom)
+ private val maximizeRestoreFillPaddingVertical = context.resources.getDimensionPixelSize(
+ R.dimen.desktop_mode_maximize_menu_restore_button_fill_vertical_padding)
+ private val maximizeRestoreFillPaddingHorizontal = context.resources.getDimensionPixelSize(
+ R.dimen.desktop_mode_maximize_menu_restore_button_fill_horizontal_padding)
+ private val maximizeFillPaddingRect = Rect(
+ maximizeFillPaddingDefault,
+ maximizeFillPaddingDefault,
+ maximizeFillPaddingDefault,
+ maximizeFillPaddingBottom
+ )
+ private val maximizeRestoreFillPaddingRect = Rect(
+ maximizeRestoreFillPaddingHorizontal,
+ maximizeRestoreFillPaddingVertical,
+ maximizeRestoreFillPaddingHorizontal,
+ maximizeRestoreFillPaddingVertical,
+ )
+ private val immersiveFillPaddingRect = Rect(
+ immersiveFillPadding,
+ immersiveFillPadding,
+ immersiveFillPadding,
+ immersiveFillPadding
+ )
+
private val hoverTempRect = Rect()
private var menuAnimatorSet: AnimatorSet? = null
private lateinit var taskInfo: RunningTaskInfo
@@ -289,6 +342,8 @@
/** Invoked when the maximize or restore option is clicked. */
var onMaximizeClickListener: (() -> Unit)? = null
+ /** Invoked when the immersive or restore option is clicked. */
+ var onImmersiveOrRestoreClickListener: (() -> Unit)? = null
/** Invoked when the left snap option is clicked. */
var onLeftSnapClickListener: (() -> Unit)? = null
/** Invoked when the right snap option is clicked. */
@@ -338,6 +393,11 @@
return@setOnHoverListener false
}
+ immersiveToggleContainer.isGone = immersiveConfig is ImmersiveConfig.Hidden
+ sizeToggleContainer.isVisible = true
+ snapContainer.isGone = !showSnapOptions
+
+ immersiveToggleButton.setOnClickListener { onImmersiveOrRestoreClickListener?.invoke() }
sizeToggleButton.setOnClickListener { onMaximizeClickListener?.invoke() }
snapRightButton.setOnClickListener { onRightSnapClickListener?.invoke() }
snapLeftButton.setOnClickListener { onLeftSnapClickListener?.invoke() }
@@ -349,17 +409,36 @@
true
}
- val btnTextId = if (sizeToggleDirection == SizeToggleDirection.RESTORE)
+ // Maximize/restore button.
+ val sizeToggleBtnTextId = if (sizeToggleDirection == SizeToggleDirection.RESTORE)
R.string.desktop_mode_maximize_menu_restore_button_text
else
R.string.desktop_mode_maximize_menu_maximize_button_text
- val btnText = context.resources.getText(btnTextId)
- sizeToggleButton.contentDescription = btnText
- sizeToggleButtonText.text = btnText
+ val sizeToggleBtnText = context.resources.getText(sizeToggleBtnTextId)
+ sizeToggleButton.contentDescription = sizeToggleBtnText
+ sizeToggleButtonText.text = sizeToggleBtnText
+
+ // Immersive enter/exit button.
+ if (immersiveConfig is ImmersiveConfig.Visible) {
+ val immersiveToggleBtnTextId = when (immersiveConfig.direction) {
+ ImmersiveToggleDirection.ENTER -> {
+ R.string.desktop_mode_maximize_menu_immersive_button_text
+ }
+
+ ImmersiveToggleDirection.EXIT -> {
+ R.string.desktop_mode_maximize_menu_immersive_restore_button_text
+ }
+ }
+ val immersiveToggleBtnText = context.resources.getText(immersiveToggleBtnTextId)
+ immersiveToggleButton.contentDescription = immersiveToggleBtnText
+ immersiveToggleButtonText.text = immersiveToggleBtnText
+ }
// To prevent aliasing.
sizeToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
sizeToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+ immersiveToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+ immersiveToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
}
/** Bind the menu views to the new [RunningTaskInfo] data. */
@@ -373,6 +452,10 @@
sizeToggleButton.background = style.maximizeOption.drawable
sizeToggleButtonText.setTextColor(style.textColor)
+ // Immersive option.
+ immersiveToggleButton.background = style.immersiveOption.drawable
+ immersiveToggleButtonText.setTextColor(style.textColor)
+
// Snap options.
snapWindowText.setTextColor(style.textColor)
updateSplitSnapSelection(SnapToHalfSelection.NONE)
@@ -382,6 +465,8 @@
fun animateOpenMenu(onEnd: () -> Unit) {
sizeToggleButton.setLayerType(View.LAYER_TYPE_HARDWARE, null)
sizeToggleButtonText.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+ immersiveToggleButton.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+ immersiveToggleButtonText.setLayerType(View.LAYER_TYPE_HARDWARE, null)
menuAnimatorSet = AnimatorSet()
menuAnimatorSet?.playTogether(
ObjectAnimator.ofFloat(rootView, SCALE_Y, STARTING_MENU_HEIGHT_SCALE, 1f)
@@ -411,8 +496,10 @@
// scale is cancelled out and only the background is scaled.
val value = animatedValue as Float
sizeToggleButton.scaleY = value
+ immersiveToggleButton.scaleY = value
snapButtonsLayout.scaleY = value
sizeToggleButtonText.scaleY = value
+ immersiveToggleButtonText.scaleY = value
snapWindowText.scaleY = value
}
},
@@ -432,8 +519,10 @@
addUpdateListener {
val value = animatedValue as Float
sizeToggleButton.alpha = value
+ immersiveToggleButton.alpha = value
snapButtonsLayout.alpha = value
sizeToggleButtonText.alpha = value
+ immersiveToggleButtonText.alpha = value
snapWindowText.alpha = value
}
},
@@ -447,6 +536,8 @@
onEnd = {
sizeToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
sizeToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+ immersiveToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+ immersiveToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
onEnd.invoke()
}
)
@@ -457,6 +548,8 @@
fun animateCloseMenu(onEnd: (() -> Unit)) {
sizeToggleButton.setLayerType(View.LAYER_TYPE_HARDWARE, null)
sizeToggleButtonText.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+ immersiveToggleButton.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+ immersiveToggleButtonText.setLayerType(View.LAYER_TYPE_HARDWARE, null)
cancelAnimation()
menuAnimatorSet = AnimatorSet()
menuAnimatorSet?.playTogether(
@@ -487,8 +580,10 @@
// scale is cancelled out and only the background is scaled.
val value = animatedValue as Float
sizeToggleButton.scaleY = value
+ immersiveToggleButton.scaleY = value
snapButtonsLayout.scaleY = value
sizeToggleButtonText.scaleY = value
+ immersiveToggleButtonText.scaleY = value
snapWindowText.scaleY = value
}
},
@@ -508,8 +603,10 @@
addUpdateListener {
val value = animatedValue as Float
sizeToggleButton.alpha = value
+ immersiveToggleButton.alpha = value
snapButtonsLayout.alpha = value
sizeToggleButtonText.alpha = value
+ immersiveToggleButtonText.alpha = value
snapWindowText.alpha = value
}
},
@@ -522,6 +619,8 @@
onEnd = {
sizeToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
sizeToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+ immersiveToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+ immersiveToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
onEnd?.invoke()
}
)
@@ -531,6 +630,14 @@
/** Request that the accessibility service focus on the menu. */
fun requestAccessibilityFocus() {
// Focus the first button in the menu by default.
+ if (immersiveToggleButton.isVisible) {
+ immersiveToggleButton.post {
+ immersiveToggleButton.sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_VIEW_FOCUSED
+ )
+ }
+ return
+ }
sizeToggleButton.post {
sizeToggleButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
}
@@ -557,7 +664,21 @@
backgroundColor = menuBackgroundColor,
textColor = colorScheme.onSurface.toArgb(),
maximizeOption = MenuStyle.MaximizeOption(
- drawable = createMaximizeDrawable(menuBackgroundColor, colorScheme)
+ drawable = createMaximizeOrImmersiveDrawable(
+ menuBackgroundColor,
+ colorScheme,
+ fillPadding = when (sizeToggleDirection) {
+ SizeToggleDirection.MAXIMIZE -> maximizeFillPaddingRect
+ SizeToggleDirection.RESTORE -> maximizeRestoreFillPaddingRect
+ }
+ )
+ ),
+ immersiveOption = MenuStyle.ImmersiveOption(
+ drawable = createMaximizeOrImmersiveDrawable(
+ menuBackgroundColor,
+ colorScheme,
+ fillPadding = immersiveFillPaddingRect,
+ ),
),
snapOptions = MenuStyle.SnapOptions(
inactiveSnapSideColor = colorScheme.outlineVariant.toArgb(),
@@ -624,19 +745,21 @@
}
}
- private fun createMaximizeDrawable(
+ private fun createMaximizeOrImmersiveDrawable(
@ColorInt menuBackgroundColor: Int,
- colorScheme: ColorScheme
+ colorScheme: ColorScheme,
+ fillPadding: Rect,
): StateListDrawable {
val activeStrokeAndFill = colorScheme.primary.toArgb()
val activeBackground = colorScheme.primary.toArgb().withAlpha(OPACITY_12)
- val activeDrawable = createMaximizeButtonDrawable(
+ val activeDrawable = createMaximizeOrImmersiveButtonDrawable(
strokeAndFillColor = activeStrokeAndFill,
backgroundColor = activeBackground,
// Add a mask with the menu background's color because the active background color is
// semi transparent, otherwise the transparency will reveal the stroke/fill color
// behind it.
- backgroundMask = menuBackgroundColor
+ backgroundMask = menuBackgroundColor,
+ fillPadding = fillPadding,
)
return StateListDrawable().apply {
addState(intArrayOf(android.R.attr.state_pressed), activeDrawable)
@@ -646,19 +769,21 @@
// Inactive drawable.
addState(
StateSet.WILD_CARD,
- createMaximizeButtonDrawable(
+ createMaximizeOrImmersiveButtonDrawable(
strokeAndFillColor = colorScheme.outlineVariant.toArgb(),
backgroundColor = colorScheme.surfaceContainerLow.toArgb(),
- backgroundMask = null // not needed because the bg color is fully opaque
+ backgroundMask = null, // not needed because the bg color is fully opaque
+ fillPadding = fillPadding,
)
)
}
}
- private fun createMaximizeButtonDrawable(
+ private fun createMaximizeOrImmersiveButtonDrawable(
@ColorInt strokeAndFillColor: Int,
@ColorInt backgroundColor: Int,
- @ColorInt backgroundMask: Int?
+ @ColorInt backgroundMask: Int?,
+ fillPadding: Rect,
): LayerDrawable {
val layers = mutableListOf<Drawable>()
// First (bottom) layer, effectively the button's border ring once its inner shape is
@@ -708,30 +833,17 @@
paint.style = Paint.Style.FILL
})
- val (horizontalFillPadding, verticalFillPadding) =
- if (sizeToggleDirection == SizeToggleDirection.MAXIMIZE) {
- context.resources.getDimensionPixelSize(R.dimen
- .desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding) to
- context.resources.getDimensionPixelSize(R.dimen
- .desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding)
- } else {
- context.resources.getDimensionPixelSize(R.dimen
- .desktop_mode_maximize_menu_restore_button_fill_horizontal_padding) to
- context.resources.getDimensionPixelSize(R.dimen
- .desktop_mode_maximize_menu_restore_button_fill_vertical_padding)
- }
-
return LayerDrawable(layers.toTypedArray()).apply {
when (numberOfLayers) {
3 -> {
setLayerInset(1, outlineStroke)
- setLayerInset(2, horizontalFillPadding, verticalFillPadding,
- horizontalFillPadding, verticalFillPadding)
+ setLayerInset(2, fillPadding.left, fillPadding.top,
+ fillPadding.right, fillPadding.bottom)
}
4 -> {
setLayerInset(intArrayOf(1, 2), outlineStroke)
- setLayerInset(3, horizontalFillPadding, verticalFillPadding,
- horizontalFillPadding, verticalFillPadding)
+ setLayerInset(3, fillPadding.left, fillPadding.top,
+ fillPadding.right, fillPadding.bottom)
}
else -> error("Unexpected number of layers: $numberOfLayers")
}
@@ -755,11 +867,15 @@
@ColorInt val backgroundColor: Int,
@ColorInt val textColor: Int,
val maximizeOption: MaximizeOption,
+ val immersiveOption: ImmersiveOption,
val snapOptions: SnapOptions,
) {
data class MaximizeOption(
val drawable: StateListDrawable,
)
+ data class ImmersiveOption(
+ val drawable: StateListDrawable,
+ )
data class SnapOptions(
@ColorInt val inactiveSnapSideColor: Int,
@ColorInt val semiActiveSnapSideColor: Int,
@@ -776,10 +892,23 @@
NONE, LEFT, RIGHT
}
+ /** The possible immersive configs for this menu instance. */
+ sealed class ImmersiveConfig {
+ data class Visible(
+ val direction: ImmersiveToggleDirection,
+ ) : ImmersiveConfig()
+ data object Hidden : ImmersiveConfig()
+ }
+
/** The possible selection states of the size toggle button in the maximize menu. */
enum class SizeToggleDirection {
MAXIMIZE, RESTORE
}
+
+ /** The possible selection states of the immersive toggle button in the maximize menu. */
+ enum class ImmersiveToggleDirection {
+ ENTER, EXIT
+ }
}
companion object {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
index 8b6aaaf..1451f36 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
@@ -23,8 +23,8 @@
import android.view.LayoutInflater
import android.view.SurfaceControl
import android.view.View
-import android.view.WindowInsets
import android.view.WindowManager
+import android.view.WindowManager.LayoutParams
import com.android.wm.shell.windowdecor.WindowManagerWrapper
/**
@@ -33,27 +33,11 @@
*/
class AdditionalSystemViewContainer(
private val windowManagerWrapper: WindowManagerWrapper,
- taskId: Int,
- x: Int,
- y: Int,
- width: Int,
- height: Int,
- flags: Int,
- @WindowInsets.Type.InsetsType forciblyShownTypes: Int = 0,
- override val view: View
+ override val view: View,
+ val lp: LayoutParams
) : AdditionalViewContainer() {
- val lp: WindowManager.LayoutParams = WindowManager.LayoutParams(
- width, height, x, y,
- WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
- flags,
- PixelFormat.TRANSPARENT
- ).apply {
- title = "Additional view container of Task=$taskId"
- gravity = Gravity.LEFT or Gravity.TOP
- setTrustedOverlay()
- this.forciblyShownTypes = forciblyShownTypes
- }
+ /** Provide a layout id of a view to inflate for this view container. */
constructor(
context: Context,
windowManagerWrapper: WindowManagerWrapper,
@@ -66,15 +50,30 @@
@LayoutRes layoutId: Int
) : this(
windowManagerWrapper = windowManagerWrapper,
- taskId = taskId,
- x = x,
- y = y,
- width = width,
- height = height,
- flags = flags,
- view = LayoutInflater.from(context).inflate(layoutId, null /* parent */)
+ view = LayoutInflater.from(context).inflate(layoutId, null /* parent */),
+ lp = createLayoutParams(x, y, width, height, flags, taskId)
)
+ /** Provide a view directly for this view container */
+ constructor(
+ windowManagerWrapper: WindowManagerWrapper,
+ taskId: Int,
+ x: Int,
+ y: Int,
+ width: Int,
+ height: Int,
+ flags: Int,
+ view: View,
+ forciblyShownTypes: Int = 0
+ ) : this(
+ windowManagerWrapper = windowManagerWrapper,
+ view = view,
+ lp = createLayoutParams(x, y, width, height, flags, taskId).apply {
+ this.forciblyShownTypes = forciblyShownTypes
+ }
+ )
+
+ /** Do not supply a view at all, instead creating the view container with a basic view. */
constructor(
context: Context,
windowManagerWrapper: WindowManagerWrapper,
@@ -86,12 +85,7 @@
flags: Int
) : this(
windowManagerWrapper = windowManagerWrapper,
- taskId = taskId,
- x = x,
- y = y,
- width = width,
- height = height,
- flags = flags,
+ lp = createLayoutParams(x, y, width, height, flags, taskId),
view = View(context)
)
@@ -104,7 +98,7 @@
}
override fun setPosition(t: SurfaceControl.Transaction, x: Float, y: Float) {
- val lp = (view.layoutParams as WindowManager.LayoutParams).apply {
+ lp.apply {
this.x = x.toInt()
this.y = y.toInt()
}
@@ -124,13 +118,29 @@
): AdditionalSystemViewContainer =
AdditionalSystemViewContainer(
windowManagerWrapper = windowManagerWrapper,
- taskId = taskId,
- x = x,
- y = y,
- width = width,
- height = height,
- flags = flags,
- view = view
+ view = view,
+ lp = createLayoutParams(x, y, width, height, flags, taskId)
)
}
+ companion object {
+ fun createLayoutParams(
+ x: Int,
+ y: Int,
+ width: Int,
+ height: Int,
+ flags: Int,
+ taskId: Int
+ ): LayoutParams {
+ return LayoutParams(
+ width, height, x, y,
+ LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
+ flags,
+ PixelFormat.TRANSPARENT
+ ).apply {
+ title = "Additional view container of Task=$taskId"
+ gravity = Gravity.LEFT or Gravity.TOP
+ setTrustedOverlay()
+ }
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
index b5700ff..b43a983 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
@@ -36,13 +36,10 @@
import android.widget.ImageButton
import androidx.core.view.ViewCompat
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat
-import com.android.internal.policy.SystemBarUtils
-import com.android.window.flags.Flags
import com.android.wm.shell.R
import com.android.wm.shell.shared.animation.Interpolators
import com.android.wm.shell.windowdecor.WindowManagerWrapper
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
-import com.android.wm.shell.windowdecor.viewholder.WindowDecorationViewHolder.Data
/**
* A desktop mode window decoration used when the window is in full "focus" (i.e. fullscreen/split).
@@ -69,10 +66,12 @@
) : Data()
private lateinit var taskInfo: RunningTaskInfo
+ private val position: Point = Point()
+ private var width: Int = 0
+ private var height: Int = 0
private val captionView: View = rootView.requireViewById(R.id.desktop_mode_caption)
private val captionHandle: ImageButton = rootView.requireViewById(R.id.caption_handle)
private val inputManager = context.getSystemService(InputManager::class.java)
- private var statusBarInputLayerExists = false
// An invisible View that takes up the same coordinates as captionHandle but is layered
// above the status bar. The purpose of this View is to receive input intended for
@@ -112,21 +111,54 @@
) {
captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo))
this.taskInfo = taskInfo
- // If handle is not in status bar region(i.e., bottom stage in vertical split),
- // do not create an input layer
- if (position.y >= SystemBarUtils.getStatusBarHeight(context)) return
- if (!isCaptionVisible && statusBarInputLayerExists) {
- disposeStatusBarInputLayer()
+ this.position.set(position)
+ this.width = width
+ this.height = height
+ if (!isCaptionVisible && statusBarInputLayer != null) {
+ detachStatusBarInputLayer()
return
}
- // Input layer view creation / modification takes a significant amount of time;
+ }
+
+ fun bindStatusBarInputLayer(
+ statusBarLayer: AdditionalSystemViewContainer
+ ) {
+ // Input layer view modification takes a significant amount of time;
// post them so we don't hold up DesktopModeWindowDecoration#relayout.
- if (statusBarInputLayerExists) {
+ if (statusBarLayer == statusBarInputLayer) {
handler.post { updateStatusBarInputLayer(position) }
- } else {
- // Input layer is created on a delay; prevent multiple from being created.
- statusBarInputLayerExists = true
- handler.post { createStatusBarInputLayer(position, width, height) }
+ return
+ }
+ // Remove the old input layer when changing to a new one.
+ if (statusBarInputLayer != null) detachStatusBarInputLayer()
+ if (statusBarLayer.view.visibility == View.INVISIBLE) {
+ statusBarLayer.view.visibility = View.VISIBLE
+ }
+ statusBarInputLayer = statusBarLayer
+ statusBarInputLayer?.let {
+ inputLayer -> setupAppHandleA11y(inputLayer.view)
+ }
+ handler.post {
+ val view = statusBarInputLayer?.view
+ ?: error("Unable to find statusBarInputLayer View")
+ // Caption handle is located within the status bar region, meaning the
+ // DisplayPolicy will attempt to transfer this input to status bar if it's
+ // a swipe down. Pilfer here to keep the gesture in handle alone.
+ view.setOnTouchListener { v, event ->
+ if (event.actionMasked == ACTION_DOWN) {
+ inputManager.pilferPointers(v.viewRootImpl.inputToken)
+ }
+ captionHandle.dispatchTouchEvent(event)
+ return@setOnTouchListener true
+ }
+ view.setOnHoverListener { _, event ->
+ captionHandle.onHoverEvent(event)
+ }
+ val lp = statusBarInputLayer?.view?.layoutParams as WindowManager.LayoutParams
+ lp.x = position.x
+ lp.y = position.y
+ lp.width = width
+ lp.height = height
}
}
@@ -138,40 +170,6 @@
animateCaptionHandleAlpha(startValue = 0f, endValue = 1f)
}
- private fun createStatusBarInputLayer(handlePosition: Point,
- handleWidth: Int,
- handleHeight: Int) {
- if (!Flags.enableHandleInputFix()) return
- statusBarInputLayer = AdditionalSystemViewContainer(context, windowManagerWrapper,
- taskInfo.taskId, handlePosition.x, handlePosition.y, handleWidth, handleHeight,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- )
- val view = statusBarInputLayer?.view ?: error("Unable to find statusBarInputLayer View")
- val lp = statusBarInputLayer?.lp ?: error("Unable to find statusBarInputLayer " +
- "LayoutParams")
- lp.title = "Handle Input Layer of task " + taskInfo.taskId
- lp.setTrustedOverlay()
- // Make this window a spy window to enable it to pilfer pointers from the system-wide
- // gesture listener that receives events before window. This is to prevent notification
- // shade gesture when we swipe down to enter desktop.
- lp.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY
- view.setOnHoverListener { _, event ->
- captionHandle.onHoverEvent(event)
- }
- // Caption handle is located within the status bar region, meaning the
- // DisplayPolicy will attempt to transfer this input to status bar if it's
- // a swipe down. Pilfer here to keep the gesture in handle alone.
- view.setOnTouchListener { v, event ->
- if (event.actionMasked == ACTION_DOWN) {
- inputManager.pilferPointers(v.viewRootImpl.inputToken)
- }
- captionHandle.dispatchTouchEvent(event)
- return@setOnTouchListener true
- }
- setupAppHandleA11y(view)
- windowManagerWrapper.updateViewLayout(view, lp)
- }
-
private fun setupAppHandleA11y(view: View) {
view.accessibilityDelegate = object : View.AccessibilityDelegate() {
override fun onInitializeAccessibilityNodeInfo(
@@ -224,15 +222,12 @@
}
/**
- * Remove the input layer from [WindowManager]. Should be used when caption handle
- * is not visible.
+ * Remove the input listeners from the input layer and remove it from this view holder.
*/
- fun disposeStatusBarInputLayer() {
- statusBarInputLayerExists = false
- handler.post {
- statusBarInputLayer?.releaseView()
- statusBarInputLayer = null
- }
+ fun detachStatusBarInputLayer() {
+ statusBarInputLayer?.view?.setOnTouchListener(null)
+ statusBarInputLayer?.view?.setOnHoverListener(null)
+ statusBarInputLayer = null
}
private fun getCaptionHandleBarColor(taskInfo: RunningTaskInfo): Int {
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index 049a5a0..91be5f5 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -63,6 +63,7 @@
"com.android.window.flags.window-aconfig-java",
"platform-test-annotations",
"flag-junit",
+ "platform-parametric-runner-lib",
],
libs: [
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index f01ed84..841b6ce 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -45,6 +45,8 @@
import android.app.TaskInfo;
import android.content.LocusId;
import android.content.pm.ParceledListSlice;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -589,9 +591,9 @@
@Test
public void testRecentTasks_visibilityChanges_shouldNotifyTaskController() {
- RunningTaskInfo task1 = createTaskInfo(/* taskId= */ 1, WINDOWING_MODE_FREEFORM);
+ RunningTaskInfo task1 = createFreeformTaskInfo(/* taskId= */ 1);
mOrganizer.onTaskAppeared(task1, /* leash= */ null);
- RunningTaskInfo task2 = createTaskInfo(/* taskId= */ 1, WINDOWING_MODE_FREEFORM);
+ RunningTaskInfo task2 = createFreeformTaskInfo(/* taskId= */ 1);
task2.isVisible = false;
mOrganizer.onTaskInfoChanged(task2);
@@ -600,6 +602,30 @@
}
@Test
+ public void testRecentTasks_sizeChanges_shouldNotifyTaskController() {
+ RunningTaskInfo task1 = createFreeformTaskInfo(/* taskId= */ 1);
+ mOrganizer.onTaskAppeared(task1, /* leash= */ null);
+ RunningTaskInfo task2 = createFreeformTaskInfo(/* taskId= */ 1);
+ task2.configuration.windowConfiguration.setAppBounds(new Rect(0, 0, 300, 400));
+
+ mOrganizer.onTaskInfoChanged(task2);
+
+ verify(mRecentTasksController).onTaskRunningInfoChanged(task2);
+ }
+
+ @Test
+ public void testRecentTasks_positionChanges_shouldNotifyTaskController() {
+ RunningTaskInfo task1 = createFreeformTaskInfo(/* taskId= */ 1);
+ mOrganizer.onTaskAppeared(task1, /* leash= */ null);
+ RunningTaskInfo task2 = createFreeformTaskInfo(/* taskId= */ 1);
+ task2.positionInParent = new Point(200, 200);
+
+ mOrganizer.onTaskInfoChanged(task2);
+
+ verify(mRecentTasksController).onTaskRunningInfoChanged(task2);
+ }
+
+ @Test
public void testRecentTasks_visibilityChanges_notFreeForm_shouldNotNotifyTaskController() {
RunningTaskInfo task1_visible = createTaskInfo(/* taskId= */ 1, WINDOWING_MODE_FULLSCREEN);
mOrganizer.onTaskAppeared(task1_visible, /* leash= */ null);
@@ -649,6 +675,13 @@
return taskInfo;
}
+ private static RunningTaskInfo createFreeformTaskInfo(int taskId) {
+ RunningTaskInfo taskInfo = createTaskInfo(taskId, WINDOWING_MODE_FREEFORM);
+ taskInfo.positionInParent = new Point(100, 100);
+ taskInfo.configuration.windowConfiguration.setAppBounds(new Rect(0, 0, 200, 200));
+ return taskInfo;
+ }
+
private void verifyOnCompatInfoChangedInvokedWith(TaskInfo taskInfo,
ShellTaskOrganizer.TaskListener listener) {
final ArgumentCaptor<CompatUIInfo> capture = ArgumentCaptor.forClass(CompatUIInfo.class);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 6b62adb..72d4dc6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -51,6 +51,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.input.InputManager;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -812,7 +813,10 @@
if (taskId != INVALID_TASK_ID) {
final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
taskInfo.taskId = taskId;
- taskInfo.token = new WindowContainerToken(mock(IWindowContainerToken.class));
+ final IWindowContainerToken mockT = mock(IWindowContainerToken.class);
+ Binder binder = new Binder();
+ doReturn(binder).when(mockT).asBinder();
+ taskInfo.token = new WindowContainerToken(mockT);
change = new TransitionInfo.Change(
taskInfo.token, b.build());
change.setTaskInfo(taskInfo);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
index fa878d0..4d7e47f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
@@ -329,7 +329,7 @@
wct = wct,
newFrontTaskId = setUpFreeformTask().taskId)
- assertThat(minimizedTaskId).isEqualTo(tasks.first())
+ assertThat(minimizedTaskId).isEqualTo(tasks.first().taskId)
assertThat(wct.hierarchyOps.size).isEqualTo(1)
assertThat(wct.hierarchyOps[0].type).isEqualTo(HIERARCHY_OP_TYPE_REORDER)
assertThat(wct.hierarchyOps[0].toTop).isFalse() // Reorder to bottom
@@ -355,7 +355,7 @@
fun getTaskToMinimize_tasksWithinLimit_returnsNull() {
val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
- val minimizedTask = desktopTasksLimiter.getTaskToMinimize(
+ val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize(
visibleOrderedTasks = tasks.map { it.taskId })
assertThat(minimizedTask).isNull()
@@ -365,11 +365,11 @@
fun getTaskToMinimize_tasksAboveLimit_returnsBackTask() {
val tasks = (1..MAX_TASK_LIMIT + 1).map { setUpFreeformTask() }
- val minimizedTask = desktopTasksLimiter.getTaskToMinimize(
+ val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize(
visibleOrderedTasks = tasks.map { it.taskId })
// first == front, last == back
- assertThat(minimizedTask).isEqualTo(tasks.last())
+ assertThat(minimizedTask).isEqualTo(tasks.last().taskId)
}
@Test
@@ -379,23 +379,23 @@
interactionJankMonitor, mContext, handler)
val tasks = (1..MAX_TASK_LIMIT2 + 1).map { setUpFreeformTask() }
- val minimizedTask = desktopTasksLimiter.getTaskToMinimize(
+ val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize(
visibleOrderedTasks = tasks.map { it.taskId })
// first == front, last == back
- assertThat(minimizedTask).isEqualTo(tasks.last())
+ assertThat(minimizedTask).isEqualTo(tasks.last().taskId)
}
@Test
fun getTaskToMinimize_withNewTask_tasksAboveLimit_returnsBackTask() {
val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
- val minimizedTask = desktopTasksLimiter.getTaskToMinimize(
+ val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize(
visibleOrderedTasks = tasks.map { it.taskId },
newTaskIdInFront = setUpFreeformTask().taskId)
// first == front, last == back
- assertThat(minimizedTask).isEqualTo(tasks.last())
+ assertThat(minimizedTask).isEqualTo(tasks.last().taskId)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
index 66f8c0b..880ca2c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -124,7 +124,7 @@
mPipResizeGestureHandler = new PipResizeGestureHandler(mContext, pipBoundsAlgorithm,
mPipBoundsState, motionHelper, mPipTouchState, mPipTaskOrganizer,
mPipDismissTargetHandler,
- () -> {}, mPipUiEventLogger, mPhonePipMenuController,
+ (Rect bounds) -> new Rect(), () -> {}, mPipUiEventLogger, mPhonePipMenuController,
mMainExecutor, null /* pipPerfHintController */) {
@Override
public void pilferPointers() {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
index d3e40f2..60e2030 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
@@ -33,7 +33,8 @@
import android.app.ActivityManager;
import android.graphics.Rect;
import android.os.IBinder;
-import android.testing.AndroidTestingRunner;
+import android.platform.test.flag.junit.FlagsParameterization;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.TestableLooper;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
@@ -42,10 +43,12 @@
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -54,11 +57,23 @@
import java.util.ArrayList;
import java.util.List;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class TaskViewTransitionsTest extends ShellTestCase {
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return FlagsParameterization.allCombinationsOf(
+ Flags.FLAG_ENABLE_TASK_VIEW_CONTROLLER_CLEANUP);
+ }
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule;
+
@Mock
Transitions mTransitions;
@Mock
@@ -70,6 +85,10 @@
TaskViewTransitions mTaskViewTransitions;
+ public TaskViewTransitionsTest(FlagsParameterization flags) {
+ mSetFlagsRule = new SetFlagsRule(flags);
+ }
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 1839b8a..c5526fc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -127,6 +127,7 @@
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
+import org.mockito.kotlin.KArgumentCaptor
import org.mockito.kotlin.verify
import org.mockito.kotlin.any
import org.mockito.kotlin.argThat
@@ -937,7 +938,7 @@
}
@Test
- fun testDecor_onClickToSplitScreen_disposesStatusBarInputLayer() {
+ fun testDecor_onClickToSplitScreen_detachesStatusBarInputLayer() {
val toSplitScreenListenerCaptor = forClass(Function0::class.java)
as ArgumentCaptor<Function0<Unit>>
val decor = createOpenTaskDecoration(
@@ -947,7 +948,7 @@
toSplitScreenListenerCaptor.value.invoke()
- verify(decor).disposeStatusBarInputLayer()
+ verify(decor).detachStatusBarInputLayer()
}
@Test
@@ -1278,12 +1279,45 @@
.toggleDesktopTaskSize(decor.mTaskInfo, ResizeTrigger.MAXIMIZE_BUTTON, null)
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ fun testImmersiveClick_togglesImmersiveState() {
+ val onImmersiveClickCaptor = argumentCaptor<() -> Unit>()
+ val decor = createOpenTaskDecoration(
+ windowingMode = WINDOWING_MODE_FREEFORM,
+ onImmersiveOrRestoreListenerCaptor = onImmersiveClickCaptor,
+ requestingImmersive = true,
+ )
+
+ onImmersiveClickCaptor.firstValue()
+
+ verify(mockDesktopTasksController)
+ .toggleDesktopTaskFullImmersiveState(decor.mTaskInfo)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ fun testImmersiveClick_closesMaximizeMenu() {
+ val onImmersiveClickCaptor = argumentCaptor<() -> Unit>()
+ val decor = createOpenTaskDecoration(
+ windowingMode = WINDOWING_MODE_FREEFORM,
+ onImmersiveOrRestoreListenerCaptor = onImmersiveClickCaptor,
+ requestingImmersive = true,
+ )
+
+ onImmersiveClickCaptor.firstValue()
+
+ verify(decor).closeMaximizeMenu()
+ }
+
private fun createOpenTaskDecoration(
@WindowingMode windowingMode: Int,
taskSurface: SurfaceControl = SurfaceControl(),
requestingImmersive: Boolean = false,
onMaxOrRestoreListenerCaptor: ArgumentCaptor<Function0<Unit>> =
forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>>,
+ onImmersiveOrRestoreListenerCaptor: KArgumentCaptor<() -> Unit> =
+ argumentCaptor<() -> Unit>(),
onLeftSnapClickListenerCaptor: ArgumentCaptor<Function0<Unit>> =
forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>>,
onRightSnapClickListenerCaptor: ArgumentCaptor<Function0<Unit>> =
@@ -1307,6 +1341,8 @@
))
onTaskOpening(decor.mTaskInfo, taskSurface)
verify(decor).setOnMaximizeOrRestoreClickListener(onMaxOrRestoreListenerCaptor.capture())
+ verify(decor)
+ .setOnImmersiveOrRestoreClickListener(onImmersiveOrRestoreListenerCaptor.capture())
verify(decor).setOnLeftSnapClickListener(onLeftSnapClickListenerCaptor.capture())
verify(decor).setOnRightSnapClickListener(onRightSnapClickListenerCaptor.capture())
verify(decor).setOnToDesktopClickListener(onToDesktopClickListenerCaptor.capture())
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 0afb6c1..8a2c778 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -49,7 +49,6 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static org.mockito.kotlin.VerificationKt.times;
import android.app.ActivityManager;
import android.app.assist.AssistContent;
@@ -850,8 +849,7 @@
ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
- // Once for view host, the other for the AppHandle input layer.
- verify(mMockHandler, times(2)).post(runnableArgument.capture());
+ verify(mMockHandler).post(runnableArgument.capture());
runnableArgument.getValue().run();
verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any());
}
@@ -878,8 +876,7 @@
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
- // Once for view host, the other for the AppHandle input layer.
- verify(mMockHandler, times(2)).post(runnableArgument.capture());
+ verify(mMockHandler).post(runnableArgument.capture());
spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
@@ -893,8 +890,7 @@
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
- // Once for view host, the other for the AppHandle input layer.
- verify(mMockHandler, times(2)).post(runnableArgument.capture());
+ verify(mMockHandler).post(runnableArgument.capture());
spyWindowDecor.close();
@@ -909,8 +905,10 @@
new FakeMaximizeMenuFactory(menu));
assertFalse(decoration.isMaximizeMenuActive());
- createMaximizeMenu(decoration, menu);
+ createMaximizeMenu(decoration);
+ verify(menu).show(anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), any(), any(), any(),
+ any(), mOnMaxMenuHoverChangeListener.capture(), any());
assertTrue(decoration.isMaximizeMenuActive());
}
@@ -921,7 +919,9 @@
final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
new FakeMaximizeMenuFactory(menu));
decoration.setAppHeaderMaximizeButtonHovered(false);
- createMaximizeMenu(decoration, menu);
+ createMaximizeMenu(decoration);
+ verify(menu).show(anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), any(), any(), any(),
+ any(), mOnMaxMenuHoverChangeListener.capture(), any());
mOnMaxMenuHoverChangeListener.getValue().invoke(false);
@@ -940,7 +940,7 @@
final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
new FakeMaximizeMenuFactory(menu));
decoration.setAppHeaderMaximizeButtonHovered(true);
- createMaximizeMenu(decoration, menu);
+ createMaximizeMenu(decoration);
decoration.setAppHeaderMaximizeButtonHovered(false);
@@ -958,7 +958,9 @@
final MaximizeMenu menu = mock(MaximizeMenu.class);
final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
new FakeMaximizeMenuFactory(menu));
- createMaximizeMenu(decoration, menu);
+ createMaximizeMenu(decoration);
+ verify(menu).show(anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), any(), any(), any(),
+ any(), mOnMaxMenuHoverChangeListener.capture(), any());
mOnMaxMenuHoverChangeListener.getValue().invoke(true);
@@ -971,13 +973,114 @@
final MaximizeMenu menu = mock(MaximizeMenu.class);
final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
new FakeMaximizeMenuFactory(menu));
- createMaximizeMenu(decoration, menu);
+ createMaximizeMenu(decoration);
+ verify(menu).show(anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), any(), any(), any(),
+ any(), mOnMaxMenuHoverChangeListener.capture(), any());
decoration.setAppHeaderMaximizeButtonHovered(true);
verify(mMockHandler).removeCallbacks(any());
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ public void createMaximizeMenu_taskRequestsImmersive_showsImmersiveOption() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
+ taskInfo.requestedVisibleTypes = ~statusBars();
+ final MaximizeMenu menu = mock(MaximizeMenu.class);
+ final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
+ new FakeMaximizeMenuFactory(menu));
+
+ createMaximizeMenu(decoration);
+
+ verify(menu).show(
+ anyBoolean(),
+ anyInt(),
+ /* showImmersiveOption= */ eq(true),
+ anyBoolean(),
+ any(),
+ any(),
+ any(),
+ any(),
+ any(),
+ any()
+ );
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ public void createMaximizeMenu_taskDoesNotRequestImmersive_hiddenImmersiveOption() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
+ taskInfo.requestedVisibleTypes = statusBars();
+ final MaximizeMenu menu = mock(MaximizeMenu.class);
+ final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
+ new FakeMaximizeMenuFactory(menu));
+
+ createMaximizeMenu(decoration);
+
+ verify(menu).show(
+ anyBoolean(),
+ anyInt(),
+ /* showImmersiveOption= */ eq(false),
+ anyBoolean(),
+ any(),
+ any(),
+ any(),
+ any(),
+ any(),
+ any()
+ );
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ public void createMaximizeMenu_taskResizable_showsSnapOptions() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
+ taskInfo.isResizeable = true;
+ final MaximizeMenu menu = mock(MaximizeMenu.class);
+ final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
+ new FakeMaximizeMenuFactory(menu));
+
+ createMaximizeMenu(decoration);
+
+ verify(menu).show(
+ anyBoolean(),
+ anyInt(),
+ anyBoolean(),
+ /* showSnapOptions= */ eq(true),
+ any(),
+ any(),
+ any(),
+ any(),
+ any(),
+ any()
+ );
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ public void createMaximizeMenu_taskUnresizable_hiddenSnapOptions() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
+ taskInfo.isResizeable = false;
+ final MaximizeMenu menu = mock(MaximizeMenu.class);
+ final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
+ new FakeMaximizeMenuFactory(menu));
+
+ createMaximizeMenu(decoration);
+
+ verify(menu).show(
+ anyBoolean(),
+ anyInt(),
+ anyBoolean(),
+ /* showSnapOptions= */ eq(false),
+ any(),
+ any(),
+ any(),
+ any(),
+ any(),
+ any()
+ );
+ }
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
@@ -1335,13 +1438,13 @@
anyInt(), anyInt(), anyInt(), anyInt());
}
- private void createMaximizeMenu(DesktopModeWindowDecoration decoration, MaximizeMenu menu) {
+ private void createMaximizeMenu(DesktopModeWindowDecoration decoration) {
final Function0<Unit> l = () -> Unit.INSTANCE;
decoration.setOnMaximizeOrRestoreClickListener(l);
+ decoration.setOnImmersiveOrRestoreClickListener(l);
decoration.setOnLeftSnapClickListener(l);
decoration.setOnRightSnapClickListener(l);
decoration.createMaximizeMenu();
- verify(menu).show(any(), any(), any(), mOnMaxMenuHoverChangeListener.capture(), any());
}
private void fillRoundedCornersResources(int fillValue) {
diff --git a/lint-baseline.xml b/lint-baseline.xml
index 0320aab..8253c1f 100644
--- a/lint-baseline.xml
+++ b/lint-baseline.xml
@@ -11265,4 +11265,15 @@
column="24"/>
</issue>
-</issues>
\ No newline at end of file
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="INetworkScoreCache permission check should be converted to @EnforcePermission annotation"
+ errorLine1=" mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/wifi/java/src/android/net/wifi/WifiNetworkScoreCache.java"
+ line="250"
+ column="9"/>
+ </issue>
+
+</issues>
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 0efefb9..39b29d0 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -279,6 +279,7 @@
TYPE_AUX_LINE,
TYPE_IP,
TYPE_BUS,
+ TYPE_REMOTE_SUBMIX,
TYPE_HEARING_AID,
TYPE_BUILTIN_SPEAKER_SAFE,
TYPE_BLE_HEADSET,
@@ -312,6 +313,7 @@
case TYPE_AUX_LINE:
case TYPE_IP:
case TYPE_BUS:
+ case TYPE_REMOTE_SUBMIX:
case TYPE_HEARING_AID:
case TYPE_BUILTIN_SPEAKER_SAFE:
case TYPE_BLE_HEADSET:
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 536afd4..4627be3 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -896,6 +896,30 @@
*/
public static final int USE_DEFAULT_STREAM_TYPE = Integer.MIN_VALUE;
+ /** @hide */
+ @IntDef(flag = false, prefix = "DEVICE_STATE", value = {
+ DEVICE_CONNECTION_STATE_DISCONNECTED,
+ DEVICE_CONNECTION_STATE_CONNECTED}
+ )
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DeviceConnectionState {}
+
+ /**
+ * @hide The device connection state for disconnected devices.
+ */
+ @SystemApi
+ @SuppressLint("UnflaggedApi") // b/373465238
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public static final int DEVICE_CONNECTION_STATE_DISCONNECTED = 0;
+
+ /**
+ * @hide The device connection state for connected devices.
+ */
+ @SystemApi
+ @SuppressLint("UnflaggedApi") // b/373465238
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public static final int DEVICE_CONNECTION_STATE_CONNECTED = 1;
+
private static IAudioService sService;
/**
@@ -6726,14 +6750,16 @@
}
/**
+ * @hide
* Indicate wired accessory connection state change and attributes.
- * @param state new connection state: 1 connected, 0 disconnected
* @param attributes attributes of the connected device
- * {@hide}
+ * @param state new connection state
*/
- @UnsupportedAppUsage
+ @SystemApi
+ @SuppressLint("UnflaggedApi") // b/373465238
@RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
- public void setWiredDeviceConnectionState(AudioDeviceAttributes attributes, int state) {
+ public void setWiredDeviceConnectionState(@NonNull AudioDeviceAttributes attributes,
+ @DeviceConnectionState int state) {
final IAudioService service = getService();
try {
service.setWiredDeviceConnectionState(attributes, state,
diff --git a/media/java/android/media/audio/common/AidlConversion.java b/media/java/android/media/audio/common/AidlConversion.java
index 65fd51b..c1d73f9 100644
--- a/media/java/android/media/audio/common/AidlConversion.java
+++ b/media/java/android/media/audio/common/AidlConversion.java
@@ -753,9 +753,11 @@
break;
case AudioSystem.DEVICE_IN_REMOTE_SUBMIX:
aidl.type = AudioDeviceType.IN_SUBMIX;
+ aidl.connection = AudioDeviceDescription.CONNECTION_VIRTUAL;
break;
case AudioSystem.DEVICE_OUT_REMOTE_SUBMIX:
aidl.type = AudioDeviceType.OUT_SUBMIX;
+ aidl.connection = AudioDeviceDescription.CONNECTION_VIRTUAL;
break;
case AudioSystem.DEVICE_IN_ANLG_DOCK_HEADSET:
aidl.type = AudioDeviceType.IN_DOCK;
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 019b1e0..effa92c 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -942,6 +942,11 @@
return static_cast<jint>(PublicFormat::PRIVATE);
} else {
BufferItem* buffer = Image_getBufferItem(env, thiz);
+ if (buffer == nullptr) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Image is not initialized");
+ return -1;
+ }
int readerHalFormat = mapPublicFormatToHalFormat(static_cast<PublicFormat>(readerFormat));
int32_t fmt = applyFormatOverrides(
buffer->mGraphicBuffer->getPixelFormat(), readerHalFormat);
@@ -960,6 +965,11 @@
static jobject Image_getHardwareBuffer(JNIEnv* env, jobject thiz) {
BufferItem* buffer = Image_getBufferItem(env, thiz);
+ if (buffer == nullptr) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Image is not initialized");
+ return NULL;
+ }
AHardwareBuffer* b = AHardwareBuffer_from_GraphicBuffer(buffer->mGraphicBuffer.get());
// don't user the public AHardwareBuffer_toHardwareBuffer() because this would force us
// to link against libandroid.so
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
index c16366e..edd49c5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
@@ -296,6 +296,7 @@
val listener =
object : IDeviceSettingsListener.Stub() {
override fun onDeviceSettingsChanged(settings: List<DeviceSetting>) {
+ Log.i(TAG, "Receive setting ids ${settings.map { it.settingId }}")
launch { send(settings) }
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
index 121f549..3696000 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
@@ -41,6 +41,7 @@
"/system_ext/etc/NOTICE.xml.gz",
"/vendor_dlkm/etc/NOTICE.xml.gz",
"/odm_dlkm/etc/NOTICE.xml.gz",
+ "/system_dlkm/etc/NOTICE.xml.gz",
};
static final String NOTICE_HTML_FILE_NAME = "NOTICE.html";
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index d3ee400..064198f 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -133,6 +133,7 @@
Settings.Secure.FINGERPRINT_SIDE_FPS_ENROLL_TAP_WINDOW,
Settings.Secure.FINGERPRINT_SIDE_FPS_AUTH_DOWNTIME,
Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED,
+ Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED,
Settings.Secure.ACTIVE_UNLOCK_ON_WAKE,
Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT,
Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index d34ccc5..c002a04 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -190,6 +190,7 @@
NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.FINGERPRINT_SIDE_FPS_AUTH_DOWNTIME, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.SFPS_PERFORMANT_AUTH_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.SHOW_MEDIA_WHEN_BYPASSING, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.FACE_UNLOCK_APP_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, BOOLEAN_VALIDATOR);
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 9265ceb..8ddd922 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -493,6 +493,7 @@
},
},
use_resource_processor: true,
+ resource_dirs: [],
static_libs: [
"//frameworks/libs/systemui:compilelib",
"SystemUI-res",
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 2278789..3650f68 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -33,6 +33,13 @@
}
flag {
+ name: "notifications_redesign_footer_view"
+ namespace: "systemui"
+ description: "Notifications Redesign: Update the look of the notifications footer."
+ bug: "375010573"
+}
+
+flag {
name: "notification_row_content_binder_refactor"
namespace: "systemui"
description: "Convert the NotificationContentInflater to Kotlin and restructure it to support modern views"
@@ -139,6 +146,13 @@
}
flag {
+ name: "notifications_dismiss_pruned_summaries"
+ namespace: "systemui"
+ description: "NotifCollection.dismissNotifications will now dismiss summaries that are pruned from the shade."
+ bug: "355967751"
+}
+
+flag {
name: "notification_transparent_header_fix"
namespace: "systemui"
description: "fix the transparent group header issue for async header inflation."
@@ -1104,6 +1118,13 @@
}
flag {
+ name: "communal_standalone_support"
+ namespace: "systemui"
+ description: "Support communal features without a dock"
+ bug: "352301247"
+}
+
+flag {
name: "dream_overlay_updated_font"
namespace: "systemui"
description: "Flag to enable updated font settings for dream overlay"
@@ -1546,6 +1567,16 @@
}
flag {
+ name: "transition_race_condition"
+ namespace: "systemui"
+ description: "Thread-safe keyguard transitions"
+ bug: "358533338"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "media_projection_request_attribution_fix"
namespace: "systemui"
description: "Ensure MediaProjection consent requests are properly attributed"
diff --git a/packages/SystemUI/animation/lib/Android.bp b/packages/SystemUI/animation/lib/Android.bp
index d9230ec..7d73023 100644
--- a/packages/SystemUI/animation/lib/Android.bp
+++ b/packages/SystemUI/animation/lib/Android.bp
@@ -48,6 +48,19 @@
}
filegroup {
+ name: "PlatformAnimationLib-client-srcs",
+ srcs: [
+ "src/com/android/systemui/animation/OriginRemoteTransition.java",
+ "src/com/android/systemui/animation/OriginTransitionSession.java",
+ "src/com/android/systemui/animation/SurfaceUIComponent.java",
+ "src/com/android/systemui/animation/Transactions.java",
+ "src/com/android/systemui/animation/UIComponent.java",
+ "src/com/android/systemui/animation/ViewUIComponent.java",
+ ":PlatformAnimationLib-aidl",
+ ],
+}
+
+filegroup {
name: "PlatformAnimationLib-aidl",
srcs: [
"src/**/*.aidl",
diff --git a/packages/SystemUI/animation/lib/OWNERS b/packages/SystemUI/animation/lib/OWNERS
new file mode 100644
index 0000000..7569419
--- /dev/null
+++ b/packages/SystemUI/animation/lib/OWNERS
@@ -0,0 +1,3 @@
+#inherits OWNERS from SystemUI in addition to WEAR framework owners below
+file:platform/frameworks/base:/WEAR_OWNERS
+
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
index 08db95e..2b5ff7c 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
@@ -44,6 +44,7 @@
/**
* An implementation of {@link IRemoteTransition} that accepts a {@link UIComponent} as the origin
* and automatically attaches it to the transition leash before the transition starts.
+ * @hide
*/
public class OriginRemoteTransition extends IRemoteTransition.Stub {
private static final String TAG = "OriginRemoteTransition";
@@ -328,7 +329,9 @@
/* baseBounds= */ maxBounds);
}
- /** An interface that represents an origin transitions. */
+ /** An interface that represents an origin transitions.
+ * @hide
+ */
public interface TransitionPlayer {
/**
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java
index 23693b6..6d6aa88 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java
@@ -43,6 +43,7 @@
/**
* A session object that holds origin transition states for starting an activity from an on-screen
* UI component and smoothly transitioning back from the activity to the same UI component.
+ * @hide
*/
public class OriginTransitionSession {
private static final String TAG = "OriginTransitionSession";
@@ -179,7 +180,10 @@
}
}
- /** A builder to build a {@link OriginTransitionSession}. */
+ /**
+ * A builder to build a {@link OriginTransitionSession}.
+ * @hide
+ */
public static class Builder {
private final Context mContext;
@Nullable private final IOriginTransitions mOriginTransitions;
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/SurfaceUIComponent.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/SurfaceUIComponent.java
index 2438736..e1ff2a4 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/SurfaceUIComponent.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/SurfaceUIComponent.java
@@ -26,7 +26,10 @@
import java.util.Collection;
import java.util.concurrent.Executor;
-/** A {@link UIComponent} representing a {@link SurfaceControl}. */
+/**
+ * A {@link UIComponent} representing a {@link SurfaceControl}.
+ * @hide
+ */
public class SurfaceUIComponent implements UIComponent {
private final Collection<SurfaceControl> mSurfaces;
private final Rect mBaseBounds;
@@ -89,7 +92,10 @@
+ "}";
}
- /** A {@link Transaction} wrapping a {@link SurfaceControl.Transaction}. */
+ /**
+ * A {@link Transaction} wrapping a {@link SurfaceControl.Transaction}.
+ * @hide
+ */
public static class Transaction implements UIComponent.Transaction<SurfaceUIComponent> {
private final SurfaceControl.Transaction mTransaction;
private final ArrayList<Runnable> mChanges = new ArrayList<>();
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/Transactions.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/Transactions.java
index 5240d99..64d2172 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/Transactions.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/Transactions.java
@@ -27,6 +27,7 @@
/**
* A composite {@link UIComponent.Transaction} that combines multiple other transactions for each ui
* type.
+ * @hide
*/
public class Transactions implements UIComponent.Transaction<UIComponent> {
private final Map<Class, UIComponent.Transaction> mTransactions = new ArrayMap<>();
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/UIComponent.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/UIComponent.java
index 747e4d1..8aec2f4 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/UIComponent.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/UIComponent.java
@@ -17,12 +17,17 @@
package com.android.systemui.animation;
import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Rect;
import android.view.SurfaceControl;
import java.util.concurrent.Executor;
-/** An interface representing an UI component on the display. */
+/**
+ * An interface representing an UI component on the display.
+ * @hide
+ */
public interface UIComponent {
/** Get the current alpha of this UI. */
@@ -32,39 +37,48 @@
boolean isVisible();
/** Get the bounds of this UI in its display. */
+ @NonNull
Rect getBounds();
/** Create a new {@link Transaction} that can update this UI. */
+ @NonNull
Transaction newTransaction();
/**
* A transaction class for updating {@link UIComponent}.
*
* @param <T> the subtype of {@link UIComponent} that this {@link Transaction} can handle.
+ * @hide
*/
interface Transaction<T extends UIComponent> {
/** Update alpha of an UI. Execution will be delayed until {@link #commit()} is called. */
- Transaction setAlpha(T ui, @FloatRange(from = 0.0, to = 1.0) float alpha);
+ Transaction setAlpha(@NonNull T ui, @FloatRange(from = 0.0, to = 1.0) float alpha);
/**
* Update visibility of an UI. Execution will be delayed until {@link #commit()} is called.
*/
- Transaction setVisible(T ui, boolean visible);
+ @NonNull
+ Transaction setVisible(@NonNull T ui, boolean visible);
/** Update bounds of an UI. Execution will be delayed until {@link #commit()} is called. */
- Transaction setBounds(T ui, Rect bounds);
+ @NonNull
+ Transaction setBounds(@NonNull T ui, @NonNull Rect bounds);
/**
* Attach a ui to the transition leash. Execution will be delayed until {@link #commit()} is
* called.
*/
- Transaction attachToTransitionLeash(T ui, SurfaceControl transitionLeash, int w, int h);
+ @NonNull
+ Transaction attachToTransitionLeash(
+ @NonNull T ui, @NonNull SurfaceControl transitionLeash, int w, int h);
/**
* Detach a ui from the transition leash. Execution will be delayed until {@link #commit} is
* called.
*/
- Transaction detachFromTransitionLeash(T ui, Executor executor, Runnable onDone);
+ @NonNull
+ Transaction detachFromTransitionLeash(
+ @NonNull T ui, @NonNull Executor executor, @Nullable Runnable onDone);
/** Commit any pending changes added to this transaction. */
void commit();
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
index 313789c..4c047d5 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
@@ -38,6 +38,7 @@
* be changed to INVISIBLE in its view tree. This allows the {@link View} to transform in the
* full-screen size leash without being constrained by the view tree's boundary or inheriting its
* parent's alpha and transformation.
+ * @hide
*/
public class ViewUIComponent implements UIComponent {
private static final String TAG = "ViewUIComponent";
@@ -234,6 +235,9 @@
mView.post(this::draw);
}
+ /**
+ * @hide
+ */
public static class Transaction implements UIComponent.Transaction<ViewUIComponent> {
private final List<Runnable> mChanges = new ArrayList<>();
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/back/FlingOnBackAnimationCallback.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/FlingOnBackAnimationCallback.kt
index cd2dd04..47ad0b3 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/back/FlingOnBackAnimationCallback.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/back/FlingOnBackAnimationCallback.kt
@@ -95,16 +95,18 @@
final override fun onBackProgressed(backEvent: BackEvent) {
val interpolatedProgress = progressInterpolator.getInterpolation(backEvent.progress)
if (predictiveBackTimestampApi()) {
- velocityTracker.addMovement(
- MotionEvent.obtain(
- /* downTime */ downTime!!,
- /* eventTime */ backEvent.frameTimeMillis,
- /* action */ ACTION_MOVE,
- /* x */ interpolatedProgress * SCALE_FACTOR,
- /* y */ 0f,
- /* metaState */ 0,
+ downTime?.let { downTime ->
+ velocityTracker.addMovement(
+ MotionEvent.obtain(
+ /* downTime */ downTime,
+ /* eventTime */ backEvent.frameTimeMillis,
+ /* action */ ACTION_MOVE,
+ /* x */ interpolatedProgress * SCALE_FACTOR,
+ /* y */ 0f,
+ /* metaState */ 0,
+ )
)
- )
+ }
lastBackEvent =
BackEvent(
backEvent.touchX,
diff --git a/packages/SystemUI/common/Android.bp b/packages/SystemUI/common/Android.bp
index 6fc13d7..91dc3e3 100644
--- a/packages/SystemUI/common/Android.bp
+++ b/packages/SystemUI/common/Android.bp
@@ -22,20 +22,13 @@
default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
}
-android_library {
-
+java_library {
name: "SystemUICommon",
- use_resource_processor: true,
srcs: [
"src/**/*.java",
"src/**/*.kt",
],
- static_libs: [
- "androidx.core_core-ktx",
- ],
-
- manifest: "AndroidManifest.xml",
kotlincflags: ["-Xjvm-default=all"],
}
diff --git a/packages/SystemUI/common/AndroidManifest.xml b/packages/SystemUI/common/AndroidManifest.xml
deleted file mode 100644
index 6f757eb..0000000
--- a/packages/SystemUI/common/AndroidManifest.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2023 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.systemui.common">
-
-</manifest>
diff --git a/packages/SystemUI/common/README.md b/packages/SystemUI/common/README.md
index 1cc5277..17e5412 100644
--- a/packages/SystemUI/common/README.md
+++ b/packages/SystemUI/common/README.md
@@ -1,5 +1,9 @@
# SystemUICommon
-`SystemUICommon` is a module within SystemUI that hosts standalone helper libraries. It is intended to be used by other modules, and therefore should not have other SystemUI dependencies to avoid circular dependencies.
+`SystemUICommon` is a module within SystemUI that hosts standalone helper libraries. It is intended
+to be used by other modules, and therefore should not have other SystemUI dependencies to avoid
+circular dependencies.
-To maintain the structure of this module, please refrain from adding components at the top level. Instead, add them to specific sub-packages, such as `systemui/common/buffer/`. This will help to keep the module organized and easy to navigate.
+To maintain the structure of this module, please refrain from adding components at the top level.
+Instead, add them to specific sub-packages, such as `systemui/common/buffer/`. This will help to
+keep the module organized and easy to navigate.
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt
index e4c60e1..5cb45e5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt
@@ -18,9 +18,11 @@
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.util.fastCoerceAtLeast
import androidx.compose.ui.util.fastCoerceAtMost
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
+import com.android.compose.nestedscroll.ScrollController
/**
* A [NestedScrollConnection] that listens for all vertical scroll events and responds in the
@@ -58,33 +60,40 @@
offsetAvailable > 0 && (scrimOffset() < maxScrimOffset || isCurrentGestureOverscroll())
},
canStartPostFling = { false },
- canStopOnPreFling = { false },
- onStart = { offsetAvailable -> onStart(offsetAvailable) },
- onScroll = { offsetAvailable, _ ->
- val currentHeight = scrimOffset()
- val amountConsumed =
- if (offsetAvailable > 0) {
- val amountLeft = maxScrimOffset - currentHeight
- offsetAvailable.fastCoerceAtMost(amountLeft)
- } else {
- val amountLeft = minScrimOffset() - currentHeight
- offsetAvailable.fastCoerceAtLeast(amountLeft)
+ onStart = { firstScroll ->
+ onStart(firstScroll)
+ object : ScrollController {
+ override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float {
+ val currentHeight = scrimOffset()
+ val amountConsumed =
+ if (deltaScroll > 0) {
+ val amountLeft = maxScrimOffset - currentHeight
+ deltaScroll.fastCoerceAtMost(amountLeft)
+ } else {
+ val amountLeft = minScrimOffset() - currentHeight
+ deltaScroll.fastCoerceAtLeast(amountLeft)
+ }
+ snapScrimOffset(currentHeight + amountConsumed)
+ return amountConsumed
}
- snapScrimOffset(currentHeight + amountConsumed)
- amountConsumed
- },
- onStop = { velocityAvailable ->
- onStop(velocityAvailable)
- if (scrimOffset() < minScrimOffset()) {
- animateScrimOffset(minScrimOffset())
- }
- // Don't consume the velocity on pre/post fling
- 0f
- },
- onCancel = {
- onStop(0f)
- if (scrimOffset() < minScrimOffset()) {
- animateScrimOffset(minScrimOffset())
+
+ override suspend fun onStop(initialVelocity: Float): Float {
+ onStop(initialVelocity)
+ if (scrimOffset() < minScrimOffset()) {
+ animateScrimOffset(minScrimOffset())
+ }
+ // Don't consume the velocity on pre/post fling
+ return 0f
+ }
+
+ override fun onCancel() {
+ onStop(0f)
+ if (scrimOffset() < minScrimOffset()) {
+ animateScrimOffset(minScrimOffset())
+ }
+ }
+
+ override fun canStopOnPreFling() = false
}
},
)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
index edb05eb..e1b74a9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
@@ -23,6 +23,7 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
@@ -30,6 +31,7 @@
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastCoerceAtLeast
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
+import com.android.compose.nestedscroll.ScrollController
import kotlin.math.max
import kotlin.math.roundToInt
import kotlin.math.tanh
@@ -92,20 +94,29 @@
offsetAvailable < 0f && offsetBeforeStart < 0f && !canScrollForward()
},
canStartPostFling = { velocityAvailable -> velocityAvailable < 0f && !canScrollForward() },
- canStopOnPreFling = { false },
- onStart = { offsetAvailable -> onStart(offsetAvailable) },
- onScroll = { offsetAvailable, _ ->
- val minOffset = 0f
- val consumed = offsetAvailable.fastCoerceAtLeast(minOffset - stackOffset())
- if (consumed != 0f) {
- onScroll(consumed)
+ onStart = { firstScroll ->
+ onStart(firstScroll)
+ object : ScrollController {
+ override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float {
+ val minOffset = 0f
+ val consumed = deltaScroll.fastCoerceAtLeast(minOffset - stackOffset())
+ if (consumed != 0f) {
+ onScroll(consumed)
+ }
+ return consumed
+ }
+
+ override suspend fun onStop(initialVelocity: Float): Float {
+ onStop(initialVelocity)
+ return initialVelocity
+ }
+
+ override fun onCancel() {
+ onStop(0f)
+ }
+
+ override fun canStopOnPreFling() = false
}
- consumed
},
- onStop = { velocityAvailable ->
- onStop(velocityAvailable)
- velocityAvailable
- },
- onCancel = { onStop(0f) },
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index 834a7f5..4fa1984 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -375,9 +375,13 @@
LaunchedEffect(shadeScrollState) { viewModel.setScrollState(shadeScrollState) }
// if contentHeight drops below minimum visible scrim height while scrim is
- // expanded, reset scrim offset.
- LaunchedEffect(stackHeight, scrimOffset) {
- snapshotFlow { stackHeight.intValue < minVisibleScrimHeight() && scrimOffset.value < 0f }
+ // expanded and IME is not showing, reset scrim offset.
+ LaunchedEffect(stackHeight, scrimOffset, imeTop) {
+ snapshotFlow {
+ stackHeight.intValue < minVisibleScrimHeight() &&
+ scrimOffset.value < 0f &&
+ imeTop.floatValue <= 0f
+ }
.collect { shouldCollapse -> if (shouldCollapse) scrimOffset.animateTo(0f, tween()) }
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
index 58801e0..e725ce5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
@@ -128,10 +128,10 @@
QSSceneAdapter.State.QS
}
else ->
- error(
- "Bad transition for QuickSettings: fromContent=$fromContent," +
- " toScene=$toContent"
- )
+ // We are not in a transition between states that have QS, so just make
+ // sure it's closed. This could be an issue if going from SplitShade to
+ // a folded device.
+ QSSceneAdapter.State.CLOSED
}
}
is TransitionState.Transition.OverlayTransition ->
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index 491221f..bba3d69 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -516,13 +516,14 @@
.weight(1f)
.graphicsLayer { translationX = unfoldTranslationXForStartSide }
) {
- BrightnessMirror(
- viewModel = brightnessMirrorViewModel,
- qsSceneAdapter = viewModel.qsSceneAdapter,
- // Need to use the offset measured from the container as the header
- // has to be accounted for
- measureFromContainer = true,
- )
+ Box(modifier = Modifier.fillMaxSize()) {
+ BrightnessMirror(
+ viewModel = brightnessMirrorViewModel,
+ qsSceneAdapter = viewModel.qsSceneAdapter,
+ modifier = Modifier.align(Alignment.TopCenter),
+ measureFromContainer = true,
+ )
+ }
Column(
verticalArrangement = Arrangement.Top,
modifier = Modifier.fillMaxSize().padding(bottom = bottomPadding),
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index 8469007..7c7202a 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -20,6 +20,7 @@
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
import androidx.compose.ui.util.fastCoerceIn
@@ -27,6 +28,7 @@
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
+import com.android.compose.nestedscroll.ScrollController
import kotlin.math.absoluteValue
internal typealias SuspendedValue<T> = suspend () -> T
@@ -66,6 +68,7 @@
internal val orientation: Orientation,
) : DraggableHandler {
internal val nestedScrollKey = Any()
+
/** The [DraggableHandler] can only have one active [DragController] at a time. */
private var dragController: DragControllerImpl? = null
@@ -345,6 +348,7 @@
distance == DistanceUnspecified ||
swipeAnimation.contentTransition.isWithinProgressRange(desiredProgress) ->
desiredOffset
+
distance > 0f -> desiredOffset.fastCoerceIn(0f, distance)
else -> desiredOffset.fastCoerceIn(distance, 0f)
}
@@ -545,6 +549,7 @@
upOrLeftResult == null && downOrRightResult == null -> null
(directionOffset < 0f && upOrLeftResult != null) || downOrRightResult == null ->
upOrLeftResult
+
else -> downOrRightResult
}
}
@@ -608,7 +613,6 @@
return overscrollSpec != null
}
- var dragController: DragController? = null
var isIntercepting = false
return PriorityNestedScrollConnection(
@@ -669,10 +673,12 @@
canChangeScene = isZeroOffset
isZeroOffset && hasNextScene(offsetAvailable)
}
+
NestedScrollBehavior.EdgeWithPreview -> {
canChangeScene = isZeroOffset
hasNextScene(offsetAvailable)
}
+
NestedScrollBehavior.EdgeAlways -> {
canChangeScene = true
hasNextScene(offsetAvailable)
@@ -710,56 +716,59 @@
canStart
},
- // We need to maintain scroll priority even if the scene transition can no longer
- // consume the scroll gesture to allow us to return to the previous scene.
- canStopOnScroll = { _, _ -> false },
- canStopOnPreFling = { true },
- onStart = { offsetAvailable ->
+ onStart = { firstScroll ->
val pointersInfo = pointersInfo()
- dragController =
- draggableHandler.onDragStarted(
- pointersDown = pointersInfo.pointersDown,
- startedPosition = pointersInfo.startedPosition,
- overSlop = if (isIntercepting) 0f else offsetAvailable,
- )
- },
- onScroll = { offsetAvailable, _ ->
- val controller = dragController ?: error("Should be called after onStart")
-
- val pointersInfo = pointersInfoOwner.pointersInfo()
- if (pointersInfo.isMouseWheel) {
- // Do not support mouse wheel interactions
- return@PriorityNestedScrollConnection 0f
- }
-
- // TODO(b/297842071) We should handle the overscroll or slow drag if the gesture is
- // initiated in a nested child.
- controller.onDrag(delta = offsetAvailable)
- },
- onStop = { velocityAvailable ->
- val controller = dragController ?: error("Should be called after onStart")
- try {
- controller
- .onStop(velocity = velocityAvailable, canChangeContent = canChangeScene)
- .invoke()
- } finally {
- // onStop might still be running when a new gesture begins.
- // To prevent conflicts, we should only remove the drag controller if it's the
- // same one that was active initially.
- if (dragController == controller) {
- dragController = null
- }
- }
- },
- onCancel = {
- val controller = dragController ?: error("Should be called after onStart")
- controller.onStop(velocity = 0f, canChangeContent = canChangeScene)
- dragController = null
+ scrollController(
+ dragController =
+ draggableHandler.onDragStarted(
+ pointersDown = pointersInfo.pointersDown,
+ startedPosition = pointersInfo.startedPosition,
+ overSlop = if (isIntercepting) 0f else firstScroll,
+ ),
+ canChangeScene = canChangeScene,
+ pointersInfoOwner = pointersInfoOwner,
+ )
},
)
}
}
+private fun scrollController(
+ dragController: DragController,
+ canChangeScene: Boolean,
+ pointersInfoOwner: PointersInfoOwner,
+): ScrollController {
+ return object : ScrollController {
+ override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float {
+ val pointersInfo = pointersInfoOwner.pointersInfo()
+ if (pointersInfo.isMouseWheel) {
+ // Do not support mouse wheel interactions
+ return 0f
+ }
+
+ return dragController.onDrag(delta = deltaScroll)
+ }
+
+ override suspend fun onStop(initialVelocity: Float): Float {
+ return dragController
+ .onStop(velocity = initialVelocity, canChangeContent = canChangeScene)
+ .invoke()
+ }
+
+ override fun onCancel() {
+ dragController.onStop(velocity = 0f, canChangeContent = canChangeScene)
+ }
+
+ /**
+ * We need to maintain scroll priority even if the scene transition can no longer consume
+ * the scroll gesture to allow us to return to the previous scene.
+ */
+ override fun canCancelScroll(available: Float, consumed: Float) = false
+
+ override fun canStopOnPreFling() = true
+ }
+}
+
/**
* The number of pixels below which there won't be a visible difference in the transition and from
* which the animation can stop.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
index 8a0e462..fbd1cd5 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
@@ -128,10 +128,10 @@
) : DelegatingNode() {
private var scrollBehaviorOwner: ScrollBehaviorOwner? = null
- private fun requireScrollBehaviorOwner(): ScrollBehaviorOwner {
+ private fun findScrollBehaviorOwner(): ScrollBehaviorOwner? {
var behaviorOwner = scrollBehaviorOwner
if (behaviorOwner == null) {
- behaviorOwner = requireScrollBehaviorOwner(layoutImpl.draggableHandler(orientation))
+ behaviorOwner = findScrollBehaviorOwner(layoutImpl.draggableHandler(orientation))
scrollBehaviorOwner = behaviorOwner
}
return behaviorOwner
@@ -156,8 +156,8 @@
// transition between scenes. We can assume that the behavior is only needed if
// there is some remaining amount.
if (available != Offset.Zero) {
- requireScrollBehaviorOwner()
- .updateScrollBehaviors(
+ findScrollBehaviorOwner()
+ ?.updateScrollBehaviors(
topOrLeftBehavior = topOrLeftBehavior,
bottomOrRightBehavior = bottomOrRightBehavior,
isExternalOverscrollGesture = isExternalOverscrollGesture,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index a3f2a43..fdf01cc 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -172,15 +172,12 @@
}
/** Find the [ScrollBehaviorOwner] for the current orientation. */
-internal fun DelegatableNode.requireScrollBehaviorOwner(
+internal fun DelegatableNode.findScrollBehaviorOwner(
draggableHandler: DraggableHandlerImpl
-): ScrollBehaviorOwner {
- val ancestorNode =
- checkNotNull(findNearestAncestor(draggableHandler.nestedScrollKey)) {
- "This should never happen! Couldn't find a ScrollBehaviorOwner. " +
- "Are we inside an SceneTransitionLayout?"
- }
- return ancestorNode as ScrollBehaviorOwner
+): ScrollBehaviorOwner? {
+ // If there are no scenes in a particular orientation, the corresponding ScrollBehaviorOwnerNode
+ // is removed from the composition.
+ return findNearestAncestor(draggableHandler.nestedScrollKey) as? ScrollBehaviorOwner
}
internal fun interface ScrollBehaviorOwner {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
index ecf64b7..255da31 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
@@ -18,6 +18,7 @@
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.util.fastCoerceAtLeast
import androidx.compose.ui.util.fastCoerceAtMost
@@ -54,23 +55,38 @@
offsetAvailable > 0 && height() < maxHeight()
},
canStartPostFling = { false },
- canStopOnPreFling = { false },
- onStart = { /* do nothing */ },
- onScroll = { offsetAvailable, _ ->
- val currentHeight = height()
- val amountConsumed =
- if (offsetAvailable > 0) {
- val amountLeft = maxHeight() - currentHeight
- offsetAvailable.fastCoerceAtMost(amountLeft)
- } else {
- val amountLeft = minHeight() - currentHeight
- offsetAvailable.fastCoerceAtLeast(amountLeft)
- }
- onHeightChanged(currentHeight + amountConsumed)
- amountConsumed
- },
- // Don't consume the velocity on pre/post fling
- onStop = { 0f },
- onCancel = { /* do nothing */ },
+ onStart = { LargeTopAppBarScrollController(height, maxHeight, minHeight, onHeightChanged) },
)
}
+
+private class LargeTopAppBarScrollController(
+ val height: () -> Float,
+ val maxHeight: () -> Float,
+ val minHeight: () -> Float,
+ val onHeightChanged: (Float) -> Unit,
+) : ScrollController {
+ override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float {
+ val currentHeight = height()
+ val amountConsumed =
+ if (deltaScroll > 0) {
+ val amountLeft = maxHeight() - currentHeight
+ deltaScroll.fastCoerceAtMost(amountLeft)
+ } else {
+ val amountLeft = minHeight() - currentHeight
+ deltaScroll.fastCoerceAtLeast(amountLeft)
+ }
+ onHeightChanged(currentHeight + amountConsumed)
+ return amountConsumed
+ }
+
+ override suspend fun onStop(initialVelocity: Float): Float {
+ // Don't consume the velocity on pre/post fling
+ return 0f
+ }
+
+ override fun onCancel() {
+ // do nothing
+ }
+
+ override fun canStopOnPreFling() = false
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
index 57d236b..ca44a5c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
@@ -16,46 +16,106 @@
package com.android.compose.nestedscroll
-import androidx.compose.animation.core.AnimationState
-import androidx.compose.animation.core.DecayAnimationSpec
-import androidx.compose.animation.core.animateDecay
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.unit.Velocity
import com.android.compose.ui.util.SpaceVectorConverter
-import kotlin.math.abs
import kotlin.math.sign
-import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
/**
- * A [NestedScrollConnection] that intercepts scroll events in priority mode.
+ * The [ScrollController] provides control over the scroll gesture. It allows you to:
+ * - Scroll the content by a given pixel amount.
+ * - Cancel the current scroll operation.
+ * - Stop the scrolling with a given initial velocity.
*
- * Priority mode allows this connection to take control over scroll events within a nested scroll
- * hierarchy. When in priority mode, this connection consumes scroll events before its children,
- * enabling custom scrolling behaviors like sticky headers.
+ * **Important Notes:**
+ * - [onCancel] is called only when [PriorityNestedScrollConnection.reset] is invoked or when
+ * [canCancelScroll] returns `true` after a call to [onScroll]. It is never called after [onStop].
+ * - [onStop] can be interrupted by a new gesture. In such cases, you need to handle a potential
+ * cancellation within your implementation of [onStop], although [onCancel] will not be called.
+ */
+interface ScrollController {
+ /**
+ * Scrolls the current content by [deltaScroll] pixels.
+ *
+ * @param deltaScroll The amount of pixels to scroll by.
+ * @param source The source of the scroll event.
+ * @return The amount of [deltaScroll] that was consumed.
+ */
+ fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float
+
+ /**
+ * Checks if the current scroll operation can be canceled. This is typically called after
+ * [onScroll] to determine if the [ScrollController] has lost priority and should cancel the
+ * ongoing scroll operation.
+ *
+ * @param available The total amount of scroll available.
+ * @param consumed The amount of scroll consumed by [onScroll].
+ * @return `true` if the scroll can be canceled.
+ */
+ fun canCancelScroll(available: Float, consumed: Float): Boolean {
+ return consumed == 0f
+ }
+
+ /**
+ * Cancels the current scroll operation. This method is called when
+ * [PriorityNestedScrollConnection.reset] is invoked or when [canCancelScroll] returns `true`.
+ */
+ fun onCancel()
+
+ /**
+ * Checks if the scroll can be stopped during the [NestedScrollConnection.onPreFling] phase.
+ *
+ * @return `true` if the scroll can be stopped.
+ */
+ fun canStopOnPreFling(): Boolean
+
+ /**
+ * Stops the controller with the given [initialVelocity]. This typically starts a decay
+ * animation to smoothly bring the scrolling to a stop. This method can be interrupted by a new
+ * gesture, requiring you to handle potential cancellation within your implementation.
+ *
+ * @param initialVelocity The initial velocity of the scroll when stopping.
+ * @return The consumed [initialVelocity] when the animation completes.
+ */
+ suspend fun onStop(initialVelocity: Float): Float
+}
+
+/**
+ * A [NestedScrollConnection] that lets you implement custom scroll behaviors that take priority
+ * over the default nested scrolling logic.
+ *
+ * When started, this connection intercepts scroll events *before* they reach child composables.
+ * This "priority mode" is activated activated when either [canStartPreScroll], [canStartPostScroll]
+ * or [canStartPostFling] returns `true`.
+ *
+ * Once started, the [onStart] lambda provides a [ScrollController] to manage the scrolling. This
+ * controller allows you to directly manipulate the scroll state and define how scroll events are
+ * consumed.
+ *
+ * **Important Considerations:**
+ * - When started, scroll events are typically consumed in `onPreScroll`.
+ * - The provided [ScrollController] should handle potential cancellation of `onStop` due to new
+ * gestures.
+ * - Use [reset] to release the current [ScrollController] and reset the connection to its initial
+ * state.
*
* @param orientation The orientation of the scroll.
- * @param canStartPreScroll lambda that returns true if the connection can start consuming scroll
- * events in pre-scroll mode.
- * @param canStartPostScroll lambda that returns true if the connection can start consuming scroll
- * events in post-scroll mode.
- * @param canStartPostFling lambda that returns true if the connection can start consuming scroll
- * events in post-fling mode.
- * @param canStopOnScroll lambda that returns true if the connection can stop consuming scroll
- * events in scroll mode.
- * @param canStopOnPreFling lambda that returns true if the connection can stop consuming scroll
- * events in pre-fling (i.e. as soon as the user lifts their fingers).
- * @param onStart lambda that is called when the connection starts consuming scroll events.
- * @param onScroll lambda that is called when the connection consumes a scroll event and returns the
- * consumed amount.
- * @param onStop lambda that is called when the connection stops consuming scroll events and returns
- * the consumed velocity.
- * @param onCancel lambda that is called when the connection is cancelled.
+ * @param canStartPreScroll A lambda that returns `true` if the connection should enter priority
+ * mode during the pre-scroll phase. This is called before child connections have a chance to
+ * consume the scroll.
+ * @param canStartPostScroll A lambda that returns `true` if the connection should enter priority
+ * mode during the post-scroll phase. This is called after child connections have consumed the
+ * scroll.
+ * @param canStartPostFling A lambda that returns `true` if the connection should enter priority
+ * mode during the post-fling phase. This is called after a fling gesture has been initiated.
+ * @param onStart A lambda that is called when the connection enters priority mode. It should return
+ * a [ScrollController] that will be used to control the scroll.
* @sample LargeTopAppBarNestedScrollConnection
* @sample com.android.compose.animation.scene.NestedScrollHandlerImpl.nestedScrollConnection
*/
@@ -66,169 +126,213 @@
private val canStartPostScroll:
(offsetAvailable: Float, offsetBeforeStart: Float, source: NestedScrollSource) -> Boolean,
private val canStartPostFling: (velocityAvailable: Float) -> Boolean,
- private val canStopOnScroll: (available: Float, consumed: Float) -> Boolean = { _, consumed ->
- consumed == 0f
- },
- private val canStopOnPreFling: () -> Boolean,
- private val onStart: (offsetAvailable: Float) -> Unit,
- private val onScroll: (offsetAvailable: Float, source: NestedScrollSource) -> Float,
- private val onStop: suspend (velocityAvailable: Float) -> Float,
- private val onCancel: () -> Unit,
+ private val onStart: (firstScroll: Float) -> ScrollController,
) : NestedScrollConnection, SpaceVectorConverter by SpaceVectorConverter(orientation) {
- /** In priority mode [onPreScroll] events are first consumed by the parent, via [onScroll]. */
- private var isPriorityMode = false
+ /** The currently active [ScrollController], or `null` if not in priority mode. */
+ private var currentController: ScrollController? = null
+ /**
+ * A [Deferred] representing the ongoing `onStop` animation. Used to interrupt the animation if
+ * a new gesture occurs.
+ */
+ private var stoppingJob: Deferred<Float>? = null
+
+ /**
+ * Indicates whether the connection is currently in the process of stopping the scroll with the
+ * [ScrollController.onStop] animation.
+ */
+ private val isStopping
+ get() = stoppingJob?.isActive ?: false
+
+ /**
+ * Tracks the cumulative scroll offset that has been consumed by other composables before this
+ * connection enters priority mode. This is used to determine when the connection should take
+ * over scrolling based on the [canStartPreScroll] and [canStartPostScroll] conditions.
+ */
private var offsetScrolledBeforePriorityMode = 0f
- /** This job allows us to interrupt the onStop animation */
- private var onStopJob: Deferred<Float> = CompletableDeferred(0f)
+ override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+ // If stopping, interrupt the animation and clear the controller.
+ if (isStopping) {
+ interruptStopping()
+ }
+
+ // If in priority mode, consume the scroll using the current controller.
+ if (currentController != null) {
+ return scroll(available.toFloat(), source)
+ }
+
+ // Check if pre-scroll condition is met, and start priority mode if necessary.
+ val availableFloat = available.toFloat()
+ if (canStartPreScroll(availableFloat, offsetScrolledBeforePriorityMode, source)) {
+ start(availableFloat)
+ return scroll(availableFloat, source)
+ }
+
+ // Track offset consumed before entering priority mode.
+ offsetScrolledBeforePriorityMode += availableFloat
+ return Offset.Zero
+ }
override fun onPostScroll(
consumed: Offset,
available: Offset,
source: NestedScrollSource,
): Offset {
+ // If in priority mode, scroll events are consumed only in pre-scroll phase.
+ if (currentController != null) return Offset.Zero
+
+ // Check if post-scroll condition is met, and start priority mode if necessary.
val availableFloat = available.toFloat()
- // The offset before the start takes into account the up and down movements, starting from
- // the beginning or from the last fling gesture.
val offsetBeforeStart = offsetScrolledBeforePriorityMode - availableFloat
-
- if (isPriorityMode || !canStartPostScroll(availableFloat, offsetBeforeStart, source)) {
- // The priority mode cannot start so we won't consume the available offset.
- return Offset.Zero
+ if (canStartPostScroll(availableFloat, offsetBeforeStart, source)) {
+ start(availableFloat)
+ return scroll(availableFloat, source)
}
- return start(availableFloat, source).toOffset()
- }
-
- override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
- if (!isPriorityMode) {
- val availableFloat = available.toFloat()
- if (canStartPreScroll(availableFloat, offsetScrolledBeforePriorityMode, source)) {
- return start(availableFloat, source).toOffset()
- }
- // We want to track the amount of offset consumed before entering priority mode
- offsetScrolledBeforePriorityMode += availableFloat
- return Offset.Zero
- }
-
- return scroll(available.toFloat(), source).toOffset()
+ // Do not consume the offset if priority mode is not activated.
+ return Offset.Zero
}
override suspend fun onPreFling(available: Velocity): Velocity {
- if (!isPriorityMode) {
- resetOffsetTracker()
- return Velocity.Zero
+ val controller = currentController ?: return Velocity.Zero
+
+ // If in priority mode and can stop on pre-fling phase, stop the scroll.
+ if (controller.canStopOnPreFling()) {
+ return stop(velocity = available.toFloat())
}
- if (canStopOnPreFling()) {
- // Step 3b: The finger is lifted, we can stop intercepting scroll events and use the
- // velocity of the fling gesture.
- return stop(velocityAvailable = available.toFloat()).toVelocity()
- }
-
- // We don't want to consume the velocity, we prefer to continue receiving scroll events.
+ // Do not consume the velocity if not stopping on pre-fling phase.
return Velocity.Zero
}
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
val availableFloat = available.toFloat()
- if (isPriorityMode) {
- return stop(velocityAvailable = availableFloat).toVelocity()
+ val controller = currentController
+
+ // If in priority mode, stop the scroll.
+ if (controller != null) {
+ return stop(velocity = availableFloat)
}
- if (!canStartPostFling(availableFloat)) {
- return Velocity.Zero
- }
-
- // The offset passed to onPriorityStart() must be != 0f, so we create a small offset of 1px
- // given the available velocity.
+ // Check if post-fling condition is met, and start priority mode if necessary.
// TODO(b/291053278): Remove canStartPostFling() and instead make it possible to define the
// overscroll behavior on the Scene level.
- val smallOffset = availableFloat.sign
- start(
- availableOffset = smallOffset,
- source = NestedScrollSource.SideEffect,
- skipScroll = true,
- )
+ if (canStartPostFling(availableFloat)) {
+ // The offset passed to onPriorityStart() must be != 0f, so we create a small offset of
+ // 1px given the available velocity.
+ val smallOffset = availableFloat.sign
+ start(availableOffset = smallOffset)
+ return stop(availableFloat)
+ }
- // This is the last event of a scroll gesture.
- return stop(availableFloat).toVelocity()
+ // Reset offset tracking after the fling gesture is finished.
+ resetOffsetTracker()
+ return Velocity.Zero
}
/**
- * Method to call before destroying the object or to reset the initial state.
- *
- * TODO(b/303224944) This method should be removed.
+ * Resets the connection to its initial state. This cancels any ongoing scroll operation and
+ * clears the current [ScrollController].
*/
fun reset() {
- if (isPriorityMode) {
- // Step 3c: To ensure that an onStop (or onCancel) is always called for every onStart.
+ if (currentController != null && !isStopping) {
cancel()
} else {
resetOffsetTracker()
}
}
- private fun start(
- availableOffset: Float,
- source: NestedScrollSource,
- skipScroll: Boolean = false,
- ): Float {
- check(!isPriorityMode) {
- "This should never happen, start() was called when isPriorityMode"
- }
+ /**
+ * Starts priority mode by creating a new [ScrollController] using the [onStart] lambda.
+ *
+ * @param availableOffset The initial scroll offset available.
+ */
+ private fun start(availableOffset: Float) {
+ check(currentController == null) { "Another controller is active: $currentController" }
- // Step 1: It's our turn! We start capturing scroll events when one of our children has an
- // available offset following a scroll event.
- isPriorityMode = true
+ resetOffsetTracker()
- onStopJob.cancel()
-
- // Note: onStop will be called if we cannot continue to scroll (step 3a), or the finger is
- // lifted (step 3b), or this object has been destroyed (step 3c).
- onStart(availableOffset)
-
- return if (skipScroll) 0f else scroll(availableOffset, source)
+ currentController = onStart(availableOffset)
}
- private fun scroll(offsetAvailable: Float, source: NestedScrollSource): Float {
- // Step 2: We have the priority and can consume the scroll events.
- val consumedByScroll = onScroll(offsetAvailable, source)
+ /**
+ * Retrieves the current [ScrollController], ensuring that it is not null and that the
+ * [isStopping] state matches the expected value.
+ */
+ private fun requireController(isStopping: Boolean): ScrollController {
+ check(this.isStopping == isStopping) {
+ "isStopping is ${this.isStopping}, instead of $isStopping"
+ }
+ check(offsetScrolledBeforePriorityMode == 0f) {
+ "offset scrolled should be zero, but it was $offsetScrolledBeforePriorityMode"
+ }
+ return checkNotNull(currentController) { "The controller is $currentController" }
+ }
- if (canStopOnScroll(offsetAvailable, consumedByScroll)) {
- // Step 3a: We have lost priority and we no longer need to intercept scroll events.
+ /**
+ * Scrolls the content using the current [ScrollController].
+ *
+ * @param delta The amount of scroll to apply.
+ * @param source The source of the scroll event.
+ * @return The amount of scroll consumed.
+ */
+ private fun scroll(delta: Float, source: NestedScrollSource): Offset {
+ val controller = requireController(isStopping = false)
+ val consumedByScroll = controller.onScroll(delta, source)
+
+ if (controller.canCancelScroll(delta, consumedByScroll)) {
+ // We have lost priority and we no longer need to intercept scroll events.
cancel()
-
- // We've just reset offsetScrolledBeforePriorityMode to 0f
- // We want to track the amount of offset consumed before entering priority mode
- offsetScrolledBeforePriorityMode += offsetAvailable - consumedByScroll
+ offsetScrolledBeforePriorityMode = delta - consumedByScroll
}
- return consumedByScroll
+ return consumedByScroll.toOffset()
}
- /** Reset the tracking of consumed offsets before entering in priority mode. */
+ /** Cancels the current scroll operation and clears the current [ScrollController]. */
+ private fun cancel() {
+ requireController(isStopping = false).onCancel()
+ currentController = null
+ }
+
+ /**
+ * Stops the scroll with the given velocity using the current [ScrollController].
+ *
+ * @param velocity The velocity to stop with.
+ * @return The consumed velocity.
+ */
+ suspend fun stop(velocity: Float): Velocity {
+ val controller = requireController(isStopping = false)
+ return coroutineScope {
+ try {
+ async { controller.onStop(velocity) }
+ // Allows others to interrupt the job.
+ .also { stoppingJob = it }
+ // Note: this can be cancelled by [interruptStopping]
+ .await()
+ .toVelocity()
+ } finally {
+ // If the job is interrupted, it might take a while to cancel. We need to make sure
+ // the current controller is still the initial one.
+ if (currentController == controller) {
+ currentController = null
+ }
+ }
+ }
+ }
+
+ /** Interrupts the ongoing stop animation and clears the current [ScrollController]. */
+ private fun interruptStopping() {
+ requireController(isStopping = true)
+ // We are throwing a CancellationException in the [ScrollController.onStop] method.
+ stoppingJob?.cancel()
+ currentController = null
+ }
+
+ /** Resets the tracking of consumed offsets before entering priority mode. */
private fun resetOffsetTracker() {
offsetScrolledBeforePriorityMode = 0f
}
-
- private suspend fun stop(velocityAvailable: Float): Float {
- check(isPriorityMode) { "This should never happen, stop() was called before start()" }
- isPriorityMode = false
- resetOffsetTracker()
-
- return coroutineScope {
- onStopJob = async { onStop(velocityAvailable) }
- onStopJob.await()
- }
- }
-
- private fun cancel() {
- check(isPriorityMode) { "This should never happen, cancel() was called before start()" }
- isPriorityMode = false
- resetOffsetTracker()
- onCancel()
- }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt
index 5edb99e..37dae39 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt
@@ -225,6 +225,40 @@
}
@Test
+ fun stlNotConsumeUnobservedGesture() {
+ val state =
+ rule.runOnUiThread {
+ MutableSceneTransitionLayoutState(SceneA, transitions = EmptyTestTransitions)
+ }
+
+ rule.setContent {
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ SceneTransitionLayout(
+ state = state,
+ modifier = Modifier.size(layoutWidth, layoutHeight),
+ ) {
+ scene(SceneA) {
+ Spacer(
+ Modifier.verticalNestedScrollToScene()
+ // This scrollable will not consume the gesture.
+ .scrollable(rememberScrollableState { 0f }, Vertical)
+ .fillMaxSize()
+ )
+ }
+ }
+ }
+
+ rule.onRoot().performTouchInput {
+ down(Offset.Zero)
+ // There is no vertical scene.
+ moveBy(Offset(0f, layoutWidth.toPx()), delayMillis = 1_000)
+ }
+
+ rule.waitForIdle()
+ assertThat(state.transitionState).isIdle()
+ }
+
+ @Test
fun customizeStlNestedScrollBehavior_multipleRequests() {
var canScroll = true
val state = setup2ScenesAndScrollTouchSlop {
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
index 1a3b86b..0364cdc 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
@@ -20,6 +20,7 @@
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion.UserInput
import androidx.compose.ui.unit.Velocity
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -48,17 +49,26 @@
canStartPreScroll = { _, _, _ -> canStartPreScroll },
canStartPostScroll = { _, _, _ -> canStartPostScroll },
canStartPostFling = { canStartPostFling },
- canStopOnPreFling = { canStopOnPreFling },
- onStart = { isStarted = true },
- onScroll = { offsetAvailable, _ ->
- lastScroll = offsetAvailable
- if (consumeScroll) offsetAvailable else 0f
+ onStart = { _ ->
+ isStarted = true
+ object : ScrollController {
+ override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float {
+ lastScroll = deltaScroll
+ return if (consumeScroll) deltaScroll else 0f
+ }
+
+ override suspend fun onStop(initialVelocity: Float): Float {
+ lastStop = initialVelocity
+ return if (consumeStop) initialVelocity else 0f
+ }
+
+ override fun onCancel() {
+ isCancelled = true
+ }
+
+ override fun canStopOnPreFling() = canStopOnPreFling
+ }
},
- onStop = {
- lastStop = it
- if (consumeStop) it else 0f
- },
- onCancel = { isCancelled = true },
)
@Test
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index 8e838f3..a89e6fb 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -19,12 +19,14 @@
import com.android.systemui.customization.R
import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.core.LogcatOnlyMessageBuffer
+import com.android.systemui.plugins.clocks.AxisType
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockId
import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockMetadata
import com.android.systemui.plugins.clocks.ClockPickerConfig
import com.android.systemui.plugins.clocks.ClockProvider
+import com.android.systemui.plugins.clocks.ClockReactiveAxis
import com.android.systemui.plugins.clocks.ClockSettings
import com.android.systemui.shared.clocks.view.HorizontalAlignment
import com.android.systemui.shared.clocks.view.VerticalAlignment
@@ -85,8 +87,21 @@
// TODO(b/352049256): Update placeholder to actual resource
resources.getDrawable(R.drawable.clock_default_thumbnail, null),
isReactiveToTone = true,
- isReactiveToTouch = isClockReactiveVariantsEnabled,
- axes = listOf(), // TODO: Ater some picker definition
+ // TODO(b/364673969): Populate the rest of this
+ axes =
+ if (isClockReactiveVariantsEnabled)
+ listOf(
+ ClockReactiveAxis(
+ key = "wdth",
+ type = AxisType.Slider,
+ maxValue = 1000f,
+ minValue = 100f,
+ currentValue = 400f,
+ name = "Width",
+ description = "Glyph Width",
+ )
+ )
+ else listOf(),
)
}
diff --git a/packages/SystemUI/log/Android.bp b/packages/SystemUI/log/Android.bp
index 2f1d354..afdcae48 100644
--- a/packages/SystemUI/log/Android.bp
+++ b/packages/SystemUI/log/Android.bp
@@ -22,19 +22,15 @@
default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
}
-android_library {
+java_library {
name: "SystemUILogLib",
- use_resource_processor: true,
srcs: [
"src/**/*.java",
"src/**/*.kt",
],
static_libs: [
- "androidx.core_core-ktx",
- "androidx.annotation_annotation",
"error_prone_annotations",
"SystemUICommon",
],
- manifest: "AndroidManifest.xml",
kotlincflags: ["-Xjvm-default=all"],
}
diff --git a/packages/SystemUI/log/AndroidManifest.xml b/packages/SystemUI/log/AndroidManifest.xml
deleted file mode 100644
index 4021e1a..0000000
--- a/packages/SystemUI/log/AndroidManifest.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2023 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.systemui.log">
-
-
-</manifest>
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java
index bd33e52..58c3fec 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java
@@ -20,9 +20,7 @@
import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -31,12 +29,11 @@
import android.graphics.Rect;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.FlagsParameterization;
-import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.VelocityTracker;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
@@ -45,12 +42,9 @@
import com.android.systemui.ambient.touch.scrim.ScrimController;
import com.android.systemui.ambient.touch.scrim.ScrimManager;
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel;
-import com.android.systemui.flags.SceneContainerFlagParameterizationKt;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.scene.domain.interactor.SceneInteractor;
-import com.android.systemui.scene.ui.view.WindowRootView;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -64,14 +58,10 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import java.util.List;
import java.util.Optional;
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
@SmallTest
-@RunWith(ParameterizedAndroidJunit4.class)
+@RunWith(AndroidJUnit4.class)
@EnableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
@DisableFlags(Flags.FLAG_COMMUNAL_BOUNCER_DO_NOT_MODIFY_PLUGIN_OPEN)
public class BouncerFullscreenSwipeTouchHandlerTest extends SysuiTestCase {
@@ -124,11 +114,6 @@
@Mock
KeyguardInteractor mKeyguardInteractor;
- @Mock
- WindowRootView mWindowRootView;
-
- private SceneInteractor mSceneInteractor;
-
private static final float TOUCH_REGION = .3f;
private static final float MIN_BOUNCER_HEIGHT = .05f;
@@ -139,21 +124,9 @@
/* flags= */ 0
);
- @Parameters(name = "{0}")
- public static List<FlagsParameterization> getParams() {
- return SceneContainerFlagParameterizationKt.parameterizeSceneContainerFlag();
- }
-
- public BouncerFullscreenSwipeTouchHandlerTest(FlagsParameterization flags) {
- super();
- mSetFlagsRule.setFlagsParameterization(flags);
- }
-
@Before
public void setup() {
mKosmos = new KosmosJavaAdapter(this);
- mSceneInteractor = spy(mKosmos.getSceneInteractor());
-
MockitoAnnotations.initMocks(this);
mTouchHandler = new BouncerSwipeTouchHandler(
mKosmos.getTestScope(),
@@ -169,9 +142,7 @@
MIN_BOUNCER_HEIGHT,
mUiEventLogger,
mActivityStarter,
- mKeyguardInteractor,
- mSceneInteractor,
- Optional.of(() -> mWindowRootView));
+ mKeyguardInteractor);
when(mScrimManager.getCurrentController()).thenReturn(mScrimController);
when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator);
@@ -182,38 +153,6 @@
}
/**
- * Makes sure that touches go to the scene container when the flag is on.
- */
- @Test
- @EnableFlags(Flags.FLAG_SCENE_CONTAINER)
- public void testSwipeUp_sendsTouchesToWindowRootView() {
- mTouchHandler.onGlanceableTouchAvailable(true);
- mTouchHandler.onSessionStart(mTouchSession);
- ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
- ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
- verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
-
- final OnGestureListener gestureListener = gestureListenerCaptor.getValue();
-
- final int screenHeight = 100;
- final float distanceY = screenHeight * 0.42f;
-
- final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
- 0, screenHeight, 0);
- final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
- 0, screenHeight - distanceY, 0);
-
- assertThat(gestureListener.onScroll(event1, event2, 0,
- distanceY))
- .isTrue();
-
- // Ensure only called once
- verify(mSceneInteractor).onRemoteUserInputStarted(any());
- verify(mWindowRootView).dispatchTouchEvent(event1);
- verify(mWindowRootView).dispatchTouchEvent(event2);
- }
-
- /**
* Ensures expansion does not happen for full vertical swipes when touch is not available.
*/
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java
index 494e0b4..9568167 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java
@@ -26,7 +26,6 @@
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
@@ -38,12 +37,12 @@
import android.graphics.Region;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.FlagsParameterization;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.VelocityTracker;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
@@ -53,12 +52,9 @@
import com.android.systemui.ambient.touch.scrim.ScrimManager;
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants;
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel;
-import com.android.systemui.flags.SceneContainerFlagParameterizationKt;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.scene.domain.interactor.SceneInteractor;
-import com.android.systemui.scene.ui.view.WindowRootView;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -74,14 +70,10 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import java.util.List;
import java.util.Optional;
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
@SmallTest
-@RunWith(ParameterizedAndroidJunit4.class)
+@RunWith(AndroidJUnit4.class)
@DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
private KosmosJavaAdapter mKosmos;
@@ -130,9 +122,6 @@
Region mRegion;
@Mock
- WindowRootView mWindowRootView;
-
- @Mock
CommunalViewModel mCommunalViewModel;
@Mock
@@ -141,8 +130,6 @@
@Captor
ArgumentCaptor<Rect> mRectCaptor;
- private SceneInteractor mSceneInteractor;
-
private static final float TOUCH_REGION = .3f;
private static final int SCREEN_WIDTH_PX = 1024;
private static final int SCREEN_HEIGHT_PX = 100;
@@ -155,21 +142,9 @@
/* flags= */ 0
);
- @Parameters(name = "{0}")
- public static List<FlagsParameterization> getParams() {
- return SceneContainerFlagParameterizationKt.parameterizeSceneContainerFlag();
- }
-
- public BouncerSwipeTouchHandlerTest(FlagsParameterization flags) {
- super();
- mSetFlagsRule.setFlagsParameterization(flags);
- }
-
@Before
public void setup() {
mKosmos = new KosmosJavaAdapter(this);
- mSceneInteractor = spy(mKosmos.getSceneInteractor());
-
MockitoAnnotations.initMocks(this);
mTouchHandler = new BouncerSwipeTouchHandler(
mKosmos.getTestScope(),
@@ -185,10 +160,7 @@
MIN_BOUNCER_HEIGHT,
mUiEventLogger,
mActivityStarter,
- mKeyguardInteractor,
- mSceneInteractor,
- Optional.of(() -> mWindowRootView)
- );
+ mKeyguardInteractor);
when(mScrimManager.getCurrentController()).thenReturn(mScrimController);
when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator);
@@ -395,7 +367,6 @@
* Makes sure the expansion amount is proportional to (1 - scroll).
*/
@Test
- @DisableFlags(Flags.FLAG_SCENE_CONTAINER)
public void testSwipeUp_setsCorrectExpansionAmount() {
mTouchHandler.onSessionStart(mTouchSession);
ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
@@ -409,36 +380,6 @@
}
/**
- * Makes sure that touches go to the scene container when the flag is on.
- */
- @Test
- @EnableFlags(Flags.FLAG_SCENE_CONTAINER)
- public void testSwipeUp_sendsTouchesToWindowRootView() {
- mTouchHandler.onSessionStart(mTouchSession);
- ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
- ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
- verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
-
- final OnGestureListener gestureListener = gestureListenerCaptor.getValue();
-
- final float distanceY = SCREEN_HEIGHT_PX * 0.42f;
-
- final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
- 0, SCREEN_HEIGHT_PX, 0);
- final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
- 0, SCREEN_HEIGHT_PX - distanceY, 0);
-
- assertThat(gestureListener.onScroll(event1, event2, 0,
- distanceY))
- .isTrue();
-
- // Ensure only called once
- verify(mSceneInteractor).onRemoteUserInputStarted(any());
- verify(mWindowRootView).dispatchTouchEvent(event1);
- verify(mWindowRootView).dispatchTouchEvent(event2);
- }
-
- /**
* Verifies that swiping up when the lock pattern is not secure dismissed dream and consumes
* the gesture.
*/
@@ -535,7 +476,6 @@
* Tests that ending an upward swipe before the set threshold leads to bouncer collapsing down.
*/
@Test
- @DisableFlags(Flags.FLAG_SCENE_CONTAINER)
public void testSwipeUpPositionBelowThreshold_collapsesBouncer() {
final float swipeUpPercentage = .3f;
final float expansion = 1 - swipeUpPercentage;
@@ -559,7 +499,6 @@
* Tests that ending an upward swipe above the set threshold will continue the expansion.
*/
@Test
- @DisableFlags(Flags.FLAG_SCENE_CONTAINER)
public void testSwipeUpPositionAboveThreshold_expandsBouncer() {
final float swipeUpPercentage = .7f;
final float expansion = 1 - swipeUpPercentage;
@@ -589,7 +528,6 @@
* Tests that swiping up with a speed above the set threshold will continue the expansion.
*/
@Test
- @DisableFlags(Flags.FLAG_SCENE_CONTAINER)
public void testSwipeUpVelocityAboveMin_expandsBouncer() {
when(mFlingAnimationUtils.getMinVelocityPxPerSecond()).thenReturn((float) 0);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt
index ad636cf..38ea4497 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt
@@ -18,9 +18,9 @@
import android.app.DreamManager
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.FlagsParameterization
import android.view.GestureDetector
import android.view.MotionEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
@@ -28,20 +28,14 @@
import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
-import com.android.systemui.flags.andSceneContainer
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.kosmos.testScope
-import com.android.systemui.scene.data.repository.sceneContainerRepository
-import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shared.system.InputChannelCompat
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.testKosmos
import com.google.common.truth.Truth
-import com.google.common.truth.Truth.assertThat
import java.util.Optional
-import javax.inject.Provider
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -53,29 +47,22 @@
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4
-import platform.test.runner.parameterized.Parameters
@SmallTest
-@RunWith(ParameterizedAndroidJunit4::class)
-class ShadeTouchHandlerTest(flags: FlagsParameterization) : SysuiTestCase() {
+@RunWith(AndroidJUnit4::class)
+class ShadeTouchHandlerTest : SysuiTestCase() {
private var kosmos = testKosmos()
private var mCentralSurfaces = mock<CentralSurfaces>()
private var mShadeViewController = mock<ShadeViewController>()
private var mDreamManager = mock<DreamManager>()
private var mTouchSession = mock<TouchSession>()
private var communalViewModel = mock<CommunalViewModel>()
- private var windowRootView = mock<WindowRootView>()
private lateinit var mTouchHandler: ShadeTouchHandler
private var mGestureListenerCaptor = argumentCaptor<GestureDetector.OnGestureListener>()
private var mInputListenerCaptor = argumentCaptor<InputChannelCompat.InputEventListener>()
- init {
- mSetFlagsRule.setFlagsParameterization(flags)
- }
-
@Before
fun setup() {
mTouchHandler =
@@ -86,9 +73,7 @@
mDreamManager,
communalViewModel,
kosmos.communalSettingsInteractor,
- kosmos.sceneInteractor,
- Optional.of(Provider<WindowRootView> { windowRootView }),
- TOUCH_HEIGHT,
+ TOUCH_HEIGHT
)
}
@@ -112,7 +97,7 @@
// Verifies that a swipe down forwards captured touches to central surfaces for handling.
@Test
- @DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX, Flags.FLAG_SCENE_CONTAINER)
+ @DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
@EnableFlags(Flags.FLAG_COMMUNAL_HUB)
fun testSwipeDown_communalEnabled_sentToCentralSurfaces() {
kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
@@ -125,11 +110,7 @@
// Verifies that a swipe down forwards captured touches to the shade view for handling.
@Test
- @DisableFlags(
- Flags.FLAG_COMMUNAL_HUB,
- Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX,
- Flags.FLAG_SCENE_CONTAINER,
- )
+ @DisableFlags(Flags.FLAG_COMMUNAL_HUB, Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
fun testSwipeDown_communalDisabled_sentToShadeView() {
swipe(Direction.DOWN)
@@ -140,7 +121,7 @@
// Verifies that a swipe down while dreaming forwards captured touches to the shade view for
// handling.
@Test
- @DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX, Flags.FLAG_SCENE_CONTAINER)
+ @DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
fun testSwipeDown_dreaming_sentToShadeView() {
whenever(mDreamManager.isDreaming).thenReturn(true)
swipe(Direction.DOWN)
@@ -149,34 +130,9 @@
verify(mShadeViewController, times(2)).handleExternalTouch(any())
}
- // Verifies that a swipe down forwards captured touches to the window root view for handling.
- @Test
- @EnableFlags(Flags.FLAG_COMMUNAL_HUB, Flags.FLAG_SCENE_CONTAINER)
- fun testSwipeDown_sceneContainerEnabled_sentToWindowRootView() {
- swipe(Direction.DOWN)
-
- // Both motion events are sent for central surfaces to process.
- assertThat(kosmos.sceneContainerRepository.isRemoteUserInputOngoing.value).isTrue()
- verify(windowRootView, times(2)).dispatchTouchEvent(any())
- }
-
- // Verifies that a swipe down while dreaming forwards captured touches to the window root view
- // for handling.
- @Test
- @EnableFlags(Flags.FLAG_SCENE_CONTAINER)
- @DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
- fun testSwipeDown_dreaming_sentToWindowRootView() {
- whenever(mDreamManager.isDreaming).thenReturn(true)
- swipe(Direction.DOWN)
-
- // Both motion events are sent for the shade view to process.
- assertThat(kosmos.sceneContainerRepository.isRemoteUserInputOngoing.value).isTrue()
- verify(windowRootView, times(2)).dispatchTouchEvent(any())
- }
-
// Verifies that a swipe up is not forwarded to central surfaces.
@Test
- @DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX, Flags.FLAG_SCENE_CONTAINER)
+ @DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
@EnableFlags(Flags.FLAG_COMMUNAL_HUB)
fun testSwipeUp_communalEnabled_touchesNotSent() {
kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
@@ -190,11 +146,7 @@
// Verifies that a swipe up is not forwarded to the shade view.
@Test
- @DisableFlags(
- Flags.FLAG_COMMUNAL_HUB,
- Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX,
- Flags.FLAG_SCENE_CONTAINER,
- )
+ @DisableFlags(Flags.FLAG_COMMUNAL_HUB, Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
fun testSwipeUp_communalDisabled_touchesNotSent() {
swipe(Direction.UP)
@@ -203,17 +155,6 @@
verify(mShadeViewController, never()).handleExternalTouch(any())
}
- // Verifies that a swipe up is not forwarded to the window root view.
- @Test
- @EnableFlags(Flags.FLAG_COMMUNAL_HUB, Flags.FLAG_SCENE_CONTAINER)
- fun testSwipeUp_sceneContainerEnabled_touchesNotSent() {
- swipe(Direction.UP)
-
- // Motion events are not sent for window root view to process as the swipe is going in the
- // wrong direction.
- verify(windowRootView, never()).dispatchTouchEvent(any())
- }
-
@Test
@DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
fun testCancelMotionEvent_popsTouchSession() {
@@ -302,16 +243,10 @@
private enum class Direction {
DOWN,
- UP,
+ UP
}
companion object {
private const val TOUCH_HEIGHT = 20
-
- @JvmStatic
- @Parameters(name = "{0}")
- fun getParams(): List<FlagsParameterization> {
- return FlagsParameterization.allCombinationsOf().andSceneContainer()
- }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt
new file mode 100644
index 0000000..bd26e42
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@kotlinx.coroutines.ExperimentalCoroutinesApi
+class KeyguardLockWhileAwakeInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private lateinit var underTest: KeyguardLockWhileAwakeInteractor
+
+ @Before
+ fun setup() {
+ underTest = kosmos.keyguardLockWhileAwakeInteractor
+ }
+
+ @Test
+ fun emitsMultipleTimeoutEvents() =
+ testScope.runTest {
+ val values by collectValues(underTest.lockWhileAwakeEvents)
+
+ underTest.onKeyguardServiceDoKeyguardTimeout(options = null)
+ runCurrent()
+
+ assertThat(values)
+ .containsExactly(LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON)
+
+ advanceTimeBy(1000)
+ underTest.onKeyguardServiceDoKeyguardTimeout(options = null)
+ runCurrent()
+
+ assertThat(values)
+ .containsExactly(
+ LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON,
+ LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON,
+ )
+ }
+
+ @Test
+ fun emitsWhenKeyguardEnabled_onlyIfShowingWhenDisabled() =
+ testScope.runTest {
+ val values by collectValues(underTest.lockWhileAwakeEvents)
+
+ kosmos.biometricSettingsRepository.setIsUserInLockdown(false)
+ runCurrent()
+
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+ runCurrent()
+
+ assertEquals(0, values.size)
+
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(true)
+ runCurrent()
+
+ assertThat(values).containsExactly(LockWhileAwakeReason.KEYGUARD_REENABLED)
+
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ testScope = testScope,
+ )
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(true)
+ runCurrent()
+
+ assertThat(values).containsExactly(LockWhileAwakeReason.KEYGUARD_REENABLED)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index eef4c3d..83d2617 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -20,7 +20,6 @@
import android.app.admin.DevicePolicyManager
import android.content.Intent
import android.os.UserHandle
-import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
@@ -79,10 +78,6 @@
import platform.test.runner.parameterized.Parameters
@OptIn(ExperimentalCoroutinesApi::class)
-@FlakyTest(
- bugId = 292574995,
- detail = "on certain architectures all permutations with startActivity=true is causing failures"
-)
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
@DisableSceneContainer
@@ -93,11 +88,7 @@
private val DRAWABLE =
mock<Icon> {
whenever(this.contentDescription)
- .thenReturn(
- ContentDescription.Resource(
- res = CONTENT_DESCRIPTION_RESOURCE_ID,
- )
- )
+ .thenReturn(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID))
}
private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337
@@ -273,13 +264,7 @@
context = context,
userFileManager =
mock<UserFileManager>().apply {
- whenever(
- getSharedPreferences(
- anyString(),
- anyInt(),
- anyInt(),
- )
- )
+ whenever(getSharedPreferences(anyString(), anyInt(), anyInt()))
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
@@ -316,9 +301,7 @@
underTest =
KeyguardQuickAffordanceInteractor(
keyguardInteractor =
- KeyguardInteractorFactory.create(
- featureFlags = featureFlags,
- )
+ KeyguardInteractorFactory.create(featureFlags = featureFlags)
.keyguardInteractor,
shadeInteractor = kosmos.shadeInteractor,
lockPatternUtils = lockPatternUtils,
@@ -350,9 +333,7 @@
homeControls.setState(
lockScreenState =
- KeyguardQuickAffordanceConfig.LockScreenState.Visible(
- icon = DRAWABLE,
- )
+ KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = DRAWABLE)
)
homeControls.onTriggeredResult =
if (startActivity) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index f4d2ea0..c8a1648 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -560,7 +560,9 @@
.startedTransition(
to = KeyguardState.LOCKSCREEN,
from = KeyguardState.GONE,
- ownerName = "FromGoneTransitionInteractor",
+ ownerName =
+ "FromGoneTransitionInteractor" +
+ "(keyguard interactor says keyguard is showing)",
animatorAssertion = { it.isNotNull() },
)
@@ -640,7 +642,9 @@
.startedTransition(
to = KeyguardState.GLANCEABLE_HUB,
from = KeyguardState.GONE,
- ownerName = FromGoneTransitionInteractor::class.simpleName,
+ ownerName =
+ FromGoneTransitionInteractor::class.simpleName +
+ "(keyguard interactor says keyguard is showing)",
animatorAssertion = { it.isNotNull() },
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 527aeaa..83f95ea 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -28,6 +28,8 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
@@ -604,6 +606,46 @@
assertThat(mPagedTileLayoutListening).isFalse();
}
+ @Test
+ public void reAttach_configurationChangePending_triggersConfigurationListener() {
+ mController.onViewDetached();
+
+ when(mQSPanel.hadConfigurationChangeWhileDetached()).thenReturn(true);
+ clearInvocations(mQSLogger);
+
+ mController.onViewAttached();
+
+ verify(mQSLogger).logOnConfigurationChanged(
+ anyInt(),
+ anyInt(),
+ anyBoolean(),
+ anyBoolean(),
+ anyInt(),
+ anyInt(),
+ anyString()
+ );
+ }
+
+ @Test
+ public void reAttach_noConfigurationChangePending_doesntTriggerConfigurationListener() {
+ mController.onViewDetached();
+
+ when(mQSPanel.hadConfigurationChangeWhileDetached()).thenReturn(false);
+ clearInvocations(mQSLogger);
+
+ mController.onViewAttached();
+
+ verify(mQSLogger, never()).logOnConfigurationChanged(
+ anyInt(),
+ anyInt(),
+ anyBoolean(),
+ anyBoolean(),
+ anyInt(),
+ anyInt(),
+ anyString()
+ );
+ }
+
private boolean usingMediaPlayer() {
return !SceneContainerFlag.isEnabled();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt
index 2194c75..f3fc0ab 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles.impl.irecording
+import android.os.Handler
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -28,6 +29,7 @@
import com.android.systemui.recordissue.IssueRecordingState
import com.android.systemui.settings.fakeUserFileManager
import com.android.systemui.settings.userTracker
+import com.android.systemui.util.settings.fakeGlobalSettings
import com.google.common.truth.Truth
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
@@ -51,7 +53,14 @@
@Before
fun setup() {
- state = IssueRecordingState(userTracker, userFileManager)
+ state =
+ IssueRecordingState(
+ userTracker,
+ userFileManager,
+ Handler.getMain(),
+ mContext.contentResolver,
+ kosmos.fakeGlobalSettings,
+ )
underTest = IssueRecordingDataInteractor(state, kosmos.testScope.testScheduler)
}
@@ -67,10 +76,12 @@
Truth.assertThat(data?.isRecording).isFalse()
state.isRecording = true
+ state.onRecordingChangeListener.onChange(true)
runCurrent()
Truth.assertThat(data?.isRecording).isTrue()
state.isRecording = false
+ state.onRecordingChangeListener.onChange(true)
runCurrent()
Truth.assertThat(data?.isRecording).isFalse()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
index 99d2da67..9c2be89 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles.impl.irecording
+import android.os.Handler
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -38,6 +39,7 @@
import com.android.systemui.settings.userTracker
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
import com.android.systemui.statusbar.policy.keyguardStateController
+import com.android.systemui.util.settings.fakeGlobalSettings
import com.google.common.truth.Truth
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -83,7 +85,13 @@
underTest =
IssueRecordingUserActionInteractor(
testDispatcher,
- IssueRecordingState(userTracker, userFileManager),
+ IssueRecordingState(
+ userTracker,
+ userFileManager,
+ Handler.getMain(),
+ mContext.contentResolver,
+ kosmos.fakeGlobalSettings,
+ ),
KeyguardDismissUtil(
keyguardStateController,
statusBarStateController,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
index aceaab8..f2e658d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
@@ -19,6 +19,7 @@
import android.app.IActivityManager
import android.app.NotificationManager
import android.net.Uri
+import android.os.Handler
import android.os.UserHandle
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -33,6 +34,7 @@
import com.android.systemui.settings.UserContextProvider
import com.android.systemui.settings.userFileManager
import com.android.systemui.settings.userTracker
+import com.android.systemui.util.settings.fakeGlobalSettings
import com.android.traceur.TraceConfig
import com.google.common.truth.Truth
import org.junit.Before
@@ -55,7 +57,13 @@
private val dialogTransitionAnimator: DialogTransitionAnimator = kosmos.dialogTransitionAnimator
private lateinit var traceurConnection: TraceurConnection
private val issueRecordingState =
- IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+ IssueRecordingState(
+ kosmos.userTracker,
+ kosmos.userFileManager,
+ Handler.getMain(),
+ mContext.contentResolver,
+ kosmos.fakeGlobalSettings,
+ )
private val iActivityManager = mock<IActivityManager>()
private val notificationManager = mock<NotificationManager>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
index 4ab3c7b..83bdcd2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
@@ -16,6 +16,8 @@
package com.android.systemui.recordissue
+import android.content.ContentResolver
+import android.os.Handler
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -23,11 +25,15 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.settings.userFileManager
import com.android.systemui.settings.userTracker
+import com.android.systemui.util.settings.fakeGlobalSettings
import com.google.common.truth.Truth
-import java.util.concurrent.CountDownLatch
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.verify
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -36,10 +42,19 @@
private val kosmos = Kosmos()
private lateinit var underTest: IssueRecordingState
+ @Mock private lateinit var resolver: ContentResolver
@Before
fun setup() {
- underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+ MockitoAnnotations.initMocks(this)
+ underTest =
+ IssueRecordingState(
+ kosmos.userTracker,
+ kosmos.userFileManager,
+ Handler.getMain(),
+ resolver,
+ kosmos.fakeGlobalSettings,
+ )
}
@Test
@@ -47,7 +62,14 @@
val expected = true
underTest.takeBugreport = expected
- underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+ underTest =
+ IssueRecordingState(
+ kosmos.userTracker,
+ kosmos.userFileManager,
+ Handler.getMain(),
+ resolver,
+ kosmos.fakeGlobalSettings,
+ )
Truth.assertThat(underTest.takeBugreport).isEqualTo(expected)
}
@@ -57,7 +79,14 @@
val expected = true
underTest.recordScreen = expected
- underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+ underTest =
+ IssueRecordingState(
+ kosmos.userTracker,
+ kosmos.userFileManager,
+ Handler.getMain(),
+ resolver,
+ kosmos.fakeGlobalSettings,
+ )
Truth.assertThat(underTest.recordScreen).isEqualTo(expected)
}
@@ -65,7 +94,14 @@
@Test
fun hasUserApprovedScreenRecording_isTrue_afterBeingMarkedAsCompleted() {
underTest.markUserApprovalForScreenRecording()
- underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+ underTest =
+ IssueRecordingState(
+ kosmos.userTracker,
+ kosmos.userFileManager,
+ Handler.getMain(),
+ resolver,
+ kosmos.fakeGlobalSettings,
+ )
Truth.assertThat(underTest.hasUserApprovedScreenRecording).isEqualTo(true)
}
@@ -75,35 +111,35 @@
val expected = setOf("a", "b", "c")
underTest.tagTitles = expected
- underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+ underTest =
+ IssueRecordingState(
+ kosmos.userTracker,
+ kosmos.userFileManager,
+ Handler.getMain(),
+ resolver,
+ kosmos.fakeGlobalSettings,
+ )
Truth.assertThat(underTest.tagTitles).isEqualTo(expected)
}
@Test
- fun isRecording_callsListeners_onTheValueChanging() {
- val count = CountDownLatch(1)
- val listener = Runnable { count.countDown() }
+ fun addListener_registersContentObserver_ifListOfListenersIsNotEmpty() {
+ val listener = Runnable { /* No-op */ }
underTest.addListener(listener)
- underTest.isRecording = true
- Truth.assertThat(count.count).isEqualTo(0)
+ verify(resolver).registerContentObserver(any(), any(), any())
}
@Test
- fun isRecording_callsOnlyListeners_whoHaveNotBeenRemoved() {
- val count1 = CountDownLatch(1)
- val count2 = CountDownLatch(1)
- val listener1 = Runnable { count1.countDown() }
- val listener2 = Runnable { count2.countDown() }
+ fun removeListener_unRegistersContentObserver_ifListOfListenersIsEmpty() {
+ val listener = Runnable { /* No-op */ }
- underTest.addListener(listener1)
- underTest.removeListener(listener1)
- underTest.addListener(listener2)
- underTest.isRecording = true
+ underTest.addListener(listener)
+ underTest.removeListener(listener)
- Truth.assertThat(count1.count).isEqualTo(1)
- Truth.assertThat(count2.count).isEqualTo(0)
+ verify(resolver).registerContentObserver(any(), any(), any())
+ verify(resolver).unregisterContentObserver(any())
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
index 7d4918a..b5043ce 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
@@ -39,7 +39,7 @@
@Before
fun setUp() {
- underTest = ShadeRepositoryImpl(getContext())
+ underTest = ShadeRepositoryImpl()
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 25670cb..d5a7c89 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -64,7 +64,6 @@
import com.android.systemui.scene.data.repository.Transition
import com.android.systemui.scene.data.repository.setTransition
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.mockLargeScreenHeaderHelper
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
@@ -116,36 +115,18 @@
kosmos.aodBurnInViewModel = aodBurnInViewModel
}
- val testScope = kosmos.testScope
- val configurationRepository
- get() = kosmos.fakeConfigurationRepository
-
- val keyguardRepository
- get() = kosmos.fakeKeyguardRepository
-
- val keyguardInteractor
- get() = kosmos.keyguardInteractor
-
- val keyguardRootViewModel
- get() = kosmos.keyguardRootViewModel
-
- val keyguardTransitionRepository
- get() = kosmos.fakeKeyguardTransitionRepository
-
- val shadeTestUtil
- get() = kosmos.shadeTestUtil
-
- val sharedNotificationContainerInteractor
- get() = kosmos.sharedNotificationContainerInteractor
-
- val largeScreenHeaderHelper
- get() = kosmos.mockLargeScreenHeaderHelper
-
- val communalSceneRepository
- get() = kosmos.communalSceneRepository
-
- val shadeRepository
- get() = kosmos.fakeShadeRepository
+ private val testScope = kosmos.testScope
+ private val configurationRepository by lazy { kosmos.fakeConfigurationRepository }
+ private val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
+ private val keyguardInteractor by lazy { kosmos.keyguardInteractor }
+ private val keyguardRootViewModel by lazy { kosmos.keyguardRootViewModel }
+ private val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
+ private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
+ private val sharedNotificationContainerInteractor by lazy {
+ kosmos.sharedNotificationContainerInteractor
+ }
+ private val largeScreenHeaderHelper by lazy { kosmos.mockLargeScreenHeaderHelper }
+ private val communalSceneRepository by lazy { kosmos.communalSceneRepository }
lateinit var underTest: SharedNotificationContainerViewModel
@@ -637,6 +618,45 @@
@Test
@DisableSceneContainer
+ fun boundsStableWhenGoingToAlternateBouncer() =
+ testScope.runTest {
+ val bounds by collectLastValue(underTest.bounds)
+
+ // Start on lockscreen
+ showLockscreen()
+
+ keyguardInteractor.setNotificationContainerBounds(
+ NotificationContainerBounds(top = 1f, bottom = 2f)
+ )
+
+ assertThat(bounds).isEqualTo(NotificationContainerBounds(top = 1f, bottom = 2f))
+
+ // Begin transition to AOD
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(LOCKSCREEN, ALTERNATE_BOUNCER, 0f, TransitionState.STARTED)
+ )
+ runCurrent()
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(LOCKSCREEN, ALTERNATE_BOUNCER, 0f, TransitionState.RUNNING)
+ )
+ runCurrent()
+
+ // This is the last step before FINISHED is sent, which could trigger a change in bounds
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(LOCKSCREEN, ALTERNATE_BOUNCER, 1f, TransitionState.RUNNING)
+ )
+ runCurrent()
+ assertThat(bounds).isEqualTo(NotificationContainerBounds(top = 1f, bottom = 2f))
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(LOCKSCREEN, ALTERNATE_BOUNCER, 1f, TransitionState.FINISHED)
+ )
+ runCurrent()
+ assertThat(bounds).isEqualTo(NotificationContainerBounds(top = 1f, bottom = 2f))
+ }
+
+ @Test
+ @DisableSceneContainer
fun boundsDoNotChangeWhileLockscreenToAodTransitionIsActive() =
testScope.runTest {
val bounds by collectLastValue(underTest.bounds)
@@ -1229,6 +1249,75 @@
assertThat(alpha).isEqualTo(1f)
}
+ @Test
+ @DisableSceneContainer
+ fun notificationAbsoluteBottom() =
+ testScope.runTest {
+ var notificationCount = 2
+ val calculateSpace = { _: Float, _: Boolean -> notificationCount }
+ val shelfHeight = 10F
+ val heightForNotification = 20F
+ val calculateHeight = { count: Int -> count * heightForNotification + shelfHeight }
+ val stackAbsoluteBottom by
+ collectLastValue(
+ underTest.getNotificationStackAbsoluteBottom(
+ calculateSpace,
+ calculateHeight,
+ shelfHeight,
+ )
+ )
+ advanceTimeBy(50L)
+ showLockscreen()
+
+ shadeTestUtil.setSplitShade(false)
+ keyguardInteractor.setNotificationContainerBounds(
+ NotificationContainerBounds(top = 100F, bottom = 300F)
+ )
+ configurationRepository.onAnyConfigurationChange()
+
+ assertThat(stackAbsoluteBottom).isEqualTo(150F)
+
+ // Also updates when directly requested (as it would from NotificationStackScrollLayout)
+ notificationCount = 3
+ sharedNotificationContainerInteractor.notificationStackChanged()
+ advanceTimeBy(50L)
+ assertThat(stackAbsoluteBottom).isEqualTo(170F)
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun notificationAbsoluteBottom_maxNotificationIsZero_noShelfHeight() =
+ testScope.runTest {
+ var notificationCount = 2
+ val calculateSpace = { _: Float, _: Boolean -> notificationCount }
+ val shelfHeight = 10F
+ val heightForNotification = 20F
+ val calculateHeight = { count: Int -> count * heightForNotification + shelfHeight }
+ val stackAbsoluteBottom by
+ collectLastValue(
+ underTest.getNotificationStackAbsoluteBottom(
+ calculateSpace,
+ calculateHeight,
+ shelfHeight,
+ )
+ )
+ advanceTimeBy(50L)
+ showLockscreen()
+
+ shadeTestUtil.setSplitShade(false)
+ keyguardInteractor.setNotificationContainerBounds(
+ NotificationContainerBounds(top = 100F, bottom = 300F)
+ )
+ configurationRepository.onAnyConfigurationChange()
+
+ assertThat(stackAbsoluteBottom).isEqualTo(150F)
+
+ notificationCount = 0
+ sharedNotificationContainerInteractor.notificationStackChanged()
+ advanceTimeBy(50L)
+ assertThat(stackAbsoluteBottom).isEqualTo(100F)
+ }
+
private suspend fun TestScope.showLockscreen() {
shadeTestUtil.setQsExpansion(0f)
shadeTestUtil.setLockscreenShadeExpansion(0f)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt
new file mode 100644
index 0000000..bfafdab
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt
@@ -0,0 +1,90 @@
+/*
+ * 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.volume.dialog.sliders.domain.interactor
+
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.VolumeDialogController
+import com.android.systemui.plugins.fakeVolumeDialogController
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.dialog.sliders.domain.model.volumeDialogSliderType
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper
+class VolumeDialogSliderInteractorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private lateinit var underTest: VolumeDialogSliderInteractor
+
+ @Before
+ fun setUp() {
+ underTest = kosmos.volumeDialogSliderInteractor
+ }
+
+ @Test
+ fun settingStreamVolume_setsActiveStream() =
+ with(kosmos) {
+ testScope.runTest {
+ runCurrent()
+ // initialize the stream model
+ fakeVolumeDialogController.setStreamVolume(volumeDialogSliderType.audioStream, 0)
+
+ val sliderModel by collectLastValue(underTest.slider)
+ underTest.setStreamVolume(1)
+ runCurrent()
+
+ assertThat(sliderModel!!.isActive).isTrue()
+ }
+ }
+
+ @Test
+ fun streamVolumeIs_minMaxAreEnforced() =
+ with(kosmos) {
+ testScope.runTest {
+ runCurrent()
+ fakeVolumeDialogController.updateState {
+ states.put(
+ volumeDialogSliderType.audioStream,
+ VolumeDialogController.StreamState().apply {
+ levelMin = 0
+ level = 2
+ levelMax = 1
+ },
+ )
+ }
+
+ val sliderModel by collectLastValue(underTest.slider)
+ runCurrent()
+
+ assertThat(sliderModel!!.level).isEqualTo(1)
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
index 59676ce..111c232 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
@@ -25,9 +25,4 @@
override val wallpaperInfo = MutableStateFlow<WallpaperInfo?>(null)
override val wallpaperSupportsAmbientMode = MutableStateFlow(false)
override var rootView: View? = null
- private val _notificationStackAbsoluteBottom = MutableStateFlow(0F)
-
- override fun setNotificationStackAbsoluteBottom(bottom: Float) {
- _notificationStackAbsoluteBottom.value = bottom
- }
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
index f975c4f..e264264 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
@@ -182,7 +182,7 @@
interface ClockEvents {
@get:ProtectedReturn("return false;")
/** Set to enable or disable swipe interaction */
- var isReactiveTouchInteractionEnabled: Boolean
+ var isReactiveTouchInteractionEnabled: Boolean // TODO(b/364664388): Remove/Rename
/** Call whenever timezone changes */
fun onTimeZoneChanged(timeZone: TimeZone)
@@ -322,9 +322,6 @@
/** True if the clock will react to tone changes in the seed color */
val isReactiveToTone: Boolean = true,
- /** True if the clock is capable of changing style in reaction to touches */
- val isReactiveToTouch: Boolean = false,
-
/** Font axes that can be modified on this clock */
val axes: List<ClockReactiveAxis> = listOf(),
)
diff --git a/packages/SystemUI/plugin_core/AndroidManifest.xml b/packages/SystemUI/plugin_core/AndroidManifest.xml
deleted file mode 100644
index df835fd..0000000
--- a/packages/SystemUI/plugin_core/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2018 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.systemui.plugin_core">
-
- <uses-sdk
- android:minSdkVersion="28" />
-
-</manifest>
diff --git a/packages/SystemUI/res/drawable/notif_footer_btn_history.xml b/packages/SystemUI/res/drawable/notif_footer_btn_history.xml
new file mode 100644
index 0000000..0460a72
--- /dev/null
+++ b/packages/SystemUI/res/drawable/notif_footer_btn_history.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M480,840Q342,840 239.5,748.5Q137,657 122,520L204,520Q218,624 296.5,692Q375,760 480,760Q597,760 678.5,678.5Q760,597 760,480Q760,363 678.5,281.5Q597,200 480,200Q411,200 351,232Q291,264 250,320L360,320L360,400L120,400L120,160L200,160L200,254Q251,190 324.5,155Q398,120 480,120Q555,120 620.5,148.5Q686,177 734.5,225.5Q783,274 811.5,339.5Q840,405 840,480Q840,555 811.5,620.5Q783,686 734.5,734.5Q686,783 620.5,811.5Q555,840 480,840ZM592,648L440,496L440,280L520,280L520,464L648,592L592,648Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/notif_footer_btn_settings.xml b/packages/SystemUI/res/drawable/notif_footer_btn_settings.xml
new file mode 100644
index 0000000..800060d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/notif_footer_btn_settings.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M370,880L354,752Q341,747 329.5,740Q318,733 307,725L188,775L78,585L181,507Q180,500 180,493.5Q180,487 180,480Q180,473 180,466.5Q180,460 181,453L78,375L188,185L307,235Q318,227 330,220Q342,213 354,208L370,80L590,80L606,208Q619,213 630.5,220Q642,227 653,235L772,185L882,375L779,453Q780,460 780,466.5Q780,473 780,480Q780,487 780,493.5Q780,500 778,507L881,585L771,775L653,725Q642,733 630,740Q618,747 606,752L590,880L370,880ZM440,800L519,800L533,694Q564,686 590.5,670.5Q617,655 639,633L738,674L777,606L691,541Q696,527 698,511.5Q700,496 700,480Q700,464 698,448.5Q696,433 691,419L777,354L738,286L639,328Q617,305 590.5,289.5Q564,274 533,266L520,160L441,160L427,266Q396,274 369.5,289.5Q343,305 321,327L222,286L183,354L269,418Q264,433 262,448Q260,463 260,480Q260,496 262,511Q264,526 269,541L183,606L222,674L321,632Q343,655 369.5,670.5Q396,686 427,694L440,800ZM482,620Q540,620 581,579Q622,538 622,480Q622,422 581,381Q540,340 482,340Q423,340 382.5,381Q342,422 342,480Q342,538 382.5,579Q423,620 482,620ZM480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/volume_background_top.xml b/packages/SystemUI/res/drawable/volume_background_top.xml
index 3cd87fc..75849be 100644
--- a/packages/SystemUI/res/drawable/volume_background_top.xml
+++ b/packages/SystemUI/res/drawable/volume_background_top.xml
@@ -1,27 +1,26 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item>
<shape>
- <size android:width="@dimen/volume_dialog_panel_width" />
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners android:topLeftRadius="@dimen/volume_dialog_panel_width_half"
- android:topRightRadius="@dimen/volume_dialog_panel_width_half"/>
+ <size android:width="@dimen/volume_dialog_width" />
+ <solid android:color="?androidprv:attr/materialColorSurface" />
+ <corners android:topLeftRadius="@dimen/volume_dialog_background_corner_radius"
+ android:topRightRadius="@dimen/volume_dialog_background_corner_radius"/>
</shape>
</item>
-</layer-list>
\ No newline at end of file
+</layer-list>
diff --git a/packages/SystemUI/res/drawable/volume_background_top_legacy.xml b/packages/SystemUI/res/drawable/volume_background_top_legacy.xml
new file mode 100644
index 0000000..3cd87fc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/volume_background_top_legacy.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item>
+ <shape>
+ <size android:width="@dimen/volume_dialog_panel_width" />
+ <solid android:color="?androidprv:attr/colorSurface" />
+ <corners android:topLeftRadius="@dimen/volume_dialog_panel_width_half"
+ android:topRightRadius="@dimen/volume_dialog_panel_width_half"/>
+ </shape>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml b/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml
index 5e7cb12..df8521a 100644
--- a/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml
+++ b/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -15,10 +14,11 @@
~ limitations under the License.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:paddingMode="stack" >
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:paddingMode="stack" >
<size
- android:height="@dimen/volume_ringer_drawer_item_size"
- android:width="@dimen/volume_ringer_drawer_item_size" />
- <solid android:color="?android:attr/colorAccent" />
- <corners android:radius="@dimen/volume_ringer_drawer_item_size_half" />
+ android:height="@dimen/volume_ringer_item_size"
+ android:width="@dimen/volume_ringer_item_size" />
+ <solid android:color="?androidprv:attr/materialColorPrimary" />
+ <corners android:radius="360dp" />
</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/volume_drawer_selection_bg_legacy.xml b/packages/SystemUI/res/drawable/volume_drawer_selection_bg_legacy.xml
new file mode 100644
index 0000000..5e7cb12
--- /dev/null
+++ b/packages/SystemUI/res/drawable/volume_drawer_selection_bg_legacy.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:paddingMode="stack" >
+ <size
+ android:height="@dimen/volume_ringer_drawer_item_size"
+ android:width="@dimen/volume_ringer_drawer_item_size" />
+ <solid android:color="?android:attr/colorAccent" />
+ <corners android:radius="@dimen/volume_ringer_drawer_item_size_half" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/volume_ringer_item_bg.xml b/packages/SystemUI/res/drawable/volume_ringer_item_bg.xml
new file mode 100644
index 0000000..7ddd57b7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/volume_ringer_item_bg.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle" >
+ <size android:width="@dimen/volume_ringer_item_size" android:height="@dimen/volume_ringer_item_size"/>
+ <solid android:color="?androidprv:attr/materialColorSurfaceContainerHighest" />
+ <corners android:radius="@dimen/volume_ringer_item_radius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land-television/volume_dialog.xml b/packages/SystemUI/res/layout-land-television/volume_dialog.xml
deleted file mode 100644
index f77db95..0000000
--- a/packages/SystemUI/res/layout-land-television/volume_dialog.xml
+++ /dev/null
@@ -1,67 +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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/volume_dialog_container"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right"
- android:divider="@drawable/volume_dialog_floating_sliders_spacer"
- android:orientation="horizontal"
- android:showDividers="middle|end|beginning"
- android:theme="@style/volume_dialog_theme">
-
- <LinearLayout
- android:id="@+id/volume_dialog_floating_sliders_container"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:background="@drawable/volume_dialog_background"
- android:divider="@drawable/volume_dialog_floating_sliders_spacer"
- android:gravity="bottom"
- android:orientation="horizontal"
- android:paddingBottom="@dimen/volume_dialog_floating_sliders_bottom_padding"
- android:showDividers="middle" />
-
- <LinearLayout
- android:layout_width="@dimen/volume_dialog_width"
- android:layout_height="wrap_content"
- android:background="@drawable/volume_dialog_background"
- android:divider="@drawable/volume_dialog_spacer"
- android:gravity="center_horizontal"
- android:orientation="vertical"
- android:paddingVertical="@dimen/volume_dialog_vertical_padding"
- android:showDividers="middle">
-
- <FrameLayout
- android:id="@+id/volume_dialog_ringer_button"
- android:layout_width="@dimen/volume_dialog_button_size"
- android:layout_height="@dimen/volume_dialog_button_size" />
-
- <include
- android:id="@+id/volume_dialog_slider"
- layout="@layout/volume_dialog_slider" />
-
- <Button
- android:id="@+id/volume_dialog_settings"
- android:layout_width="@dimen/volume_dialog_button_size"
- android:layout_height="@dimen/volume_dialog_button_size"
- android:background="@drawable/ripple_drawable_20dp"
- android:contentDescription="@string/accessibility_volume_settings"
- android:soundEffectsEnabled="false"
- android:src="@drawable/horizontal_ellipsis"
- android:tint="?androidprv:attr/materialColorPrimary" />
- </LinearLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml
deleted file mode 100644
index f77db95..0000000
--- a/packages/SystemUI/res/layout-land/volume_dialog.xml
+++ /dev/null
@@ -1,67 +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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/volume_dialog_container"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right"
- android:divider="@drawable/volume_dialog_floating_sliders_spacer"
- android:orientation="horizontal"
- android:showDividers="middle|end|beginning"
- android:theme="@style/volume_dialog_theme">
-
- <LinearLayout
- android:id="@+id/volume_dialog_floating_sliders_container"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:background="@drawable/volume_dialog_background"
- android:divider="@drawable/volume_dialog_floating_sliders_spacer"
- android:gravity="bottom"
- android:orientation="horizontal"
- android:paddingBottom="@dimen/volume_dialog_floating_sliders_bottom_padding"
- android:showDividers="middle" />
-
- <LinearLayout
- android:layout_width="@dimen/volume_dialog_width"
- android:layout_height="wrap_content"
- android:background="@drawable/volume_dialog_background"
- android:divider="@drawable/volume_dialog_spacer"
- android:gravity="center_horizontal"
- android:orientation="vertical"
- android:paddingVertical="@dimen/volume_dialog_vertical_padding"
- android:showDividers="middle">
-
- <FrameLayout
- android:id="@+id/volume_dialog_ringer_button"
- android:layout_width="@dimen/volume_dialog_button_size"
- android:layout_height="@dimen/volume_dialog_button_size" />
-
- <include
- android:id="@+id/volume_dialog_slider"
- layout="@layout/volume_dialog_slider" />
-
- <Button
- android:id="@+id/volume_dialog_settings"
- android:layout_width="@dimen/volume_dialog_button_size"
- android:layout_height="@dimen/volume_dialog_button_size"
- android:background="@drawable/ripple_drawable_20dp"
- android:contentDescription="@string/accessibility_volume_settings"
- android:soundEffectsEnabled="false"
- android:src="@drawable/horizontal_ellipsis"
- android:tint="?androidprv:attr/materialColorPrimary" />
- </LinearLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/volume_dialog_legacy.xml b/packages/SystemUI/res/layout-land/volume_dialog_legacy.xml
index 08edf59..de346db 100644
--- a/packages/SystemUI/res/layout-land/volume_dialog_legacy.xml
+++ b/packages/SystemUI/res/layout-land/volume_dialog_legacy.xml
@@ -44,7 +44,7 @@
android:clipChildren="false"
android:gravity="right">
- <include layout="@layout/volume_ringer_drawer" />
+ <include layout="@layout/volume_ringer_drawer_legacy" />
<FrameLayout
android:visibility="gone"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer_redesign.xml b/packages/SystemUI/res/layout/status_bar_notification_footer_redesign.xml
new file mode 100644
index 0000000..7c59aad
--- /dev/null
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer_redesign.xml
@@ -0,0 +1,82 @@
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<!-- Extends Framelayout -->
+<com.android.systemui.statusbar.notification.footer.ui.view.FooterView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone">
+
+ <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="12dp">
+
+ <TextView
+ android:id="@+id/unlock_prompt_footer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:drawablePadding="8dp"
+ android:gravity="center"
+ android:text="@string/unlock_to_see_notif_text"
+ android:textAppearance="?android:attr/textAppearanceButton"
+ android:visibility="gone" />
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <com.android.systemui.statusbar.notification.row.FooterViewButton
+ android:id="@+id/settings_button"
+ style="@style/TextAppearance.NotificationFooterButtonRedesign"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:background="@drawable/notif_footer_btn_background"
+ android:contentDescription="@string/notification_settings_button_description"
+ android:drawableStart="@drawable/notif_footer_btn_settings"
+ android:focusable="true"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <com.android.systemui.statusbar.notification.row.FooterViewButton
+ android:id="@+id/dismiss_text"
+ style="@style/TextAppearance.NotificationFooterButtonRedesign"
+ android:layout_width="0dp"
+ android:layout_height="48dp"
+ android:layout_marginHorizontal="8dp"
+ android:background="@drawable/notif_footer_btn_background"
+ android:contentDescription="@string/accessibility_clear_all"
+ android:focusable="true"
+ android:text="@string/clear_all_notifications_text"
+ app:layout_constraintEnd_toStartOf="@id/history_button"
+ app:layout_constraintStart_toEndOf="@id/settings_button" />
+
+ <com.android.systemui.statusbar.notification.row.FooterViewButton
+ android:id="@+id/history_button"
+ style="@style/TextAppearance.NotificationFooterButtonRedesign"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:background="@drawable/notif_footer_btn_background"
+ android:contentDescription="@string/notification_history_button_description"
+ android:drawableStart="@drawable/notif_footer_btn_history"
+ android:focusable="true"
+ app:layout_constraintEnd_toEndOf="parent" />
+ </androidx.constraintlayout.widget.ConstraintLayout>
+ </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
+</com.android.systemui.statusbar.notification.footer.ui.view.FooterView>
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index f77db95..8b39e5e 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -28,7 +28,6 @@
android:id="@+id/volume_dialog_floating_sliders_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:background="@drawable/volume_dialog_background"
android:divider="@drawable/volume_dialog_floating_sliders_spacer"
android:gravity="bottom"
android:orientation="horizontal"
@@ -36,6 +35,7 @@
android:showDividers="middle" />
<LinearLayout
+ android:id="@+id/volume_dialog"
android:layout_width="@dimen/volume_dialog_width"
android:layout_height="wrap_content"
android:background="@drawable/volume_dialog_background"
@@ -45,16 +45,11 @@
android:paddingVertical="@dimen/volume_dialog_vertical_padding"
android:showDividers="middle">
- <FrameLayout
- android:id="@+id/volume_dialog_ringer_button"
- android:layout_width="@dimen/volume_dialog_button_size"
- android:layout_height="@dimen/volume_dialog_button_size" />
+ <include layout="@layout/volume_ringer_drawer" />
- <include
- android:id="@+id/volume_dialog_slider"
- layout="@layout/volume_dialog_slider" />
+ <include layout="@layout/volume_dialog_slider" />
- <Button
+ <ImageButton
android:id="@+id/volume_dialog_settings"
android:layout_width="@dimen/volume_dialog_button_size"
android:layout_height="@dimen/volume_dialog_button_size"
diff --git a/packages/SystemUI/res/layout/volume_dialog_legacy.xml b/packages/SystemUI/res/layout/volume_dialog_legacy.xml
index 39a1f1f..9010ab7 100644
--- a/packages/SystemUI/res/layout/volume_dialog_legacy.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_legacy.xml
@@ -45,7 +45,7 @@
android:orientation="vertical"
android:gravity="right">
- <include layout="@layout/volume_ringer_drawer" />
+ <include layout="@layout/volume_ringer_drawer_legacy" />
<FrameLayout
android:visibility="gone"
diff --git a/packages/SystemUI/res/layout/volume_ringer_drawer.xml b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
index 8f1e061..94b55b1 100644
--- a/packages/SystemUI/res/layout/volume_ringer_drawer.xml
+++ b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
@@ -1,35 +1,32 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2021 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
- 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.
--->
-
-<!-- Contains the active ringer icon and a hidden drawer containing all three ringer options. -->
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/volume_ringer_and_drawer_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
- android:paddingLeft="@dimen/volume_dialog_ringer_rows_padding"
- android:paddingTop="@dimen/volume_dialog_ringer_rows_padding"
- android:paddingRight="@dimen/volume_dialog_ringer_rows_padding"
- android:paddingBottom="@dimen/volume_dialog_ringer_rows_padding"
- android:background="@drawable/volume_background_top"
+ android:paddingLeft="@dimen/volume_dialog_ringer_container_padding"
+ android:paddingTop="@dimen/volume_dialog_ringer_container_padding"
+ android:paddingRight="@dimen/volume_dialog_ringer_container_padding"
android:layoutDirection="ltr"
android:clipToPadding="false"
- android:clipChildren="false">
+ android:clipChildren="false"
+ android:background="@drawable/volume_background_top">
<!-- Drawer view, invisible by default. -->
<FrameLayout
@@ -37,15 +34,15 @@
android:alpha="0.0"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@drawable/volume_drawer_bg"
android:orientation="vertical">
<!-- View that is animated to a tapped ringer selection, so it appears selected. -->
<FrameLayout
android:id="@+id/volume_drawer_selection_background"
android:alpha="0.0"
- android:layout_width="@dimen/volume_ringer_drawer_item_size"
- android:layout_height="@dimen/volume_ringer_drawer_item_size"
+ android:layout_width="@dimen/volume_ringer_item_size"
+ android:layout_marginBottom="8dp"
+ android:layout_height="@dimen/volume_ringer_item_size"
android:layout_gravity="bottom|right"
android:background="@drawable/volume_drawer_selection_bg" />
@@ -57,54 +54,60 @@
<FrameLayout
android:id="@+id/volume_drawer_vibrate"
- android:layout_width="@dimen/volume_ringer_drawer_item_size"
- android:layout_height="@dimen/volume_ringer_drawer_item_size"
+ android:layout_width="@dimen/volume_ringer_item_size"
+ android:layout_height="@dimen/volume_ringer_item_size"
+ android:layout_marginBottom="8dp"
android:contentDescription="@string/volume_ringer_hint_vibrate"
- android:gravity="center">
+ android:gravity="center"
+ android:background="@drawable/volume_ringer_item_bg">
<ImageView
android:id="@+id/volume_drawer_vibrate_icon"
- android:layout_width="@dimen/volume_ringer_drawer_icon_size"
- android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+ android:layout_width="@dimen/volume_ringer_icon_size"
+ android:layout_height="@dimen/volume_ringer_icon_size"
android:layout_gravity="center"
android:src="@drawable/ic_volume_ringer_vibrate"
- android:tint="?android:attr/textColorPrimary" />
+ android:tint="?androidprv:attr/materialColorOnSurface" />
</FrameLayout>
<FrameLayout
android:id="@+id/volume_drawer_mute"
- android:layout_width="@dimen/volume_ringer_drawer_item_size"
- android:layout_height="@dimen/volume_ringer_drawer_item_size"
+ android:layout_width="@dimen/volume_ringer_item_size"
+ android:layout_height="@dimen/volume_ringer_item_size"
+ android:layout_marginBottom="8dp"
android:accessibilityTraversalAfter="@id/volume_drawer_vibrate"
android:contentDescription="@string/volume_ringer_hint_mute"
- android:gravity="center">
+ android:gravity="center"
+ android:background="@drawable/volume_ringer_item_bg">
<ImageView
android:id="@+id/volume_drawer_mute_icon"
- android:layout_width="@dimen/volume_ringer_drawer_icon_size"
- android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+ android:layout_width="@dimen/volume_ringer_icon_size"
+ android:layout_height="@dimen/volume_ringer_icon_size"
android:layout_gravity="center"
android:src="@drawable/ic_speaker_mute"
- android:tint="?android:attr/textColorPrimary" />
+ android:tint="?androidprv:attr/materialColorOnSurface" />
</FrameLayout>
<FrameLayout
android:id="@+id/volume_drawer_normal"
- android:layout_width="@dimen/volume_ringer_drawer_item_size"
- android:layout_height="@dimen/volume_ringer_drawer_item_size"
+ android:layout_width="@dimen/volume_ringer_item_size"
+ android:layout_height="@dimen/volume_ringer_item_size"
+ android:layout_marginBottom="8dp"
android:accessibilityTraversalAfter="@id/volume_drawer_mute"
android:contentDescription="@string/volume_ringer_hint_unmute"
- android:gravity="center">
+ android:gravity="center"
+ android:background="@drawable/volume_ringer_item_bg">
<ImageView
android:id="@+id/volume_drawer_normal_icon"
- android:layout_width="@dimen/volume_ringer_drawer_icon_size"
- android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+ android:layout_width="@dimen/volume_ringer_icon_size"
+ android:layout_height="@dimen/volume_ringer_icon_size"
android:layout_gravity="center"
android:src="@drawable/ic_speaker_on"
- android:tint="?android:attr/textColorPrimary" />
+ android:tint="?androidprv:attr/materialColorOnSurface" />
</FrameLayout>
@@ -116,18 +119,19 @@
position in the drawer. When the drawer is closed, it animates back. -->
<FrameLayout
android:id="@+id/volume_new_ringer_active_icon_container"
- android:layout_width="@dimen/volume_ringer_drawer_item_size"
- android:layout_height="@dimen/volume_ringer_drawer_item_size"
+ android:layout_width="@dimen/volume_ringer_item_size"
+ android:layout_height="@dimen/volume_ringer_item_size"
+ android:layout_marginBottom="8dp"
android:layout_gravity="bottom|right"
android:contentDescription="@string/volume_ringer_change"
android:background="@drawable/volume_drawer_selection_bg">
<ImageView
android:id="@+id/volume_new_ringer_active_icon"
- android:layout_width="@dimen/volume_ringer_drawer_icon_size"
- android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+ android:layout_width="@dimen/volume_ringer_icon_size"
+ android:layout_height="@dimen/volume_ringer_icon_size"
android:layout_gravity="center"
- android:tint="?android:attr/textColorPrimaryInverse"
+ android:tint="?androidprv:attr/materialColorOnPrimary"
android:src="@drawable/ic_volume_media" />
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/volume_ringer_drawer_legacy.xml b/packages/SystemUI/res/layout/volume_ringer_drawer_legacy.xml
new file mode 100644
index 0000000..0efbc6d
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_ringer_drawer_legacy.xml
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Contains the active ringer icon and a hidden drawer containing all three ringer options. -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/volume_ringer_and_drawer_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:paddingLeft="@dimen/volume_dialog_ringer_rows_padding"
+ android:paddingTop="@dimen/volume_dialog_ringer_rows_padding"
+ android:paddingRight="@dimen/volume_dialog_ringer_rows_padding"
+ android:paddingBottom="@dimen/volume_dialog_ringer_rows_padding"
+ android:background="@drawable/volume_background_top_legacy"
+ android:layoutDirection="ltr"
+ android:clipToPadding="false"
+ android:clipChildren="false">
+
+ <!-- Drawer view, invisible by default. -->
+ <FrameLayout
+ android:id="@+id/volume_drawer_container"
+ android:alpha="0.0"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/volume_drawer_bg"
+ android:orientation="vertical">
+
+ <!-- View that is animated to a tapped ringer selection, so it appears selected. -->
+ <FrameLayout
+ android:id="@+id/volume_drawer_selection_background"
+ android:alpha="0.0"
+ android:layout_width="@dimen/volume_ringer_drawer_item_size"
+ android:layout_height="@dimen/volume_ringer_drawer_item_size"
+ android:layout_gravity="bottom|right"
+ android:background="@drawable/volume_drawer_selection_bg_legacy" />
+
+ <LinearLayout
+ android:id="@+id/volume_drawer_options"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <FrameLayout
+ android:id="@+id/volume_drawer_vibrate"
+ android:layout_width="@dimen/volume_ringer_drawer_item_size"
+ android:layout_height="@dimen/volume_ringer_drawer_item_size"
+ android:contentDescription="@string/volume_ringer_hint_vibrate"
+ android:gravity="center">
+
+ <ImageView
+ android:id="@+id/volume_drawer_vibrate_icon"
+ android:layout_width="@dimen/volume_ringer_drawer_icon_size"
+ android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+ android:layout_gravity="center"
+ android:src="@drawable/ic_volume_ringer_vibrate"
+ android:tint="?android:attr/textColorPrimary" />
+
+ </FrameLayout>
+
+ <FrameLayout
+ android:id="@+id/volume_drawer_mute"
+ android:layout_width="@dimen/volume_ringer_drawer_item_size"
+ android:layout_height="@dimen/volume_ringer_drawer_item_size"
+ android:accessibilityTraversalAfter="@id/volume_drawer_vibrate"
+ android:contentDescription="@string/volume_ringer_hint_mute"
+ android:gravity="center">
+
+ <ImageView
+ android:id="@+id/volume_drawer_mute_icon"
+ android:layout_width="@dimen/volume_ringer_drawer_icon_size"
+ android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+ android:layout_gravity="center"
+ android:src="@drawable/ic_speaker_mute"
+ android:tint="?android:attr/textColorPrimary" />
+
+ </FrameLayout>
+
+ <FrameLayout
+ android:id="@+id/volume_drawer_normal"
+ android:layout_width="@dimen/volume_ringer_drawer_item_size"
+ android:layout_height="@dimen/volume_ringer_drawer_item_size"
+ android:accessibilityTraversalAfter="@id/volume_drawer_mute"
+ android:contentDescription="@string/volume_ringer_hint_unmute"
+ android:gravity="center">
+
+ <ImageView
+ android:id="@+id/volume_drawer_normal_icon"
+ android:layout_width="@dimen/volume_ringer_drawer_icon_size"
+ android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+ android:layout_gravity="center"
+ android:src="@drawable/ic_speaker_on"
+ android:tint="?android:attr/textColorPrimary" />
+
+ </FrameLayout>
+
+ </LinearLayout>
+
+ </FrameLayout>
+
+ <!-- The current ringer selection. When the drawer is opened, this animates to the corresponding
+ position in the drawer. When the drawer is closed, it animates back. -->
+ <FrameLayout
+ android:id="@+id/volume_new_ringer_active_icon_container"
+ android:layout_width="@dimen/volume_ringer_drawer_item_size"
+ android:layout_height="@dimen/volume_ringer_drawer_item_size"
+ android:layout_gravity="bottom|right"
+ android:contentDescription="@string/volume_ringer_change"
+ android:background="@drawable/volume_drawer_selection_bg_legacy">
+
+ <ImageView
+ android:id="@+id/volume_new_ringer_active_icon"
+ android:layout_width="@dimen/volume_ringer_drawer_icon_size"
+ android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+ android:layout_gravity="center"
+ android:tint="?android:attr/textColorPrimaryInverse"
+ android:src="@drawable/ic_volume_media" />
+
+ </FrameLayout>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 87e6aa0..c2d942f 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -2064,5 +2064,11 @@
<dimen name="volume_panel_slice_vertical_padding">8dp</dimen>
<dimen name="volume_panel_slice_horizontal_padding">24dp</dimen>
+
+ <dimen name="volume_dialog_ringer_container_padding">10dp</dimen>
+ <dimen name="volume_ringer_item_size">40dp</dimen>
+ <dimen name="volume_ringer_icon_size">20dp</dimen>
+ <dimen name="volume_ringer_item_radius">12dp</dimen>
+ <dimen name="volume_dialog_ringer_item_margin_bottom">8dp</dimen>
<!-- Volume end -->
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c494e85..0aa5ccf 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1463,6 +1463,12 @@
<!-- The text for the notification history link. [CHAR LIMIT=40] -->
<string name="manage_notifications_history_text">History</string>
+ <!-- The accessibility description for the notification settings button in the notification shade. -->
+ <string name="notification_settings_button_description">Notification settings</string>
+
+ <!-- The accessibility description for the notification history button in the notification shade. -->
+ <string name="notification_history_button_description">Notification history</string>
+
<!-- Section title for notifications that have recently appeared. [CHAR LIMIT=40] -->
<string name="notification_section_header_incoming">New</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 1ab9242..ab45b9f 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -785,6 +785,16 @@
<item name="android:minWidth">0dp</item>
</style>
+ <style
+ name="TextAppearance.NotificationFooterButtonRedesign"
+ parent="@android:style/Widget.DeviceDefault.Button.Borderless">
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:drawableTint">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textAllCaps">false</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:minWidth">0dp</item>
+ </style>
+
<style name="TextAppearance.HeadsUpStatusBarText"
parent="@*android:style/TextAppearance.DeviceDefault.Notification.Info">
</style>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index b3ea75d..8b2cf7a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -80,6 +80,7 @@
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
import android.hardware.usb.UsbManager;
import android.nfc.NfcAdapter;
+import android.os.BatteryManager;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Looper;
@@ -345,9 +346,9 @@
// Device provisioning state
private boolean mDeviceProvisioned;
- // Battery status
+ // Battery status (null until first update is received)
@VisibleForTesting
- BatteryStatus mBatteryStatus;
+ BatteryStatus mBatteryStatus = null;
@VisibleForTesting
boolean mIncompatibleCharger;
@@ -2367,9 +2368,27 @@
watchForDeviceProvisioning();
}
- // Take a guess at initial SIM state, battery status and PLMN until we get an update
- mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, /* level= */ -1, /* plugged= */
- 0, CHARGING_POLICY_DEFAULT, /* maxChargingWattage= */0, /* present= */true);
+ // Request the initial battery level
+ mBackgroundExecutor.execute(() -> {
+ BatteryManager batteryManager = mContext.getSystemService(BatteryManager.class);
+ int level = -1;
+ if (batteryManager != null) {
+ int capacity = batteryManager.getIntProperty(
+ BatteryManager.BATTERY_PROPERTY_CAPACITY);
+ if (capacity >= 0 && capacity <= 100) {
+ level = capacity;
+ }
+ }
+ // Don't override if a valid battery status update has come in
+ final BatteryStatus status = new BatteryStatus(BATTERY_STATUS_UNKNOWN,
+ /* level= */ level, /* plugged= */ 0, CHARGING_POLICY_DEFAULT,
+ /* maxChargingWattage= */0, /* present= */true);
+ mMainExecutor.execute(() -> {
+ if (mBatteryStatus == null) {
+ handleBatteryUpdate(status);
+ }
+ });
+ });
// Watch for interesting updates
final IntentFilter filter = new IntentFilter();
@@ -3591,6 +3610,9 @@
}
private boolean isBatteryUpdateInteresting(BatteryStatus old, BatteryStatus current) {
+ if (old == null) {
+ return true;
+ }
final boolean nowPluggedIn = current.isPluggedIn();
final boolean wasPluggedIn = old.isPluggedIn();
final boolean stateChangedWhilePluggedIn = wasPluggedIn && nowPluggedIn
@@ -3687,7 +3709,9 @@
private void sendUpdates(KeyguardUpdateMonitorCallback callback) {
// Notify listener of the current state
- callback.onRefreshBatteryInfo(mBatteryStatus);
+ if (mBatteryStatus != null) {
+ callback.onRefreshBatteryInfo(mBatteryStatus);
+ }
callback.onTimeChanged();
callback.onPhoneStateChanged(mPhoneState);
callback.onRefreshCarrierInfo();
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt
index 7b3d337..223a21d 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt
@@ -38,9 +38,6 @@
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.shade.ShadeExpansionChangeEvent
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.phone.CentralSurfaces
@@ -48,7 +45,6 @@
import java.util.Optional
import javax.inject.Inject
import javax.inject.Named
-import javax.inject.Provider
import kotlin.math.abs
import kotlin.math.hypot
import kotlin.math.max
@@ -78,8 +74,6 @@
private val uiEventLogger: UiEventLogger,
private val activityStarter: ActivityStarter,
private val keyguardInteractor: KeyguardInteractor,
- private val sceneInteractor: SceneInteractor,
- private val windowRootViewProvider: Optional<Provider<WindowRootView>>,
) : TouchHandler {
/** An interface for creating ValueAnimators. */
interface ValueAnimatorCreator {
@@ -106,8 +100,6 @@
currentScrimController = controller
}
- private val windowRootView by lazy { windowRootViewProvider.get().get() }
-
/** Determines whether the touch handler should process touches in fullscreen swiping mode */
private var touchAvailable = false
@@ -117,7 +109,7 @@
e1: MotionEvent?,
e2: MotionEvent,
distanceX: Float,
- distanceY: Float,
+ distanceY: Float
): Boolean {
if (capture == null) {
capture =
@@ -136,11 +128,6 @@
expanded = false
// Since the user is dragging the bouncer up, set scrimmed to false.
currentScrimController?.show()
-
- if (SceneContainerFlag.isEnabled) {
- sceneInteractor.onRemoteUserInputStarted("bouncer touch handler")
- e1?.apply { windowRootView.dispatchTouchEvent(e1) }
- }
}
}
if (capture != true) {
@@ -165,27 +152,20 @@
/* cancelAction= */ null,
/* dismissShade= */ true,
/* afterKeyguardGone= */ true,
- /* deferred= */ false,
+ /* deferred= */ false
)
return true
}
- if (SceneContainerFlag.isEnabled) {
- windowRootView.dispatchTouchEvent(e2)
- } else {
- // For consistency, we adopt the expansion definition found in the
- // PanelViewController. In this case, expansion refers to the view above the
- // bouncer. As that view's expansion shrinks, the bouncer appears. The
- // bouncer
- // is fully hidden at full expansion (1) and fully visible when fully
- // collapsed
- // (0).
- touchSession?.apply {
- val screenTravelPercentage =
- (abs((this@outer.y - e2.y).toDouble()) / getBounds().height())
- .toFloat()
- setPanelExpansion(1 - screenTravelPercentage)
- }
+ // For consistency, we adopt the expansion definition found in the
+ // PanelViewController. In this case, expansion refers to the view above the
+ // bouncer. As that view's expansion shrinks, the bouncer appears. The bouncer
+ // is fully hidden at full expansion (1) and fully visible when fully collapsed
+ // (0).
+ touchSession?.apply {
+ val screenTravelPercentage =
+ (abs((this@outer.y - e2.y).toDouble()) / getBounds().height()).toFloat()
+ setPanelExpansion(1 - screenTravelPercentage)
}
}
@@ -214,7 +194,7 @@
ShadeExpansionChangeEvent(
/* fraction= */ currentExpansion,
/* expanded= */ expanded,
- /* tracking= */ true,
+ /* tracking= */ true
)
currentScrimController?.expand(event)
}
@@ -367,7 +347,7 @@
currentHeight,
targetHeight,
velocity,
- viewHeight,
+ viewHeight
)
} else {
// Shows the bouncer, i.e., fully collapses the space above the bouncer.
@@ -376,7 +356,7 @@
currentHeight,
targetHeight,
velocity,
- viewHeight,
+ viewHeight
)
}
animator.start()
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.kt b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.kt
index 05a1009..1951a23 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.kt
@@ -27,15 +27,11 @@
import com.android.systemui.ambient.touch.dagger.ShadeModule
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.statusbar.phone.CentralSurfaces
import java.util.Optional
import javax.inject.Inject
import javax.inject.Named
-import javax.inject.Provider
import kotlin.math.abs
import kotlinx.coroutines.CoroutineScope
import com.android.app.tracing.coroutines.launchTraced as launch
@@ -53,10 +49,8 @@
private val dreamManager: DreamManager,
private val communalViewModel: CommunalViewModel,
private val communalSettingsInteractor: CommunalSettingsInteractor,
- private val sceneInteractor: SceneInteractor,
- private val windowRootViewProvider: Optional<Provider<WindowRootView>>,
@param:Named(ShadeModule.NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT)
- private val initiationHeight: Int,
+ private val initiationHeight: Int
) : TouchHandler {
/**
* Tracks whether or not we are capturing a given touch. Will be null before and after a touch.
@@ -66,8 +60,6 @@
/** Determines whether the touch handler should process touches in fullscreen swiping mode */
private var touchAvailable = false
- private val windowRootView by lazy { windowRootViewProvider.get().get() }
-
init {
if (Flags.hubmodeFullscreenVerticalSwipeFix()) {
scope.launch {
@@ -108,7 +100,7 @@
e1: MotionEvent?,
e2: MotionEvent,
distanceX: Float,
- distanceY: Float,
+ distanceY: Float
): Boolean {
if (capture == null) {
// Only capture swipes that are going downwards.
@@ -118,10 +110,6 @@
if (Flags.hubmodeFullscreenVerticalSwipeFix()) touchAvailable
else true
if (capture == true) {
- if (SceneContainerFlag.isEnabled) {
- sceneInteractor.onRemoteUserInputStarted("shade touch handler")
- }
-
// Send the initial touches over, as the input listener has already
// processed these touches.
e1?.apply { sendTouchEvent(this) }
@@ -135,7 +123,7 @@
e1: MotionEvent?,
e2: MotionEvent,
velocityX: Float,
- velocityY: Float,
+ velocityY: Float
): Boolean {
return capture == true
}
@@ -144,11 +132,6 @@
}
private fun sendTouchEvent(event: MotionEvent) {
- if (SceneContainerFlag.isEnabled) {
- windowRootView.dispatchTouchEvent(event)
- return
- }
-
if (communalSettingsInteractor.isCommunalFlagEnabled() && !dreamManager.isDreaming) {
// Send touches to central surfaces only when on the glanceable hub while not dreaming.
// While sending touches where while dreaming will open the shade, the shade
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/ShadeModule.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/ShadeModule.java
index 1c781d6..bc2f354 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/ShadeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/ShadeModule.java
@@ -22,10 +22,8 @@
import com.android.systemui.ambient.touch.TouchHandler;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.res.R;
-import com.android.systemui.scene.ui.view.WindowRootView;
import dagger.Binds;
-import dagger.BindsOptionalOf;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoSet;
@@ -53,13 +51,6 @@
ShadeTouchHandler touchHandler);
/**
- * Window root view is used to send touches to the scene container. Declaring as optional as it
- * may not be present on all SysUI variants.
- */
- @BindsOptionalOf
- abstract WindowRootView bindWindowRootView();
-
- /**
* Provides the height of the gesture area for notification swipe down.
*/
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
index 7b8c19c..ec55401 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
@@ -73,7 +73,7 @@
private var progressJob: Job? = null
private val currentToState: KeyguardState
- get() = internalTransitionInteractor.currentTransitionInfoInternal.value.to
+ get() = internalTransitionInteractor.currentTransitionInfoInternal().to
/**
* The next keyguard state to trigger when exiting [CommunalScenes.Communal]. This is only used
@@ -197,7 +197,7 @@
val newTransition =
TransitionInfo(
ownerName = this::class.java.simpleName,
- from = internalTransitionInteractor.currentTransitionInfoInternal.value.to,
+ from = internalTransitionInteractor.currentTransitionInfoInternal().to,
to = state,
animator = null,
modeOnCanceled = TransitionModeOnCanceled.REVERSE,
@@ -273,7 +273,7 @@
}
private suspend fun startTransitionToGlanceableHub() {
- val currentState = internalTransitionInteractor.currentTransitionInfoInternal.value.to
+ val currentState = internalTransitionInteractor.currentTransitionInfoInternal().to
val newTransition =
TransitionInfo(
ownerName = this::class.java.simpleName,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 1ffbbd2..7a6ca08 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -65,6 +65,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.navigationbar.gestural.domain.GestureInteractor;
import com.android.systemui.navigationbar.gestural.domain.TaskMatcher;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.touch.TouchInsetManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -502,10 +503,10 @@
mDreamOverlayContainerViewController =
dreamOverlayComponent.getDreamOverlayContainerViewController();
- // Touch monitor are also used with SceneContainer. See individual touch handlers for
- // handling of SceneContainer.
- mTouchMonitor = ambientTouchComponent.getTouchMonitor();
- mTouchMonitor.init();
+ if (!SceneContainerFlag.isEnabled()) {
+ mTouchMonitor = ambientTouchComponent.getTouchMonitor();
+ mTouchMonitor.init();
+ }
mStateController.setShouldShowComplications(shouldShowComplications());
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
index 85fb90d..12984efb 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -30,10 +30,8 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.DreamOverlayContainerView;
import com.android.systemui.res.R;
-import com.android.systemui.scene.ui.view.WindowRootView;
import com.android.systemui.touch.TouchInsetManager;
-import dagger.BindsOptionalOf;
import dagger.Module;
import dagger.Provides;
@@ -56,13 +54,6 @@
public static final String DREAM_IN_TRANSLATION_Y_DURATION =
"dream_in_complications_translation_y_duration";
- /**
- * Window root view is used to send touches to the scene container. Declaring as optional as it
- * may not be present on all SysUI variants.
- */
- @BindsOptionalOf
- abstract WindowRootView bindWindowRootView();
-
/** */
@Provides
@DreamOverlayComponent.DreamOverlayScope
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
index 42a6877..5ba780f 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
@@ -31,9 +31,6 @@
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor;
import com.android.systemui.communal.domain.interactor.CommunalInteractor;
import com.android.systemui.dreams.touch.dagger.CommunalTouchModule;
-import com.android.systemui.scene.domain.interactor.SceneInteractor;
-import com.android.systemui.scene.shared.flag.SceneContainerFlag;
-import com.android.systemui.scene.ui.view.WindowRootView;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import kotlinx.coroutines.Job;
@@ -45,7 +42,6 @@
import javax.inject.Inject;
import javax.inject.Named;
-import javax.inject.Provider;
/** {@link TouchHandler} responsible for handling touches to open communal hub. **/
public class CommunalTouchHandler implements TouchHandler {
@@ -55,8 +51,6 @@
private final CommunalInteractor mCommunalInteractor;
private final ConfigurationInteractor mConfigurationInteractor;
- private final SceneInteractor mSceneInteractor;
- private final WindowRootView mWindowRootView;
private Boolean mIsEnabled = false;
private ArrayList<Job> mFlows = new ArrayList<>();
@@ -75,16 +69,12 @@
@Named(CommunalTouchModule.COMMUNAL_GESTURE_INITIATION_WIDTH) int initiationWidth,
CommunalInteractor communalInteractor,
ConfigurationInteractor configurationInteractor,
- SceneInteractor sceneInteractor,
- Optional<Provider<WindowRootView>> windowRootViewProvider,
Lifecycle lifecycle) {
mInitiationWidth = initiationWidth;
mCentralSurfaces = centralSurfaces;
mLifecycle = lifecycle;
mCommunalInteractor = communalInteractor;
mConfigurationInteractor = configurationInteractor;
- mSceneInteractor = sceneInteractor;
- mWindowRootView = windowRootViewProvider.get().get();
mFlows.add(collectFlow(
mLifecycle,
@@ -135,15 +125,8 @@
private void handleSessionStart(CentralSurfaces surfaces, TouchSession session) {
// Notification shade window has its own logic to be visible if the hub is open, no need to
// do anything here other than send touch events over.
- if (SceneContainerFlag.isEnabled()) {
- mSceneInteractor.onRemoteUserInputStarted("communal touch handler");
- }
session.registerInputListener(ev -> {
- if (SceneContainerFlag.isEnabled()) {
- mWindowRootView.dispatchTouchEvent((MotionEvent) ev);
- } else {
- surfaces.handleCommunalHubTouch((MotionEvent) ev);
- }
+ surfaces.handleCommunalHubTouch((MotionEvent) ev);
if (ev != null && ((MotionEvent) ev).getAction() == MotionEvent.ACTION_UP) {
var unused = session.pop();
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 67625d0..32c2bc7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -81,6 +81,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardLockWhileAwakeInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardStateCallbackInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardWakeDirectlyToGoneInteractor;
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier;
@@ -329,6 +330,8 @@
return new FoldGracePeriodProvider();
}
};
+ private final KeyguardLockWhileAwakeInteractor
+ mKeyguardLockWhileAwakeInteractor;
@Inject
public KeyguardService(
@@ -353,7 +356,8 @@
KeyguardWakeDirectlyToGoneInteractor keyguardWakeDirectlyToGoneInteractor,
KeyguardDismissInteractor keyguardDismissInteractor,
Lazy<DeviceEntryInteractor> deviceEntryInteractorLazy,
- KeyguardStateCallbackInteractor keyguardStateCallbackInteractor) {
+ KeyguardStateCallbackInteractor keyguardStateCallbackInteractor,
+ KeyguardLockWhileAwakeInteractor keyguardLockWhileAwakeInteractor) {
super();
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
@@ -385,6 +389,7 @@
mKeyguardEnabledInteractor = keyguardEnabledInteractor;
mKeyguardWakeDirectlyToGoneInteractor = keyguardWakeDirectlyToGoneInteractor;
mKeyguardDismissInteractor = keyguardDismissInteractor;
+ mKeyguardLockWhileAwakeInteractor = keyguardLockWhileAwakeInteractor;
}
@Override
@@ -656,6 +661,11 @@
public void doKeyguardTimeout(Bundle options) {
trace("doKeyguardTimeout");
checkPermission();
+
+ if (KeyguardWmStateRefactor.isEnabled()) {
+ mKeyguardLockWhileAwakeInteractor.onKeyguardServiceDoKeyguardTimeout(options);
+ }
+
mKeyguardViewMediator.doKeyguardTimeout(options);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index bbaa9eb..d3c17cc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -269,6 +269,8 @@
/** The top of shortcut in screen, used by wallpaper to find remaining space in lockscreen */
val shortcutAbsoluteTop: StateFlow<Float>
+ val notificationStackAbsoluteBottom: StateFlow<Float>
+
/**
* Returns `true` if the keyguard is showing; `false` otherwise.
*
@@ -339,6 +341,12 @@
fun isShowKeyguardWhenReenabled(): Boolean
fun setShortcutAbsoluteTop(top: Float)
+
+ /**
+ * Set bottom of notifications from notification stack, and Magic Portrait will layout base on
+ * this value
+ */
+ fun setNotificationStackAbsoluteBottom(bottom: Float)
}
/** Encapsulates application state for the keyguard. */
@@ -635,6 +643,9 @@
private val _shortcutAbsoluteTop = MutableStateFlow(0F)
override val shortcutAbsoluteTop = _shortcutAbsoluteTop.asStateFlow()
+ private val _notificationStackAbsoluteBottom = MutableStateFlow(0F)
+ override val notificationStackAbsoluteBottom = _notificationStackAbsoluteBottom.asStateFlow()
+
init {
val callback =
object : KeyguardStateController.Callback {
@@ -717,6 +728,10 @@
_shortcutAbsoluteTop.value = top
}
+ override fun setNotificationStackAbsoluteBottom(bottom: Float) {
+ _notificationStackAbsoluteBottom.value = bottom
+ }
+
private fun dozeMachineStateToModel(state: DozeMachine.State): DozeStateModel {
return when (state) {
DozeMachine.State.UNINITIALIZED -> DozeStateModel.UNINITIALIZED
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index b7d0d45..3a5614f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -25,6 +25,7 @@
import android.util.Log
import com.android.app.animation.Interpolators
import com.android.app.tracing.coroutines.withContextTraced as withContext
+import com.android.systemui.Flags.transitionRaceCondition
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -77,6 +78,8 @@
/** The [TransitionInfo] of the most recent call to [startTransition]. */
val currentTransitionInfoInternal: StateFlow<TransitionInfo>
+ /** The [TransitionInfo] of the most recent call to [startTransition]. */
+ val currentTransitionInfo: TransitionInfo
/**
* Interactors that require information about changes between [KeyguardState]s will call this to
@@ -132,7 +135,7 @@
private var lastStep: TransitionStep = TransitionStep()
private var lastAnimator: ValueAnimator? = null
- private val _currentTransitionMutex = Mutex()
+ private val withContextMutex = Mutex()
private val _currentTransitionInfo: MutableStateFlow<TransitionInfo> =
MutableStateFlow(
TransitionInfo(
@@ -144,6 +147,16 @@
)
override var currentTransitionInfoInternal = _currentTransitionInfo.asStateFlow()
+ @Volatile
+ override var currentTransitionInfo: TransitionInfo =
+ TransitionInfo(
+ ownerName = "",
+ from = KeyguardState.OFF,
+ to = KeyguardState.OFF,
+ animator = null,
+ )
+ private set
+
/*
* When manual control of the transition is requested, a unique [UUID] is used as the handle
* to permit calls to [updateTransition]
@@ -163,13 +176,17 @@
}
override suspend fun startTransition(info: TransitionInfo): UUID? {
- _currentTransitionInfo.value = info
+ if (transitionRaceCondition()) {
+ currentTransitionInfo = info
+ } else {
+ _currentTransitionInfo.value = info
+ }
Log.d(TAG, "(Internal) Setting current transition info: $info")
// There is no fairness guarantee with 'withContext', which means that transitions could
// be processed out of order. Use a Mutex to guarantee ordering. [updateTransition]
// requires the same lock
- _currentTransitionMutex.lock()
+ withContextMutex.lock()
// Only used in a test environment
if (forceDelayForRaceConditionTest) {
delay(50L)
@@ -177,7 +194,7 @@
// Animators must be started on the main thread.
return withContext("$TAG#startTransition", mainDispatcher) {
- _currentTransitionMutex.unlock()
+ withContextMutex.unlock()
if (lastStep.from == info.from && lastStep.to == info.to) {
Log.i(TAG, "Duplicate call to start the transition, rejecting: $info")
return@withContext null
@@ -265,9 +282,9 @@
// There is no fairness guarantee with 'withContext', which means that transitions could
// be processed out of order. Use a Mutex to guarantee ordering. [startTransition]
// requires the same lock
- _currentTransitionMutex.lock()
+ withContextMutex.lock()
withContext("$TAG#updateTransition", mainDispatcher) {
- _currentTransitionMutex.unlock()
+ withContextMutex.unlock()
updateTransitionInternal(transitionId, value, state)
}
@@ -302,13 +319,23 @@
// Tests runs on testDispatcher, which is not the main thread, causing the animator thread
// check to fail
if (testSetup) {
- _currentTransitionInfo.value =
- TransitionInfo(
- ownerName = ownerName,
- from = KeyguardState.OFF,
- to = to,
- animator = null,
- )
+ if (transitionRaceCondition()) {
+ currentTransitionInfo =
+ TransitionInfo(
+ ownerName = ownerName,
+ from = KeyguardState.OFF,
+ to = to,
+ animator = null,
+ )
+ } else {
+ _currentTransitionInfo.value =
+ TransitionInfo(
+ ownerName = ownerName,
+ from = KeyguardState.OFF,
+ to = to,
+ animator = null,
+ )
+ }
emitTransition(
TransitionStep(
KeyguardState.OFF,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index bc2ed71..bd9836f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -24,8 +24,6 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.KeyguardWmStateRefactor
-import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
@@ -54,9 +52,7 @@
powerInteractor: PowerInteractor,
private val communalSceneInteractor: CommunalSceneInteractor,
keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
- private val biometricSettingsRepository: BiometricSettingsRepository,
- private val keyguardRepository: KeyguardRepository,
- private val keyguardEnabledInteractor: KeyguardEnabledInteractor,
+ private val keyguardLockWhileAwakeInteractor: KeyguardLockWhileAwakeInteractor,
) :
TransitionInteractor(
fromState = KeyguardState.GONE,
@@ -78,7 +74,9 @@
}
fun showKeyguard() {
- scope.launch("$TAG#showKeyguard") { startTransitionTo(KeyguardState.LOCKSCREEN) }
+ scope.launch("$TAG#showKeyguard") {
+ startTransitionTo(KeyguardState.LOCKSCREEN, ownerReason = "showKeyguard()")
+ }
}
/**
@@ -102,34 +100,18 @@
// Primarily for when the user chooses to lock down the device
private fun listenForGoneToLockscreenOrHubOrOccluded() {
if (KeyguardWmStateRefactor.isEnabled) {
- scope.launch("$TAG#listenForGoneToLockscreenOrHub") {
- biometricSettingsRepository.isCurrentUserInLockdown
- .distinctUntilChanged()
- .filterRelevantKeyguardStateAnd { inLockdown -> inLockdown }
+ scope.launch {
+ keyguardLockWhileAwakeInteractor.lockWhileAwakeEvents
+ .filterRelevantKeyguardState()
.sample(communalSceneInteractor.isIdleOnCommunalNotEditMode, ::Pair)
- .collect { (_, isIdleOnCommunal) ->
+ .collect { (lockReason, idleOnCommunal) ->
val to =
- if (isIdleOnCommunal) {
+ if (idleOnCommunal) {
KeyguardState.GLANCEABLE_HUB
} else {
KeyguardState.LOCKSCREEN
}
- startTransitionTo(to, ownerReason = "User initiated lockdown")
- }
- }
-
- scope.launch {
- keyguardRepository.isKeyguardEnabled
- .filterRelevantKeyguardStateAnd { enabled -> enabled }
- .sample(keyguardEnabledInteractor.showKeyguardWhenReenabled)
- .filter { reshow -> reshow }
- .collect {
- startTransitionTo(
- KeyguardState.LOCKSCREEN,
- ownerReason =
- "Keyguard was re-enabled, and we weren't GONE when it " +
- "was originally disabled",
- )
+ startTransitionTo(to, ownerReason = "lockWhileAwake: $lockReason")
}
}
} else {
@@ -146,7 +128,10 @@
} else {
KeyguardState.LOCKSCREEN
}
- startTransitionTo(to)
+ startTransitionTo(
+ to,
+ ownerReason = "keyguard interactor says keyguard is showing",
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index a7dde34e..b60e98a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -40,6 +40,7 @@
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
+import com.android.systemui.util.kotlin.sample
import java.util.UUID
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -132,11 +133,10 @@
scope.launch("$TAG#listenForLockscreenToDreaming") {
keyguardInteractor.isAbleToDream
.filterRelevantKeyguardState()
- .sampleCombine(
- internalTransitionInteractor.currentTransitionInfoInternal,
- transitionInteractor.isFinishedIn(KeyguardState.LOCKSCREEN),
- )
- .collect { (isAbleToDream, transitionInfo, isOnLockscreen) ->
+ .sample(transitionInteractor.isFinishedIn(KeyguardState.LOCKSCREEN), ::Pair)
+ .collect { (isAbleToDream, isOnLockscreen) ->
+ val transitionInfo =
+ internalTransitionInteractor.currentTransitionInfoInternal()
val isTransitionInterruptible =
transitionInfo.to == KeyguardState.LOCKSCREEN &&
!invalidFromStates.contains(transitionInfo.from)
@@ -179,7 +179,6 @@
shadeRepository.legacyShadeExpansion
.sampleCombine(
transitionInteractor.startedKeyguardTransitionStep,
- internalTransitionInteractor.currentTransitionInfoInternal,
keyguardInteractor.statusBarState,
keyguardInteractor.isKeyguardDismissible,
keyguardInteractor.isKeyguardOccluded,
@@ -188,11 +187,12 @@
(
shadeExpansion,
startedStep,
- currentTransitionInfo,
statusBarState,
isKeyguardUnlocked,
isKeyguardOccluded) ->
val id = transitionId
+ val currentTransitionInfo =
+ internalTransitionInteractor.currentTransitionInfoInternal()
if (id != null) {
if (startedStep.to == KeyguardState.PRIMARY_BOUNCER) {
// An existing `id` means a transition is started, and calls to
@@ -288,6 +288,7 @@
startTransitionTo(
KeyguardState.GONE,
modeOnCanceled = TransitionModeOnCanceled.RESET,
+ ownerReason = "keyguard interactor says keyguard is going away",
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt
index 2cc6afa..0507834 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt
@@ -17,13 +17,13 @@
package com.android.systemui.keyguard.domain.interactor
import android.annotation.FloatRange
+import com.android.systemui.Flags.transitionRaceCondition
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionState
import java.util.UUID
import javax.inject.Inject
-import kotlinx.coroutines.flow.StateFlow
/**
* This interactor provides direct access to [KeyguardTransitionRepository] internals and exposes
@@ -32,9 +32,7 @@
@SysUISingleton
class InternalKeyguardTransitionInteractor
@Inject
-constructor(
- private val repository: KeyguardTransitionRepository,
-) {
+constructor(private val repository: KeyguardTransitionRepository) {
/**
* The [TransitionInfo] of the most recent call to
@@ -58,14 +56,19 @@
* *will* be emitted, and therefore that it can safely request an AOD -> LOCKSCREEN transition
* which will subsequently cancel GONE -> AOD.
*/
- internal val currentTransitionInfoInternal: StateFlow<TransitionInfo> =
- repository.currentTransitionInfoInternal
+ internal fun currentTransitionInfoInternal(): TransitionInfo {
+ return if (transitionRaceCondition()) {
+ repository.currentTransitionInfo
+ } else {
+ repository.currentTransitionInfoInternal.value
+ }
+ }
suspend fun startTransition(info: TransitionInfo) = repository.startTransition(info)
suspend fun updateTransition(
transitionId: UUID,
@FloatRange(from = 0.0, to = 1.0) value: Float,
- state: TransitionState
+ state: TransitionState,
) = repository.updateTransition(transitionId, value, state)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt
index c19bbbc..4793d95 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
import android.util.Log
+import com.android.systemui.Flags.transitionRaceCondition
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -51,7 +52,13 @@
fun startDismissKeyguardTransition(reason: String = "") {
if (SceneContainerFlag.isEnabled) return
Log.d(TAG, "#startDismissKeyguardTransition(reason=$reason)")
- when (val startedState = repository.currentTransitionInfoInternal.value.to) {
+ val startedState =
+ if (transitionRaceCondition()) {
+ repository.currentTransitionInfo.to
+ } else {
+ repository.currentTransitionInfoInternal.value.to
+ }
+ when (startedState) {
LOCKSCREEN -> fromLockscreenTransitionInteractor.dismissKeyguard()
PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.dismissPrimaryBouncer()
ALTERNATE_BOUNCER -> fromAlternateBouncerTransitionInteractor.dismissAlternateBouncer()
@@ -61,7 +68,7 @@
KeyguardState.GONE ->
Log.i(
TAG,
- "Already transitioning to GONE; ignoring startDismissKeyguardTransition."
+ "Already transitioning to GONE; ignoring startDismissKeyguardTransition.",
)
else -> Log.e(TAG, "We don't know how to dismiss keyguard from state $startedState.")
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
index 5f08aa3..631e44a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
@@ -22,7 +22,7 @@
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
+import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@@ -74,11 +74,9 @@
.onEach { SceneContainerFlag.assertInLegacyMode() }
// Whenever the keyguard is disabled...
.filter { enabled -> !enabled }
- .sampleCombine(
- internalTransitionInteractor.currentTransitionInfoInternal,
- biometricSettingsRepository.isCurrentUserInLockdown,
- )
- .map { (_, transitionInfo, inLockdown) ->
+ .sample(biometricSettingsRepository.isCurrentUserInLockdown, ::Pair)
+ .map { (_, inLockdown) ->
+ val transitionInfo = internalTransitionInteractor.currentTransitionInfoInternal()
// ...we hide the keyguard, if it's showing and we're not in lockdown. In that case,
// we want to remember that and re-show it when keyguard is enabled again.
transitionInfo.to != KeyguardState.GONE && !inLockdown
@@ -93,11 +91,10 @@
if (!SceneContainerFlag.isEnabled) {
repository.isKeyguardEnabled
.filter { enabled -> !enabled }
- .sampleCombine(
- biometricSettingsRepository.isCurrentUserInLockdown,
- internalTransitionInteractor.currentTransitionInfoInternal,
- )
- .collect { (_, inLockdown, currentTransitionInfo) ->
+ .sample(biometricSettingsRepository.isCurrentUserInLockdown, ::Pair)
+ .collect { (_, inLockdown) ->
+ val currentTransitionInfo =
+ internalTransitionInteractor.currentTransitionInfoInternal()
if (currentTransitionInfo.to != KeyguardState.GONE && !inLockdown) {
keyguardDismissTransitionInteractor.startDismissKeyguardTransition(
"keyguard disabled"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 29c6d5a..b24ca1a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -545,6 +545,10 @@
repository.isKeyguardGoingAway.value = isGoingAway
}
+ fun setNotificationStackAbsoluteBottom(bottom: Float) {
+ repository.setNotificationStackAbsoluteBottom(bottom)
+ }
+
companion object {
private const val TAG = "KeyguardInteractor"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt
new file mode 100644
index 0000000..0ab3e5c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.os.Bundle
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+
+/**
+ * Emitted when we receive a [KeyguardLockWhileAwakeInteractor.onKeyguardServiceDoKeyguardTimeout]
+ * call.
+ *
+ * Includes a timestamp so it's not conflated by the StateFlow.
+ */
+data class KeyguardTimeoutWhileAwakeEvent(val timestamp: Long, val options: Bundle?)
+
+/** The reason we're locking while awake, used for logging. */
+enum class LockWhileAwakeReason(private val logReason: String) {
+ LOCKDOWN("Lockdown initiated."),
+ KEYGUARD_REENABLED(
+ "Keyguard was re-enabled. We weren't unlocked when it was disabled, " +
+ "so we're returning to the lockscreen."
+ ),
+ KEYGUARD_TIMEOUT_WHILE_SCREEN_ON(
+ "Timed out while the screen was kept on, or WM#lockNow() was called."
+ );
+
+ override fun toString(): String {
+ return logReason
+ }
+}
+
+/**
+ * Logic around cases where the device locks while still awake (transitioning from GONE ->
+ * LOCKSCREEN), vs. the more common cases of a power button press or screen timeout, which result in
+ * the device going to sleep.
+ *
+ * This is possible in the following situations:
+ * - The user initiates lockdown from the power menu.
+ * - Theft detection, etc. has requested lockdown.
+ * - The keyguard was disabled while visible, and has now been re-enabled, so it's re-showing.
+ * - Someone called WM#lockNow().
+ * - The screen timed out, but an activity with FLAG_ALLOW_LOCK_WHILE_SCREEN_ON is on top.
+ */
+@SysUISingleton
+class KeyguardLockWhileAwakeInteractor
+@Inject
+constructor(
+ biometricSettingsRepository: BiometricSettingsRepository,
+ keyguardEnabledInteractor: KeyguardEnabledInteractor,
+) {
+ /** Emits whenever a timeout event is received by [KeyguardService]. */
+ private val timeoutEvents: MutableStateFlow<KeyguardTimeoutWhileAwakeEvent?> =
+ MutableStateFlow(null)
+
+ /** Emits whenever the current user is in lockdown mode. */
+ private val inLockdown: Flow<LockWhileAwakeReason> =
+ biometricSettingsRepository.isCurrentUserInLockdown
+ .distinctUntilChanged()
+ .filter { inLockdown -> inLockdown }
+ .map { LockWhileAwakeReason.LOCKDOWN }
+
+ /**
+ * Emits whenever the keyguard is re-enabled, and we need to return to lockscreen due to the
+ * device being locked when the keyguard was originally disabled.
+ */
+ private val keyguardReenabled: Flow<LockWhileAwakeReason> =
+ keyguardEnabledInteractor.isKeyguardEnabled
+ .filter { enabled -> enabled }
+ .sample(keyguardEnabledInteractor.showKeyguardWhenReenabled)
+ .filter { reshow -> reshow }
+ .map { LockWhileAwakeReason.KEYGUARD_REENABLED }
+
+ /** Emits whenever we should lock while the screen is on, for any reason. */
+ val lockWhileAwakeEvents: Flow<LockWhileAwakeReason> =
+ merge(
+ inLockdown,
+ keyguardReenabled,
+ timeoutEvents.filterNotNull().map {
+ LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON
+ },
+ )
+
+ /**
+ * Called by [KeyguardService] when it receives a doKeyguardTimeout() call. This indicates that
+ * the device locked while the screen was on.
+ *
+ * [options] appears to be no longer used, but we'll keep it in this interactor in case that
+ * turns out not to be true.
+ */
+ fun onKeyguardServiceDoKeyguardTimeout(options: Bundle?) {
+ timeoutEvents.value =
+ KeyguardTimeoutWhileAwakeEvent(
+ timestamp = System.currentTimeMillis(),
+ options = options,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt
index 7f1e881..278a98f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt
@@ -80,7 +80,7 @@
// *_BOUNCER -> LOCKSCREEN.
return powerInteractor.detailedWakefulness.value.powerButtonLaunchGestureTriggered &&
KeyguardState.deviceIsAsleepInState(
- internalTransitionInteractor.currentTransitionInfoInternal.value.to
+ internalTransitionInteractor.currentTransitionInfoInternal().to
)
}
@@ -100,13 +100,13 @@
scene = Scenes.Gone,
stateWithoutSceneContainer = KeyguardState.GONE,
),
- ::Pair
+ ::Pair,
)
.map { (wakefulness, isOnGone) ->
wakefulness.powerButtonLaunchGestureTriggered && !isOnGone
},
// Emit false once that activity goes away.
- isShowWhenLockedActivityOnTop.filter { !it }.map { false }
+ isShowWhenLockedActivityOnTop.filter { !it }.map { false },
)
.stateIn(applicationScope, SharingStarted.Eagerly, false)
@@ -134,7 +134,7 @@
*/
fun setWmNotifiedShowWhenLockedActivityOnTop(
showWhenLockedActivityOnTop: Boolean,
- taskInfo: RunningTaskInfo? = null
+ taskInfo: RunningTaskInfo? = null,
) {
repository.setShowWhenLockedActivityInfo(showWhenLockedActivityOnTop, taskInfo)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt
index cddeaaf..b986d56 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt
@@ -61,7 +61,7 @@
fun start() {
scope.launch {
- if (internalTransitionInteractor.currentTransitionInfoInternal.value.from != OFF) {
+ if (internalTransitionInteractor.currentTransitionInfoInternal().from != OFF) {
Log.e(
"KeyguardTransitionInteractor",
"showLockscreenOnBoot emitted, but we've already " +
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index 249982d..abd7f90 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -71,14 +71,14 @@
ownerReason: String = "",
): UUID? {
toState.checkValidState()
- if (fromState != internalTransitionInteractor.currentTransitionInfoInternal.value.to) {
+ if (fromState != internalTransitionInteractor.currentTransitionInfoInternal().to) {
Log.e(
name,
"Ignoring startTransition: This interactor asked to transition from " +
"$fromState -> $toState, but we last transitioned to " +
- "${internalTransitionInteractor.currentTransitionInfoInternal.value.to}, not" +
+ "${internalTransitionInteractor.currentTransitionInfoInternal().to}, not" +
" $fromState. This should never happen - check currentTransitionInfoInternal" +
- " or use filterRelevantKeyguardState before starting transitions."
+ " or use filterRelevantKeyguardState before starting transitions.",
)
return null
}
@@ -149,7 +149,7 @@
if (keyguardInteractor.isKeyguardDismissible.value) {
startTransitionTo(
KeyguardState.GONE,
- ownerReason = "Power button gesture while keyguard is dismissible"
+ ownerReason = "Power button gesture while keyguard is dismissible",
)
return true
@@ -159,7 +159,7 @@
// should transition to GONE.
startTransitionTo(
KeyguardState.GONE,
- ownerReason = "Power button gesture on dismissable keyguard"
+ ownerReason = "Power button gesture on dismissable keyguard",
)
return true
@@ -190,16 +190,13 @@
startTransitionTo(
toState = keyguardInteractor.asleepKeyguardState.value,
modeOnCanceled = modeOnCanceled,
- ownerReason = "Sleep transition triggered"
+ ownerReason = "Sleep transition triggered",
)
}
}
/** This signal may come in before the occlusion signal, and can provide a custom transition */
- fun listenForTransitionToCamera(
- scope: CoroutineScope,
- keyguardInteractor: KeyguardInteractor,
- ) {
+ fun listenForTransitionToCamera(scope: CoroutineScope, keyguardInteractor: KeyguardInteractor) {
if (!KeyguardWmStateRefactor.isEnabled) {
scope.launch {
keyguardInteractor.onCameraLaunchDetected.filterRelevantKeyguardState().collect {
@@ -223,7 +220,7 @@
* [startedKeyguardState] as it does not wait for the emission of the first STARTED step.
*/
fun inOrTransitioningToRelevantKeyguardState(): Boolean {
- return internalTransitionInteractor.currentTransitionInfoInternal.value.to == fromState
+ return internalTransitionInteractor.currentTransitionInfoInternal().to == fromState
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index a09cd7c..a1f6067 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -19,6 +19,7 @@
package com.android.systemui.keyguard.domain.interactor
import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.systemui.Flags.transitionRaceCondition
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
@@ -65,7 +66,7 @@
combine(
transitionInteractor.isFinishedIn(
scene = Scenes.Gone,
- stateWithoutSceneContainer = KeyguardState.GONE
+ stateWithoutSceneContainer = KeyguardState.GONE,
),
wakeToGoneInteractor.canWakeDirectlyToGone,
) { isOnGone, canWakeDirectlyToGone ->
@@ -197,11 +198,11 @@
combine(
transitionInteractor.isInTransition(
edge = Edge.create(to = Scenes.Gone),
- edgeWithoutSceneContainer = Edge.create(to = KeyguardState.GONE)
+ edgeWithoutSceneContainer = Edge.create(to = KeyguardState.GONE),
),
transitionInteractor.isFinishedIn(
scene = Scenes.Gone,
- stateWithoutSceneContainer = KeyguardState.GONE
+ stateWithoutSceneContainer = KeyguardState.GONE,
),
surfaceBehindInteractor.isAnimatingSurface,
notificationLaunchAnimationInteractor.isLaunchAnimationRunning,
@@ -231,7 +232,7 @@
combine(
transitionInteractor.currentKeyguardState,
wakeToGoneInteractor.canWakeDirectlyToGone,
- ::Pair
+ ::Pair,
)
.sample(transitionInteractor.startedStepWithPrecedingStep, ::toTriple)
.map { (currentState, canWakeDirectlyToGone, startedWithPrev) ->
@@ -242,7 +243,12 @@
startedFromStep.transitionState == TransitionState.CANCELED &&
startedFromStep.from == KeyguardState.GONE
- val transitionInfo = transitionRepository.currentTransitionInfoInternal.value
+ val transitionInfo =
+ if (transitionRaceCondition()) {
+ transitionRepository.currentTransitionInfo
+ } else {
+ transitionRepository.currentTransitionInfoInternal.value
+ }
val wakingDirectlyToGone =
deviceIsAsleepInState(transitionInfo.from) &&
transitionInfo.to == KeyguardState.GONE
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
index 5524b20..aa44b6d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
@@ -106,7 +106,7 @@
private suspend fun handleIdle(
prevTransition: ObservableTransitionState,
- idle: ObservableTransitionState.Idle
+ idle: ObservableTransitionState.Idle,
) {
if (currentTransitionId == null) return
if (prevTransition !is ObservableTransitionState.Transition) return
@@ -133,10 +133,10 @@
val newTransition =
TransitionInfo(
ownerName = this::class.java.simpleName,
- from = internalTransitionInteractor.currentTransitionInfoInternal.value.to,
+ from = internalTransitionInteractor.currentTransitionInfoInternal().to,
to = state,
animator = null,
- modeOnCanceled = TransitionModeOnCanceled.REVERSE
+ modeOnCanceled = TransitionModeOnCanceled.REVERSE,
)
currentTransitionId = internalTransitionInteractor.startTransition(newTransition)
internalTransitionInteractor.updateTransition(currentTransitionId!!, 1f, FINISHED)
@@ -152,8 +152,7 @@
private suspend fun handleTransition(transition: ObservableTransitionState.Transition) {
if (transition.fromContent == Scenes.Lockscreen) {
if (currentTransitionId != null) {
- val currentToState =
- internalTransitionInteractor.currentTransitionInfoInternal.value.to
+ val currentToState = internalTransitionInteractor.currentTransitionInfoInternal().to
if (currentToState == UNDEFINED) {
transitionKtfTo(transitionInteractor.startedKeyguardTransitionStep.value.from)
}
@@ -197,21 +196,21 @@
from = UNDEFINED,
to = repository.nextLockscreenTargetState.value,
animator = null,
- modeOnCanceled = TransitionModeOnCanceled.RESET
+ modeOnCanceled = TransitionModeOnCanceled.RESET,
)
repository.nextLockscreenTargetState.value = DEFAULT_STATE
startTransition(newTransition)
}
private suspend fun startTransitionFromLockscreen() {
- val currentState = internalTransitionInteractor.currentTransitionInfoInternal.value.to
+ val currentState = internalTransitionInteractor.currentTransitionInfoInternal().to
val newTransition =
TransitionInfo(
ownerName = this::class.java.simpleName,
from = currentState,
to = UNDEFINED,
animator = null,
- modeOnCanceled = TransitionModeOnCanceled.RESET
+ modeOnCanceled = TransitionModeOnCanceled.RESET,
)
startTransition(newTransition)
}
@@ -228,7 +227,7 @@
internalTransitionInteractor.updateTransition(
currentTransitionId!!,
progress.coerceIn(0f, 1f),
- RUNNING
+ RUNNING,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
index 12bcc7e..b15cacf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
@@ -35,9 +35,7 @@
@SysUISingleton
class DozingToOccludedTransitionViewModel
@Inject
-constructor(
- animationFlow: KeyguardTransitionAnimationFlow,
-) : DeviceEntryIconTransition {
+constructor(animationFlow: KeyguardTransitionAnimationFlow) : DeviceEntryIconTransition {
private val transitionAnimation =
animationFlow.setup(
duration = FromAodTransitionInteractor.TO_OCCLUDED_DURATION,
@@ -56,11 +54,7 @@
var currentAlpha = 0f
return transitionAnimation.sharedFlow(
duration = 250.milliseconds,
- startTime = if (lightRevealMigration()) {
- 100.milliseconds // Wait for the light reveal to "hit" the LS elements.
- } else {
- 0.milliseconds
- },
+ startTime = 0.milliseconds,
onStart = {
if (lightRevealMigration()) {
currentAlpha = viewState.alpha()
@@ -69,7 +63,6 @@
}
},
onStep = { MathUtils.lerp(currentAlpha, 0f, it) },
- onCancel = { 0f },
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index d3bed27..602f593 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -116,6 +116,8 @@
@Nullable
private View mMediaViewPlaceHolderForScene;
+ private boolean mHadConfigurationChangeWhileDetached;
+
public QSPanel(Context context, AttributeSet attrs) {
super(context, attrs);
mUsingMediaPlayer = useQsMediaPlayer(context);
@@ -425,10 +427,23 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
+ if (!isAttachedToWindow()) {
+ mHadConfigurationChangeWhileDetached = true;
+ }
mOnConfigurationChangedListeners.forEach(
listener -> listener.onConfigurationChange(newConfig));
}
+ final boolean hadConfigurationChangeWhileDetached() {
+ return mHadConfigurationChangeWhileDetached;
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mHadConfigurationChangeWhileDetached = false;
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 6cf5b32..85bcc25 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -234,6 +234,12 @@
mMediaHost.addVisibilityChangeListener(mMediaHostVisibilityListener);
}
mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
+ // We were not attached and the configuration may have changed, trigger the listener.
+ if (mView.hadConfigurationChangeWhileDetached()) {
+ mOnConfigurationChangedListener.onConfigurationChange(
+ getContext().getResources().getConfiguration()
+ );
+ }
setTiles();
mLastOrientation = getResources().getConfiguration().orientation;
mLastScreenLayout = getResources().getConfiguration().screenLayout;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
index 1792ebd..028ac6f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
@@ -111,10 +111,12 @@
override fun handleSetListening(listening: Boolean) {
super.handleSetListening(listening)
- if (listening) {
- issueRecordingState.addListener(onRecordingChangeListener)
- } else {
- issueRecordingState.removeListener(onRecordingChangeListener)
+ bgExecutor.execute {
+ if (listening) {
+ issueRecordingState.addListener(onRecordingChangeListener)
+ } else {
+ issueRecordingState.removeListener(onRecordingChangeListener)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt
index 1af328e..09a6ce8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt
@@ -41,7 +41,7 @@
override fun tileData(
user: UserHandle,
- triggers: Flow<DataUpdateTrigger>
+ triggers: Flow<DataUpdateTrigger>,
): Flow<IssueRecordingModel> =
conflatedCallbackFlow {
val listener = Runnable { trySend(IssueRecordingModel(state.isRecording)) }
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt
index 4353933..16ca81a 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt
@@ -55,8 +55,10 @@
var screenRecord = false
fun start() {
- bgExecutor.execute { traceurConnection.startTracing(traceConfig) }
- issueRecordingState.isRecording = true
+ bgExecutor.execute {
+ traceurConnection.startTracing(traceConfig)
+ issueRecordingState.isRecording = true
+ }
}
fun stop() {
@@ -69,8 +71,8 @@
)
}
traceurConnection.stopTracing()
+ issueRecordingState.isRecording = false
}
- issueRecordingState.isRecording = false
}
fun share(notificationId: Int, screenRecording: Uri?) {
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt
index 51c437dbe..ffdcf1b 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt
@@ -16,12 +16,20 @@
package com.android.systemui.recordissue
+import android.annotation.SuppressLint
+import android.content.ContentResolver
import android.content.Context
+import android.database.ContentObserver
+import android.os.Handler
+import androidx.annotation.VisibleForTesting
+import androidx.annotation.WorkerThread
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.recordissue.RecordIssueModule.Companion.TILE_SPEC
import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.settings.GlobalSettings
import com.android.traceur.PresetTraceConfigs
import com.android.traceur.TraceConfig
import java.util.concurrent.CopyOnWriteArrayList
@@ -31,12 +39,20 @@
class IssueRecordingState
@Inject
constructor(
- userTracker: UserTracker,
- userFileManager: UserFileManager,
+ private val userTracker: UserTracker,
+ private val userFileManager: UserFileManager,
+ @Background bgHandler: Handler,
+ private val resolver: ContentResolver,
+ private val globalSettings: GlobalSettings,
) {
- private val prefs =
- userFileManager.getSharedPreferences(TILE_SPEC, Context.MODE_PRIVATE, userTracker.userId)
+ private val prefs
+ get() =
+ userFileManager.getSharedPreferences(
+ TILE_SPEC,
+ Context.MODE_PRIVATE,
+ userTracker.userId,
+ )
val customTraceState = CustomTraceState(prefs)
@@ -77,22 +93,52 @@
private val listeners = CopyOnWriteArrayList<Runnable>()
+ @VisibleForTesting
+ val onRecordingChangeListener =
+ object : ContentObserver(bgHandler) {
+ override fun onChange(selfChange: Boolean) {
+ isRecording = globalSettings.getInt(KEY_ONGOING_ISSUE_RECORDING, 0) == 1
+ listeners.forEach(Runnable::run)
+ }
+ }
+
+ /**
+ * isRecording is purposely always set to false at the initialization of the record issue qs
+ * tile. We want to avoid a situation where the System UI crashed / the device was restarted in
+ * the middle of a trace session and the QS tile is in an active state even though no tracing is
+ * ongoing.
+ */
var isRecording = false
+ @WorkerThread
set(value) {
+ globalSettings.putInt(KEY_ONGOING_ISSUE_RECORDING, if (value) 1 else 0)
field = value
- listeners.forEach(Runnable::run)
}
fun markUserApprovalForScreenRecording() {
hasUserApprovedScreenRecording = true
}
+ @WorkerThread
+ @SuppressLint("RegisterContentObserverViaContentResolver")
fun addListener(listener: Runnable) {
+ if (listeners.isEmpty()) {
+ resolver.registerContentObserver(
+ globalSettings.getUriFor(KEY_ONGOING_ISSUE_RECORDING),
+ false,
+ onRecordingChangeListener,
+ )
+ }
listeners.add(listener)
}
+ @WorkerThread
+ @SuppressLint("RegisterContentObserverViaContentResolver")
fun removeListener(listener: Runnable) {
listeners.remove(listener)
+ if (listeners.isEmpty()) {
+ resolver.unregisterContentObserver(onRecordingChangeListener)
+ }
}
companion object {
@@ -100,6 +146,7 @@
private const val HAS_APPROVED_SCREEN_RECORDING = "HasApprovedScreenRecord"
private const val KEY_RECORD_SCREEN = "key_recordScreen"
private const val KEY_TAG_TITLES = "key_tagTitles"
+ private const val KEY_ONGOING_ISSUE_RECORDING = "issueRecordingOngoing"
const val KEY_ISSUE_TYPE_INDEX = "key_issueTypeIndex"
const val ISSUE_TYPE_NOT_SET = -1
const val TAG_TITLE_DELIMITER = ": "
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt
index 55033f0..1d97034 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt
@@ -185,6 +185,7 @@
} else if (isOnKeyguard && !unlocking && isDreaming) {
Model(scrimState = ScrimState.DREAMING, unlocking = false)
} else {
+ onKeyguardFadedAway(transitionState.isIdle(Scenes.Gone))
Model(scrimState = ScrimState.UNLOCKED, unlocking = unlocking)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt
index c74f038..38de17e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt
@@ -22,7 +22,7 @@
import javax.inject.Inject
import kotlin.math.max
-class LargeScreenHeaderHelper @Inject constructor(private val context: Context) {
+class LargeScreenHeaderHelper @Inject constructor(@ShadeDisplayAware private val context: Context) {
fun getLargeScreenHeaderHeight(): Int = getLargeScreenHeaderHeight(context)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
index f463cb5..6e63446 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
@@ -34,7 +34,7 @@
class NotificationPanelUnfoldAnimationController
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
statusBarStateController: StatusBarStateController,
progressProvider: NaturalRotationUnfoldProgressProvider,
) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 67d162b..083cf1f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -668,7 +668,7 @@
@Inject
public NotificationPanelViewController(NotificationPanelView view,
@Main Handler handler,
- LayoutInflater layoutInflater,
+ @ShadeDisplayAware LayoutInflater layoutInflater,
FeatureFlags featureFlags,
NotificationWakeUpCoordinator coordinator,
PulseExpansionHandler pulseExpansionHandler,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 5b6696b..24dba59 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -148,7 +148,7 @@
@Inject
public NotificationShadeWindowControllerImpl(
- Context context,
+ @ShadeDisplayAware Context context,
WindowRootViewComponent.Factory windowRootViewComponentFactory,
ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
IActivityManager activityManager,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt b/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
index 91627d6..7a70966 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
@@ -14,7 +14,7 @@
class QsBatteryModeController
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
insetsProviderStore: StatusBarContentInsetsProviderStore,
) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
index 1918094..15b2270 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -83,7 +83,7 @@
@Provides
@SysUISingleton
fun providesWindowRootView(
- layoutInflater: LayoutInflater,
+ @ShadeDisplayAware layoutInflater: LayoutInflater,
viewModelFactory: SceneContainerViewModel.Factory,
containerConfigProvider: Provider<SceneContainerConfig>,
scenesProvider: Provider<Set<@JvmSuppressWildcards Scene>>,
@@ -156,7 +156,7 @@
@Provides
fun providesKeyguardBottomAreaView(
npv: NotificationPanelView,
- layoutInflater: LayoutInflater,
+ @ShadeDisplayAware layoutInflater: LayoutInflater,
): KeyguardBottomAreaView {
return layoutInflater.inflate(R.layout.keyguard_bottom_area, npv, false)
as KeyguardBottomAreaView
diff --git a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
index a171d33..4b8cc00 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
@@ -44,6 +44,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.res.R;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.connectivity.SignalCallback;
@@ -153,7 +154,7 @@
ShadeCarrierGroupControllerLogger logger,
NetworkController networkController,
CarrierTextManager.Builder carrierTextManagerBuilder,
- Context context,
+ @ShadeDisplayAware Context context,
CarrierConfigTracker carrierConfigTracker,
SlotIndexResolver slotIndexResolver,
MobileUiAdapter mobileUiAdapter,
@@ -497,7 +498,7 @@
ShadeCarrierGroupControllerLogger logger,
NetworkController networkController,
CarrierTextManager.Builder carrierTextControllerBuilder,
- Context context,
+ @ShadeDisplayAware Context context,
CarrierConfigTracker carrierConfigTracker,
SlotIndexResolver slotIndexResolver,
MobileUiAdapter mobileUiAdapter,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index 193056c..5629938 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -182,7 +182,7 @@
/** Business logic for shade interactions */
@SysUISingleton
-class ShadeRepositoryImpl @Inject constructor(@Application applicationContext: Context) :
+class ShadeRepositoryImpl @Inject constructor() :
ShadeRepository {
private val _qsExpansion = MutableStateFlow(0f)
@Deprecated("Use ShadeInteractor.qsExpansion instead")
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt
index 6c44c73..e5d08a0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt
@@ -26,6 +26,7 @@
import com.android.systemui.log.dagger.ShadeTouchLog
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.shade.TouchLogger.Companion.logTouchesTo
import com.android.systemui.shade.data.repository.ShadeRepository
@@ -48,7 +49,7 @@
@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
- @Application private val applicationContext: Context,
+ @ShadeDisplayAware private val context: Context,
@ShadeTouchLog private val touchLog: LogBuffer,
private val configurationRepository: ConfigurationRepository,
private val shadeRepository: ShadeRepository,
@@ -94,7 +95,7 @@
// Force initial collection.
.onStart { emit(Unit) }
.collect {
- val resources = applicationContext.resources
+ val resources = context.resources
// The configuration for 'shouldUseSplitNotificationShade' dictates the width of
// the shade in both split-shade and dual-shade modes.
shadeRepository.setShadeLayoutWide(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImpl.kt
index 823742a..a5caa09 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImpl.kt
@@ -30,7 +30,7 @@
@Inject
internal constructor(
@ShadeDisplayAware configurationController: ConfigurationController,
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val splitShadeInterpolator: SplitShadeInterpolator,
private val portraitShadeInterpolator: LargeScreenPortraitShadeInterpolator,
private val splitShadeStateController: SplitShadeStateController,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
index 5be5ccc..45516aa 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
@@ -30,6 +30,7 @@
import com.android.systemui.privacy.PrivacyItem
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.domain.interactor.PrivacyChipInteractor
import com.android.systemui.shade.domain.interactor.ShadeHeaderClockInteractor
import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -53,7 +54,7 @@
class ShadeHeaderViewModel
@AssistedInject
constructor(
- context: Context,
+ @ShadeDisplayAware context: Context,
private val activityStarter: ActivityStarter,
private val shadeInteractor: ShadeInteractor,
private val mobileIconsInteractor: MobileIconsInteractor,
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 682a9ff..77ec65b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -29,6 +29,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.inflation.BindEventManager
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
@@ -141,7 +142,7 @@
@Inject
constructor(
bindEventManager: BindEventManager,
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val notifCollection: CommonNotifCollection,
@Main private val mainHandler: Handler
) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpManagerPhone.java
index fb42bad..0299ebc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpManagerPhone.java
@@ -426,7 +426,7 @@
}
mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false);
for (NotificationEntry entry : mEntriesToRemoveWhenReorderingAllowed) {
- if (isHeadsUpEntry(entry.getKey())) {
+ if (entry != null && isHeadsUpEntry(entry.getKey())) {
// Maybe the heads-up was removed already
removeEntry(entry.getKey(), "mOnReorderingAllowedListener");
}
@@ -488,6 +488,13 @@
}
updateTopHeadsUpFlow();
updateHeadsUpFlow();
+ if (NotificationThrottleHun.isEnabled()) {
+ if (headsUpEntry.mEntry != null) {
+ if (mEntriesToRemoveWhenReorderingAllowed.contains(headsUpEntry.mEntry)) {
+ mEntriesToRemoveWhenReorderingAllowed.remove(headsUpEntry.mEntry);
+ }
+ }
+ }
}
private void updateTopHeadsUpFlow() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryWithDismissStats.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryWithDismissStats.kt
new file mode 100644
index 0000000..48a8c01
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryWithDismissStats.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import com.android.internal.statusbar.NotificationVisibility
+import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats
+
+/**
+ * A holder class for a [NotificationEntry] and an associated [DismissedByUserStats], used by
+ * [NotifCollection] for handling dismissal.
+ */
+data class EntryWithDismissStats(val entry: NotificationEntry, val stats: DismissedByUserStats) {
+ /**
+ * Creates deep a copy of this object, but with the entry, key and rank updated to correspond to
+ * the given entry.
+ */
+ fun copyForEntry(newEntry: NotificationEntry) =
+ EntryWithDismissStats(
+ entry = newEntry,
+ stats =
+ DismissedByUserStats(
+ stats.dismissalSurface,
+ stats.dismissalSentiment,
+ NotificationVisibility.obtain(
+ newEntry.key,
+ newEntry.ranking.rank,
+ stats.notificationVisibility.count,
+ /* visible= */ false,
+ ),
+ ),
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 7b3a93a..cf9ee61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -39,6 +39,7 @@
import static android.service.notification.NotificationListenerService.REASON_UNAUTOBUNDLED;
import static android.service.notification.NotificationListenerService.REASON_USER_STOPPED;
+import static com.android.systemui.Flags.notificationsDismissPrunedSummaries;
import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.DISMISSED;
import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.NOT_DISMISSED;
@@ -62,7 +63,6 @@
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.Log;
-import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -111,6 +111,7 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -273,15 +274,19 @@
* Dismisses multiple notifications on behalf of the user.
*/
public void dismissNotifications(
- List<Pair<NotificationEntry, DismissedByUserStats>> entriesToDismiss) {
+ List<EntryWithDismissStats> entriesToDismiss) {
Assert.isMainThread();
checkForReentrantCall();
+ if (notificationsDismissPrunedSummaries()) {
+ entriesToDismiss = includeSummariesToDismiss(entriesToDismiss);
+ }
+
final int entryCount = entriesToDismiss.size();
final List<NotificationEntry> entriesToLocallyDismiss = new ArrayList<>();
for (int i = 0; i < entriesToDismiss.size(); i++) {
- NotificationEntry entry = entriesToDismiss.get(i).first;
- DismissedByUserStats stats = entriesToDismiss.get(i).second;
+ NotificationEntry entry = entriesToDismiss.get(i).getEntry();
+ DismissedByUserStats stats = entriesToDismiss.get(i).getStats();
requireNonNull(stats);
NotificationEntry storedEntry = mNotificationSet.get(entry.getKey());
@@ -336,13 +341,32 @@
dispatchEventsAndRebuildList("dismissNotifications");
}
+ private List<EntryWithDismissStats> includeSummariesToDismiss(
+ List<EntryWithDismissStats> entriesToDismiss) {
+ final HashSet<NotificationEntry> entriesSet = new HashSet<>(entriesToDismiss.size());
+ for (EntryWithDismissStats entryToStats : entriesToDismiss) {
+ entriesSet.add(entryToStats.getEntry());
+ }
+
+ final List<EntryWithDismissStats> entriesPlusSummaries =
+ new ArrayList<>(entriesToDismiss.size() + 1);
+ for (EntryWithDismissStats entryToStats : entriesToDismiss) {
+ entriesPlusSummaries.add(entryToStats);
+ NotificationEntry summary = fetchSummaryToDismiss(entryToStats.getEntry());
+ if (summary != null && !entriesSet.contains(summary)) {
+ entriesPlusSummaries.add(entryToStats.copyForEntry(summary));
+ }
+ }
+ return entriesPlusSummaries;
+ }
+
/**
* Dismisses a single notification on behalf of the user.
*/
public void dismissNotification(
NotificationEntry entry,
@NonNull DismissedByUserStats stats) {
- dismissNotifications(List.of(new Pair<>(entry, stats)));
+ dismissNotifications(List.of(new EntryWithDismissStats(entry, stats)));
}
/**
@@ -1062,6 +1086,16 @@
}
}
+ @Nullable
+ private NotificationEntry fetchSummaryToDismiss(NotificationEntry entry) {
+ if (isOnlyChildInGroup(entry)) {
+ String group = entry.getSbn().getGroupKey();
+ NotificationEntry summary = getGroupSummary(group);
+ if (summary != null && isDismissable(summary)) return summary;
+ }
+ return null;
+ }
+
/** A single method interface that callers can pass in when registering future dismissals */
public interface DismissedByUserStatsCreator {
DismissedByUserStats createDismissedByUserStats(NotificationEntry entry);
@@ -1092,16 +1126,6 @@
+ ">";
}
- @Nullable
- private NotificationEntry fetchSummaryToDismiss(NotificationEntry entry) {
- if (isOnlyChildInGroup(entry)) {
- String group = entry.getSbn().getGroupKey();
- NotificationEntry summary = getGroupSummary(group);
- if (summary != null && isDismissable(summary)) return summary;
- }
- return null;
- }
-
/** called when the entry has been removed from the collection */
public void onSystemServerCancel(@CancellationReason int cancellationReason) {
Assert.isMainThread();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
index df694bb..de868d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
@@ -18,6 +18,7 @@
import android.content.Context
import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.AssistantFeedbackController
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
@@ -33,7 +34,7 @@
*/
@CoordinatorScope
class RowAppearanceCoordinator @Inject internal constructor(
- context: Context,
+ @ShadeDisplayAware context: Context,
private var mAssistantFeedbackController: AssistantFeedbackController,
private var mSectionStyleProvider: SectionStyleProvider
) : Coordinator {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
index b8a9594..db778b80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
@@ -30,6 +30,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.Compile
import com.android.app.tracing.traceSection
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
/**
@@ -39,7 +40,7 @@
*/
@CoordinatorScope
class ViewConfigCoordinator @Inject internal constructor(
- private val mConfigurationController: ConfigurationController,
+ @ShadeDisplayAware private val mConfigurationController: ConfigurationController,
private val mLockscreenUserManager: NotificationLockscreenUserManager,
private val mGutsManager: NotificationGutsManager,
private val mKeyguardUpdateMonitor: KeyguardUpdateMonitor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 9d5d7a1..e6d22b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -37,6 +37,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -86,7 +87,7 @@
@Inject
public NotificationRowBinderImpl(
- Context context,
+ @ShadeDisplayAware Context context,
NotificationMessagingUtil notificationMessagingUtil,
NotificationRemoteInputManager notificationRemoteInputManager,
NotificationLockscreenUserManager notificationLockscreenUserManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index 1935866..cab4c1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -27,6 +27,7 @@
import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.app.tracing.traceSection
+import com.android.systemui.shade.ShadeDisplayAware
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -36,7 +37,7 @@
* currently populate the notification shade.
*/
class ShadeViewManager @AssistedInject constructor(
- context: Context,
+ @ShadeDisplayAware context: Context,
@Assisted listContainer: NotificationListContainer,
@Assisted private val stackController: NotifStackController,
mediaContainerController: MediaContainerController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
index 5a616df..e5ce25d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
@@ -40,6 +40,7 @@
import androidx.annotation.NonNull;
import com.android.settingslib.Utils;
+import com.android.systemui.Flags;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
@@ -59,6 +60,9 @@
private FooterViewButton mClearAllButton;
private FooterViewButton mManageOrHistoryButton;
+ // The settings & history buttons replace the single manage/history button in the redesign
+ private FooterViewButton mSettingsButton;
+ private FooterViewButton mHistoryButton;
private boolean mShouldBeHidden;
private boolean mShowHistory;
// String cache, for performance reasons.
@@ -269,7 +273,12 @@
}
super.onFinishInflate();
mClearAllButton = (FooterViewButton) findSecondaryView();
- mManageOrHistoryButton = findViewById(R.id.manage_text);
+ if (Flags.notificationsRedesignFooterView()) {
+ mSettingsButton = findViewById(R.id.settings_button);
+ mHistoryButton = findViewById(R.id.history_button);
+ } else {
+ mManageOrHistoryButton = findViewById(R.id.manage_text);
+ }
mSeenNotifsFooterTextView = findViewById(R.id.unlock_prompt_footer);
if (!FooterViewRefactor.isEnabled()) {
updateResources();
@@ -342,8 +351,10 @@
updateClearAllButtonText();
updateClearAllButtonDescription();
- updateManageOrHistoryButtonText();
- updateManageOrHistoryButtonDescription();
+ if (!Flags.notificationsRedesignFooterView()) {
+ updateManageOrHistoryButtonText();
+ updateManageOrHistoryButtonDescription();
+ }
updateMessageString();
updateMessageIcon();
@@ -420,8 +431,16 @@
}
mClearAllButton.setBackground(clearAllBg);
mClearAllButton.setTextColor(onSurface);
- mManageOrHistoryButton.setBackground(manageBg);
- mManageOrHistoryButton.setTextColor(onSurface);
+ if (Flags.notificationsRedesignFooterView()) {
+ mSettingsButton.setBackground(manageBg);
+ mSettingsButton.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface));
+
+ mHistoryButton.setBackground(manageBg);
+ mHistoryButton.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface));
+ } else {
+ mManageOrHistoryButton.setBackground(manageBg);
+ mManageOrHistoryButton.setTextColor(onSurface);
+ }
mSeenNotifsFooterTextView.setTextColor(onSurface);
mSeenNotifsFooterTextView.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface));
ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
index 2ec7f53..ddd9cdd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
@@ -18,6 +18,7 @@
import android.view.View
import androidx.lifecycle.lifecycleScope
+import com.android.systemui.Flags
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.notification.NotificationActivityStarter
import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
@@ -63,14 +64,16 @@
notificationActivityStarter: NotificationActivityStarter,
) = coroutineScope {
launch { bindClearAllButton(footer, viewModel, clearAllNotifications) }
- launch {
- bindManageOrHistoryButton(
- footer,
- viewModel,
- launchNotificationSettings,
- launchNotificationHistory,
- notificationActivityStarter,
- )
+ if (!Flags.notificationsRedesignFooterView()) {
+ launch {
+ bindManageOrHistoryButton(
+ footer,
+ viewModel,
+ launchNotificationSettings,
+ launchNotificationHistory,
+ notificationActivityStarter,
+ )
+ }
}
launch { bindMessage(footer, viewModel) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
index d4466f8..71cddc9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
@@ -29,6 +29,7 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider.Decision
@@ -72,7 +73,7 @@
private val systemSettings: SystemSettings,
private val packageManager: PackageManager,
private val bubbles: Optional<Bubbles>,
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val notificationManager: NotificationManager,
private val settingsInteractor: NotificationSettingsInteractor
) : VisualInterruptionDecisionProvider {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt
index 068f23d..e233def 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt
@@ -32,6 +32,7 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.graphics.ImageLoader
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.row.BigPictureIconManager.DrawableState.Empty
import com.android.systemui.statusbar.notification.row.BigPictureIconManager.DrawableState.FullImage
import com.android.systemui.statusbar.notification.row.BigPictureIconManager.DrawableState.Initial
@@ -61,7 +62,7 @@
class BigPictureIconManager
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val imageLoader: ImageLoader,
private val statsManager: BigPictureStatsManager,
@Application private val scope: CoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt
index 172b76c..98d704c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt
@@ -33,6 +33,7 @@
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.collection.NotifCollectionCache
import com.android.systemui.util.asIndenting
import com.android.systemui.util.withIncreasedIndent
@@ -67,7 +68,7 @@
@SysUISingleton
class AppIconProviderImpl
@Inject
-constructor(private val sysuiContext: Context, dumpManager: DumpManager) :
+constructor(@ShadeDisplayAware private val sysuiContext: Context, dumpManager: DumpManager) :
AppIconProvider, Dumpable {
init {
dumpManager.registerNormalDumpable(TAG, this)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 129d4ce..7771421 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -596,10 +596,12 @@
}
public void setContentHeight(int contentHeight) {
+ SceneContainerFlag.assertInLegacyMode();
mContentHeight = contentHeight;
}
public float getContentHeight() {
+ SceneContainerFlag.assertInLegacyMode();
return mContentHeight;
}
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 7441c70..c9a0010 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
@@ -20,6 +20,7 @@
import android.view.View
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.media.controls.ui.controller.KeyguardMediaController
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.SourceType
import com.android.systemui.statusbar.notification.collection.NotificationClassificationFlag
@@ -41,7 +42,7 @@
class NotificationSectionsManager
@Inject
internal constructor(
- private val configurationController: ConfigurationController,
+ @ShadeDisplayAware private val configurationController: ConfigurationController,
private val keyguardMediaController: KeyguardMediaController,
private val sectionsFeatureManager: NotificationSectionsFeatureManager,
private val mediaContainerController: MediaContainerController,
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 87b16ef..dde83b9 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
@@ -615,13 +615,13 @@
if (SceneContainerFlag.isEnabled()) {
return mScrollViewFields.getScrollState().isScrolledToTop();
} else {
- return mOwnScrollY == 0;
+ return getOwnScrollY() == 0;
}
}
@Override
public boolean isScrolledToBottom() {
- return mOwnScrollY >= getScrollRange();
+ return getOwnScrollY() >= getScrollRange();
}
@Override
@@ -900,11 +900,11 @@
drawDebugInfo(canvas, y, Color.LTGRAY,
/* label= */ "mAmbientState.getStackY() + mAmbientState.getStackHeight() = " + y);
- y = (int) (mAmbientState.getStackY() + mIntrinsicContentHeight);
+ y = (int) (mAmbientState.getStackY() + getIntrinsicContentHeight());
drawDebugInfo(canvas, y, Color.YELLOW,
/* label= */ "mAmbientState.getStackY() + mIntrinsicContentHeight = " + y);
- y = mContentHeight;
+ y = getContentHeight();
drawDebugInfo(canvas, y, Color.MAGENTA,
/* label= */ "mContentHeight = " + y);
@@ -1200,7 +1200,6 @@
if (!SceneContainerFlag.isEnabled()) {
setMaxLayoutHeight(getHeight());
updateContentHeight();
- mWallpaperInteractor.setNotificationStackAbsoluteBottom(mContentHeight);
}
clampScrollPosition();
requestChildrenUpdate();
@@ -1278,7 +1277,6 @@
if (mAmbientState.getStackTop() != stackTop) {
mAmbientState.setStackTop(stackTop);
onTopPaddingChanged(/* animate = */ isAddOrRemoveAnimationPending());
- mWallpaperInteractor.setNotificationStackAbsoluteBottom((int) stackTop);
}
}
@@ -1376,7 +1374,7 @@
/**
* Updates the children views according to the stack scroll algorithm. Call this whenever
- * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
+ * modifications to {@link #getOwnScrollY()} are performed to reflect it in the view layout.
*/
private void updateChildren() {
Trace.beginSection("NSSL#updateChildren");
@@ -1406,11 +1404,11 @@
if (mChildrenToAddAnimated.contains(child)) {
final int startingPosition = getPositionInLinearLayout(child);
final int childHeight = getIntrinsicHeight(child) + mPaddingBetweenElements;
- if (startingPosition < mOwnScrollY) {
+ if (startingPosition < getOwnScrollY()) {
// This child starts off screen, so let's keep it offscreen to keep the
// others visible
- setOwnScrollY(mOwnScrollY + childHeight);
+ setOwnScrollY(getOwnScrollY() + childHeight);
}
}
}
@@ -1430,7 +1428,7 @@
targetScroll = Math.max(0, Math.min(targetScroll, getScrollRange()));
// Only apply the scroll if we're scrolling the view upwards, or the view is so
// far up that it is not visible anymore.
- if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
+ if (getOwnScrollY() < targetScroll || outOfViewScroll < getOwnScrollY()) {
setOwnScrollY(targetScroll);
}
}
@@ -1454,7 +1452,7 @@
return;
}
int scrollRange = getScrollRange();
- if (scrollRange < mOwnScrollY && !mAmbientState.isClearAllInProgress()) {
+ if (scrollRange < getOwnScrollY() && !mAmbientState.isClearAllInProgress()) {
// if the scroll boundary updates the position of the stack,
boolean animateStackY = scrollRange < getScrollAmountToScrollBoundary()
&& mAnimateStackYForContentHeightChange;
@@ -1588,7 +1586,7 @@
if (mMaxDisplayedNotifications != -1) {
// The stack intrinsic height already contains the correct value when there is a limit
// in the max number of notifications (e.g. as in keyguard).
- stackEndHeight = mIntrinsicContentHeight;
+ stackEndHeight = getIntrinsicContentHeight();
} else {
stackEndHeight = Math.max(0f, height - bottomMargin - topPadding);
}
@@ -1694,7 +1692,8 @@
if (mShouldShowShelfOnly) {
stackHeight = getTopPadding() + mShelf.getIntrinsicHeight();
} else if (mQsFullScreen) {
- int stackStartPosition = mContentHeight - getTopPadding() + getIntrinsicPadding();
+ int stackStartPosition =
+ getContentHeight() - getTopPadding() + getIntrinsicPadding();
int stackEndPosition = mMaxTopPadding + mShelf.getIntrinsicHeight();
if (stackStartPosition <= stackEndPosition) {
stackHeight = stackEndPosition;
@@ -1763,14 +1762,6 @@
updateClipping();
}
- /**
- * Return the height of the content ignoring the footer.
- */
- public int getIntrinsicContentHeight() {
- SceneContainerFlag.assertInLegacyMode();
- return (int) mIntrinsicContentHeight;
- }
-
public void updateClipping() {
boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
&& !mHeadsUpAnimatingAway;
@@ -2074,8 +2065,8 @@
// Only apply the scroll if we're scrolling the view upwards, or the view is so far up
// that it is not visible anymore.
- if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
- mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
+ if (getOwnScrollY() < targetScroll || outOfViewScroll < getOwnScrollY()) {
+ mScroller.startScroll(mScrollX, getOwnScrollY(), 0, targetScroll - getOwnScrollY());
mDontReportNextOverScroll = true;
animateScroll();
return true;
@@ -2109,7 +2100,7 @@
}
int range = getScrollRange();
- if (mOwnScrollY > range) {
+ if (getOwnScrollY() > range) {
setOwnScrollY(range);
}
}
@@ -2202,7 +2193,7 @@
// Top overScroll might not grab all scrolling motion,
// we have to scroll as well.
float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
- float newScrollY = mOwnScrollY + scrollAmount;
+ float newScrollY = getOwnScrollY() + scrollAmount;
if (newScrollY > range) {
if (!mExpandedInThisMotion) {
float currentBottomPixels = getCurrentOverScrolledPixels(false);
@@ -2235,7 +2226,7 @@
// Bottom overScroll might not grab all scrolling motion,
// we have to scroll as well.
float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
- float newScrollY = mOwnScrollY + scrollAmount;
+ float newScrollY = getOwnScrollY() + scrollAmount;
if (newScrollY < 0) {
float currentTopPixels = getCurrentOverScrolledPixels(true);
// We overScroll on the top
@@ -2275,7 +2266,7 @@
private void animateScroll() {
if (mScroller.computeScrollOffset()) {
- int oldY = mOwnScrollY;
+ int oldY = getOwnScrollY();
int y = mScroller.getCurrY();
if (oldY != y) {
@@ -2462,8 +2453,8 @@
springBack();
} else {
float overScrollTop = getCurrentOverScrollAmount(true);
- if (mOwnScrollY < 0) {
- notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
+ if (getOwnScrollY() < 0) {
+ notifyOverscrollTopListener(-getOwnScrollY(), isRubberbanded(true));
} else {
notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
}
@@ -2479,19 +2470,19 @@
*/
private void springBack() {
int scrollRange = getScrollRange();
- boolean overScrolledTop = mOwnScrollY <= 0;
- boolean overScrolledBottom = mOwnScrollY >= scrollRange;
+ boolean overScrolledTop = getOwnScrollY() <= 0;
+ boolean overScrolledBottom = getOwnScrollY() >= scrollRange;
if (overScrolledTop || overScrolledBottom) {
boolean onTop;
float newAmount;
if (overScrolledTop) {
onTop = true;
- newAmount = -mOwnScrollY;
+ newAmount = -getOwnScrollY();
setOwnScrollY(0);
mDontReportNextOverScroll = true;
} else {
onTop = false;
- newAmount = mOwnScrollY - scrollRange;
+ newAmount = getOwnScrollY() - scrollRange;
setOwnScrollY(scrollRange);
}
setOverScrollAmount(newAmount, onTop, false);
@@ -2508,7 +2499,7 @@
}
// In current design, it only use the top HUN to treat all of HUNs
// although there are more than one HUNs
- int contentHeight = mContentHeight;
+ int contentHeight = getContentHeight();
if (!isExpanded() && mInHeadsUpPinnedMode) {
contentHeight = mHeadsUpInset + getTopHeadsUpPinnedHeight();
}
@@ -2644,16 +2635,16 @@
(int) scrimTopPadding + (int) mNotificationStackSizeCalculator.computeHeight(
/* notificationStackScrollLayout= */ this, mMaxDisplayedNotifications,
shelfIntrinsicHeight);
- mIntrinsicContentHeight = height;
+ setIntrinsicContentHeight(height);
// The topPadding can be bigger than the regular padding when qs is expanded, in that
// state the maxPanelHeight and the contentHeight should be bigger
- mContentHeight =
- (int) (height + Math.max(getIntrinsicPadding(), getTopPadding()) + mBottomPadding);
+ setContentHeight(
+ (int) (height + Math.max(getIntrinsicPadding(), getTopPadding()) + mBottomPadding));
updateScrollability();
clampScrollPosition();
updateStackPosition();
- mAmbientState.setContentHeight(mContentHeight);
+ mAmbientState.setContentHeight(getContentHeight());
}
@Override
@@ -2795,7 +2786,7 @@
float topAmount = getCurrentOverScrollAmount(true);
float bottomAmount = getCurrentOverScrollAmount(false);
if (velocityY < 0 && topAmount > 0) {
- setOwnScrollY(mOwnScrollY - (int) topAmount);
+ setOwnScrollY(getOwnScrollY() - (int) topAmount);
if (!mShouldUseSplitNotificationShade) {
mDontReportNextOverScroll = true;
setOverScrollAmount(0, true, false);
@@ -2803,7 +2794,7 @@
mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
* mOverflingDistance + topAmount;
} else if (velocityY > 0 && bottomAmount > 0) {
- setOwnScrollY((int) (mOwnScrollY + bottomAmount));
+ setOwnScrollY((int) (getOwnScrollY() + bottomAmount));
setOverScrollAmount(0, false, false);
mMaxOverScroll = Math.abs(velocityY) / 1000f
* getRubberBandFactor(false /* onTop */) * mOverflingDistance
@@ -2817,8 +2808,8 @@
if (mExpandedInThisMotion) {
minScrollY = Math.min(minScrollY, mMaxScrollAfterExpand);
}
- mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0, minScrollY, 0,
- mExpandedInThisMotion && mOwnScrollY >= 0 ? 0 : Integer.MAX_VALUE / 2);
+ mScroller.fling(mScrollX, getOwnScrollY(), 1, velocityY, 0, 0, 0, minScrollY, 0,
+ mExpandedInThisMotion && getOwnScrollY() >= 0 ? 0 : Integer.MAX_VALUE / 2);
animateScroll();
}
@@ -3139,11 +3130,11 @@
final int scrollBoundaryStart = getScrollAmountToScrollBoundary();
mAnimateStackYForContentHeightChange = true;
// This is reset onLayout
- if (endPosition <= mOwnScrollY - scrollBoundaryStart) {
+ if (endPosition <= getOwnScrollY() - scrollBoundaryStart) {
// This child is fully scrolled of the top, so we have to deduct its height from the
// scrollPosition
- setOwnScrollY(mOwnScrollY - childHeight);
- } else if (startingPosition < mOwnScrollY - scrollBoundaryStart) {
+ setOwnScrollY(getOwnScrollY() - childHeight);
+ } else if (startingPosition < getOwnScrollY() - scrollBoundaryStart) {
// This child is currently being scrolled into, set the scroll position to the
// start of this child
setOwnScrollY(startingPosition + scrollBoundaryStart);
@@ -3765,7 +3756,7 @@
if (vscroll != 0) {
final int delta = (int) (vscroll * getVerticalScrollFactor());
final int range = getScrollRange();
- int oldScrollY = mOwnScrollY;
+ int oldScrollY = getOwnScrollY();
int newScrollY = oldScrollY - delta;
if (newScrollY < 0) {
newScrollY = 0;
@@ -3882,7 +3873,7 @@
if (scrollAmount != 0.0f) {
// The scrolling motion could not be compensated with the
// existing overScroll, we have to scroll the view
- customOverScrollBy((int) scrollAmount, mOwnScrollY,
+ customOverScrollBy((int) scrollAmount, getOwnScrollY(),
range, getHeight() / 2);
// If we're scrolling, leavebehinds should be dismissed
mController.checkSnoozeLeavebehind();
@@ -3914,7 +3905,7 @@
onOverScrollFling(false, initialVelocity);
}
} else {
- if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
+ if (mScroller.springBack(mScrollX, getOwnScrollY(), 0, 0, 0,
getScrollRange())) {
animateScroll();
}
@@ -3928,7 +3919,7 @@
break;
case ACTION_CANCEL:
if (mIsBeingDragged && getChildCount() > 0) {
- if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
+ if (mScroller.springBack(mScrollX, getOwnScrollY(), 0, 0, 0,
getScrollRange())) {
animateScroll();
}
@@ -4214,7 +4205,7 @@
setIsBeingDragged(false);
mActivePointerId = INVALID_POINTER;
recycleVelocityTracker();
- if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
+ if (mScroller.springBack(mScrollX, getOwnScrollY(), 0, 0, 0, getScrollRange())) {
animateScroll();
}
break;
@@ -4312,10 +4303,10 @@
getHeight() - mPaddingBottom - getTopPadding() - mPaddingTop
- mShelf.getIntrinsicHeight();
final int targetScrollY = Math.max(0,
- Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
- if (targetScrollY != mOwnScrollY) {
- mScroller.startScroll(mScrollX, mOwnScrollY, 0,
- targetScrollY - mOwnScrollY);
+ Math.min(getOwnScrollY() + direction * viewportHeight, getScrollRange()));
+ if (targetScrollY != getOwnScrollY()) {
+ mScroller.startScroll(mScrollX, getOwnScrollY(), 0,
+ targetScrollY - getOwnScrollY());
animateScroll();
return true;
}
@@ -4357,9 +4348,9 @@
// it is based on notifications bottom, which is lower on split shade.
// Here we prefer to use at least a minimum height defined for split shade.
// Otherwise the expansion motion is too fast.
- contentHeight = Math.max(mSplitShadeMinContentHeight, mContentHeight);
+ contentHeight = Math.max(mSplitShadeMinContentHeight, getContentHeight());
} else {
- contentHeight = mContentHeight;
+ contentHeight = getContentHeight();
}
return Math.max(mMaxLayoutHeight - contentHeight, 0);
}
@@ -4569,7 +4560,7 @@
float diff = endPosition - layoutEnd;
mScrollViewFields.sendSyntheticScroll(diff);
}
- setOwnScrollY((int) (mOwnScrollY + endPosition - layoutEnd));
+ setOwnScrollY((int) (getOwnScrollY() + endPosition - layoutEnd));
mDisallowScrollingInThisMotion = true;
}
}
@@ -5080,7 +5071,7 @@
event.setScrollY(mScrollViewFields.getScrollState().getScrollPosition());
event.setMaxScrollY(mScrollViewFields.getScrollState().getMaxScrollPosition());
} else {
- event.setScrollY(mOwnScrollY);
+ event.setScrollY(getOwnScrollY());
event.setMaxScrollY(getScrollRange());
}
}
@@ -5286,8 +5277,8 @@
// If notifications are scrolled,
// clear out scrollY by the time we push notifications offscreen
- if (mOwnScrollY > 0) {
- setOwnScrollY((int) MathUtils.lerp(mOwnScrollY, 0, mQsExpansionFraction));
+ if (getOwnScrollY() > 0) {
+ setOwnScrollY((int) MathUtils.lerp(getOwnScrollY(), 0, mQsExpansionFraction));
}
if (!FooterViewRefactor.isEnabled() && footerAffected) {
updateFooter();
@@ -5295,6 +5286,15 @@
}
@VisibleForTesting
+ int getOwnScrollY() {
+ if (SceneContainerFlag.isEnabled()) {
+ return 0;
+ } else {
+ return mOwnScrollY;
+ }
+ }
+
+ @VisibleForTesting
void setOwnScrollY(int ownScrollY) {
setOwnScrollY(ownScrollY, false /* animateScrollChangeListener */);
}
@@ -5311,11 +5311,11 @@
return;
}
- if (ownScrollY != mOwnScrollY) {
+ if (ownScrollY != getOwnScrollY()) {
// We still want to call the normal scrolled changed for accessibility reasons
- onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY);
+ onScrollChanged(mScrollX, ownScrollY, mScrollX, getOwnScrollY());
mOwnScrollY = ownScrollY;
- mAmbientState.setScrollY(mOwnScrollY);
+ mAmbientState.setScrollY(getOwnScrollY());
updateOnScrollChange();
updateStackPosition(animateStackYChangeListener);
}
@@ -5324,7 +5324,7 @@
private void updateOnScrollChange() {
SceneContainerFlag.assertInLegacyMode();
if (mScrollListener != null) {
- mScrollListener.accept(mOwnScrollY);
+ mScrollListener.accept(getOwnScrollY());
}
updateForwardAndBackwardScrollability();
requestChildrenUpdate();
@@ -5517,12 +5517,7 @@
println(pw, "hideAmount", mAmbientState.getHideAmount());
println(pw, "ambientStateSwipingUp", mAmbientState.isSwipingUp());
println(pw, "maxDisplayedNotifications", mMaxDisplayedNotifications);
- println(pw, "intrinsicContentHeight", mIntrinsicContentHeight);
- println(pw, "contentHeight", mContentHeight);
println(pw, "intrinsicPadding", mIntrinsicPadding);
- if (!SceneContainerFlag.isEnabled()) {
- println(pw, "topPadding", getTopPadding());
- }
println(pw, "bottomPadding", mBottomPadding);
dumpRoundedRectClipping(pw);
println(pw, "requestedClipBounds", mRequestedClipBounds);
@@ -5547,6 +5542,12 @@
println(pw, "isSmallLandscapeLockscreenEnabled", mIsSmallLandscapeLockscreenEnabled);
mNotificationStackSizeCalculator.dump(pw, args);
mScrollViewFields.dump(pw);
+ if (!SceneContainerFlag.isEnabled()) {
+ // fields which will be removed with SceneContainer
+ println(pw, "intrinsicContentHeight", getIntrinsicContentHeight());
+ println(pw, "contentHeight", getContentHeight());
+ println(pw, "topPadding", getTopPadding());
+ }
});
pw.println();
pw.println("Contents:");
@@ -6891,7 +6892,7 @@
public void expansionStateChanged(boolean isExpanding) {
mExpandingNotification = isExpanding;
if (!mExpandedInThisMotion) {
- mMaxScrollAfterExpand = mOwnScrollY;
+ mMaxScrollAfterExpand = getOwnScrollY();
mExpandedInThisMotion = true;
}
}
@@ -6947,4 +6948,31 @@
void onAnimationEnd(
List<ExpandableNotificationRow> viewsToRemove, @SelectedRows int selectedRows);
}
+
+ // -------------------- Getters / Setters for the SceneContainer refactor ----------------------
+
+ /** Use {@link ScrollViewFields#intrinsicStackHeight}, when SceneContainerFlag is enabled. */
+ private int getContentHeight() {
+ SceneContainerFlag.assertInLegacyMode();
+ return mContentHeight;
+ }
+
+ private void setContentHeight(int contentHeight) {
+ SceneContainerFlag.assertInLegacyMode();
+ mContentHeight = contentHeight;
+ }
+
+ /**
+ * Use {@link ScrollViewFields#intrinsicStackHeight}, when SceneContainerFlag is enabled.
+ * @return the height of the content ignoring the footer.
+ */
+ public float getIntrinsicContentHeight() {
+ SceneContainerFlag.assertInLegacyMode();
+ return mIntrinsicContentHeight;
+ }
+
+ private void setIntrinsicContentHeight(float intrinsicContentHeight) {
+ SceneContainerFlag.assertInLegacyMode();
+ mIntrinsicContentHeight = intrinsicContentHeight;
+ }
}
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 04974b4..dc1a191 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
@@ -43,7 +43,6 @@
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.util.Log;
-import android.util.Pair;
import android.util.Property;
import android.view.Display;
import android.view.MotionEvent;
@@ -105,6 +104,7 @@
import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.collection.EntryWithDismissStats;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -1188,7 +1188,7 @@
public int getIntrinsicContentHeight() {
SceneContainerFlag.assertInLegacyMode();
- return mView.getIntrinsicContentHeight();
+ return (int) mView.getIntrinsicContentHeight();
}
/**
@@ -1777,12 +1777,12 @@
mNotifCollection.dismissAllNotifications(
mLockscreenUserManager.getCurrentUserId());
} else {
- final List<Pair<NotificationEntry, DismissedByUserStats>>
+ final List<EntryWithDismissStats>
entriesWithRowsDismissedFromShade = new ArrayList<>();
for (ExpandableNotificationRow row : viewsToRemove) {
final NotificationEntry entry = row.getEntry();
entriesWithRowsDismissedFromShade.add(
- new Pair<>(entry, getDismissedByUserStats(entry)));
+ new EntryWithDismissStats(entry, getDismissedByUserStats(entry)));
}
mNotifCollection.dismissNotifications(entriesWithRowsDismissedFromShade);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
index 6042964..e644815 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
@@ -25,7 +25,7 @@
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.LargeScreenHeaderHelper
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.policy.SplitShadeStateController
import dagger.Lazy
import javax.inject.Inject
@@ -45,9 +45,8 @@
class SharedNotificationContainerInteractor
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val splitShadeStateController: Lazy<SplitShadeStateController>,
- private val shadeInteractor: Lazy<ShadeInteractor>,
configurationInteractor: ConfigurationInteractor,
keyguardInteractor: KeyguardInteractor,
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
@@ -66,23 +65,14 @@
* distinctUntilChanged() to this would cause configurationBasedDimensions to miss configuration
* updates that affect other resources, like margins or the large screen header flag.
*/
- private val dimensionsUpdateEventsWithShouldUseSplitShade: Flow<Boolean> =
- if (SceneContainerFlag.isEnabled) {
- combine(
- configurationInteractor.onAnyConfigurationChange,
- shadeInteractor.get().isShadeLayoutWide,
- ) { _, isShadeLayoutWide ->
- isShadeLayoutWide
- }
- } else {
- configurationInteractor.onAnyConfigurationChange.map {
- splitShadeStateController.get().shouldUseSplitNotificationShade(context.resources)
- }
- }
-
+ @Deprecated("Use SharedNotificationContainerViewModel.ConfigurationBasedDimensions instead")
val configurationBasedDimensions: Flow<ConfigurationBasedDimensions> =
- dimensionsUpdateEventsWithShouldUseSplitShade
- .map { shouldUseSplitShade ->
+ configurationInteractor.onAnyConfigurationChange
+ .map {
+ val shouldUseSplitShade =
+ splitShadeStateController
+ .get()
+ .shouldUseSplitNotificationShade(context.resources)
with(context.resources) {
ConfigurationBasedDimensions(
useSplitShade = shouldUseSplitShade,
@@ -101,6 +91,10 @@
}
}
.distinctUntilChanged()
+ get() {
+ SceneContainerFlag.assertInLegacyMode()
+ return field
+ }
/**
* The notification shelf can extend over the lock icon area if:
@@ -125,6 +119,7 @@
_notificationStackChanged.value = _notificationStackChanged.value + 1
}
+ @Deprecated("Use SharedNotificationContainerViewModel.ConfigurationBasedDimensions instead")
data class ConfigurationBasedDimensions(
val useSplitShade: Boolean,
val useLargeScreenHeader: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index 787ff02..fd19f1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -22,6 +22,7 @@
import com.android.app.tracing.TraceUtils.traceAsync
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.nano.MetricsProto
+import com.android.systemui.Flags
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.common.ui.view.setImportantForAccessibilityYesNo
import com.android.systemui.dagger.qualifiers.Background
@@ -145,7 +146,9 @@
// The footer needs to be re-inflated every time the theme or the font size changes.
configuration
.inflateLayout<FooterView>(
- R.layout.status_bar_notification_footer,
+ if (Flags.notificationsRedesignFooterView())
+ R.layout.status_bar_notification_footer_redesign
+ else R.layout.status_bar_notification_footer,
parentView,
attachToRoot = false,
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
index 54b5ca3..ce89d78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
@@ -18,10 +18,12 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.Flags
import com.android.systemui.common.ui.view.onLayoutChanged
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -48,8 +50,19 @@
private val notificationScrollViewBinder: NotificationScrollViewBinder,
private val communalSettingsInteractor: CommunalSettingsInteractor,
@Main private val mainImmediateDispatcher: CoroutineDispatcher,
+ val keyguardInteractor: KeyguardInteractor,
) {
+ private val calculateMaxNotifications: (Float, Boolean) -> Int = { space, extraShelfSpace ->
+ val shelfHeight = controller.getShelfHeight().toFloat()
+ notificationStackSizeCalculator.computeMaxKeyguardNotifications(
+ controller.view,
+ space,
+ if (extraShelfSpace) shelfHeight else 0f,
+ shelfHeight,
+ )
+ }
+
fun bind(
view: SharedNotificationContainer,
viewModel: SharedNotificationContainerViewModel,
@@ -107,17 +120,9 @@
}
launch {
- viewModel
- .getMaxNotifications { space, extraShelfSpace ->
- val shelfHeight = controller.getShelfHeight().toFloat()
- notificationStackSizeCalculator.computeMaxKeyguardNotifications(
- controller.getView(),
- space,
- if (extraShelfSpace) shelfHeight else 0f,
- shelfHeight,
- )
- }
- .collect { controller.setMaxDisplayedNotifications(it) }
+ viewModel.getMaxNotifications(calculateMaxNotifications).collect {
+ controller.setMaxDisplayedNotifications(it)
+ }
}
if (!SceneContainerFlag.isEnabled) {
@@ -136,6 +141,30 @@
}
}
+ if (!SceneContainerFlag.isEnabled) {
+ if (Flags.magicPortraitWallpapers()) {
+ launch {
+ viewModel
+ .getNotificationStackAbsoluteBottom(
+ calculateMaxNotifications = calculateMaxNotifications,
+ calculateHeight = { maxNotifications ->
+ notificationStackSizeCalculator.computeHeight(
+ maxNotifs = maxNotifications,
+ shelfHeight = controller.getShelfHeight().toFloat(),
+ stack = controller.view,
+ )
+ },
+ controller.getShelfHeight().toFloat(),
+ )
+ .collect { bottom ->
+ keyguardInteractor.setNotificationStackAbsoluteBottom(
+ bottom
+ )
+ }
+ }
+ }
+ }
+
launch { viewModel.translationX.collect { x -> controller.translationX = x } }
launch {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 9515029..e6663d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -19,9 +19,11 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+import android.content.Context
import androidx.annotation.VisibleForTesting
import com.android.app.tracing.coroutines.flow.flowName
import com.android.systemui.common.shared.model.NotificationContainerBounds
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -66,10 +68,14 @@
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
+import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.LargeScreenHeaderHelper
import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.shade.shared.model.ShadeMode.Dual
+import com.android.systemui.shade.shared.model.ShadeMode.Single
+import com.android.systemui.shade.shared.model.ShadeMode.Split
import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
@@ -90,6 +96,7 @@
import kotlinx.coroutines.flow.combineTransform
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
@@ -108,6 +115,8 @@
private val interactor: SharedNotificationContainerInteractor,
dumpManager: DumpManager,
@Application applicationScope: CoroutineScope,
+ private val context: Context,
+ configurationInteractor: ConfigurationInteractor,
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val shadeInteractor: ShadeInteractor,
@@ -145,6 +154,7 @@
private val communalSceneInteractor: CommunalSceneInteractor,
// Lazy because it's only used in the SceneContainer + Dual Shade configuration.
headsUpNotificationInteractor: Lazy<HeadsUpNotificationInteractor>,
+ private val largeScreenHeaderHelperLazy: Lazy<LargeScreenHeaderHelper>,
unfoldTransitionInteractor: UnfoldTransitionInteractor,
) : FlowDumperImpl(dumpManager) {
@@ -187,33 +197,62 @@
@VisibleForTesting
val paddingTopDimen: Flow<Int> =
- interactor.configurationBasedDimensions
- .map {
- when {
- it.useLargeScreenHeader -> it.marginTopLargeScreen
- else -> it.marginTop
+ if (SceneContainerFlag.isEnabled) {
+ configurationInteractor.onAnyConfigurationChange.map {
+ with(context.resources) {
+ val useLargeScreenHeader =
+ getBoolean(R.bool.config_use_large_screen_shade_header)
+ if (useLargeScreenHeader) {
+ largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
+ } else {
+ getDimensionPixelSize(R.dimen.notification_panel_margin_top)
+ }
+ }
+ }
+ } else {
+ interactor.configurationBasedDimensions.map {
+ when {
+ it.useLargeScreenHeader -> it.marginTopLargeScreen
+ else -> it.marginTop
+ }
}
}
.distinctUntilChanged()
.dumpWhileCollecting("paddingTopDimen")
val configurationBasedDimensions: Flow<ConfigurationBasedDimensions> =
- interactor.configurationBasedDimensions
- .map {
- val marginTop =
- when {
- // y position of the NSSL in the window needs to be 0 under scene container
- SceneContainerFlag.isEnabled -> 0
- it.useLargeScreenHeader -> it.marginTopLargeScreen
- else -> it.marginTop
+ if (SceneContainerFlag.isEnabled) {
+ combine(
+ shadeInteractor.isShadeLayoutWide,
+ configurationInteractor.onAnyConfigurationChange,
+ ) { isShadeLayoutWide, _ ->
+ with(context.resources) {
+ // TODO(b/338033836): Define separate horizontal margins for dual shade.
+ val marginHorizontal =
+ getDimensionPixelSize(R.dimen.notification_panel_margin_horizontal)
+ ConfigurationBasedDimensions(
+ marginStart = if (isShadeLayoutWide) 0 else marginHorizontal,
+ marginEnd = marginHorizontal,
+ marginBottom =
+ getDimensionPixelSize(R.dimen.notification_panel_margin_bottom),
+ // y position of the NSSL in the window needs to be 0 under scene
+ // container
+ marginTop = 0,
+ useSplitShade = isShadeLayoutWide,
+ )
}
- ConfigurationBasedDimensions(
- marginStart = if (it.useSplitShade) 0 else it.marginHorizontal,
- marginEnd = it.marginHorizontal,
- marginBottom = it.marginBottom,
- marginTop = marginTop,
- useSplitShade = it.useSplitShade,
- )
+ }
+ } else {
+ interactor.configurationBasedDimensions.map {
+ ConfigurationBasedDimensions(
+ marginStart = if (it.useSplitShade) 0 else it.marginHorizontal,
+ marginEnd = it.marginHorizontal,
+ marginBottom = it.marginBottom,
+ marginTop =
+ if (it.useLargeScreenHeader) it.marginTopLargeScreen else it.marginTop,
+ useSplitShade = it.useSplitShade,
+ )
+ }
}
.distinctUntilChanged()
.dumpWhileCollecting("configurationBasedDimensions")
@@ -221,13 +260,15 @@
/** If the user is visually on one of the unoccluded lockscreen states. */
val isOnLockscreen: Flow<Boolean> =
anyOf(
- keyguardTransitionInteractor.isFinishedIn(AOD),
- keyguardTransitionInteractor.isFinishedIn(DOZING),
- keyguardTransitionInteractor.isFinishedIn(ALTERNATE_BOUNCER),
- keyguardTransitionInteractor.isFinishedIn(
- scene = Scenes.Bouncer,
- stateWithoutSceneContainer = PRIMARY_BOUNCER,
- ),
+ keyguardTransitionInteractor.transitionValue(AOD).map { it > 0f },
+ keyguardTransitionInteractor.transitionValue(DOZING).map { it > 0f },
+ keyguardTransitionInteractor.transitionValue(ALTERNATE_BOUNCER).map { it > 0f },
+ keyguardTransitionInteractor
+ .transitionValue(
+ scene = Scenes.Bouncer,
+ stateWithoutSceneContainer = PRIMARY_BOUNCER,
+ )
+ .map { it > 0f },
keyguardTransitionInteractor.transitionValue(LOCKSCREEN).map { it > 0f },
)
.flowName("isOnLockscreen")
@@ -404,42 +445,60 @@
* notifications unless in splitshade.
*/
private val alphaForShadeAndQsExpansion: Flow<Float> =
- if (DualShade.isEnabled) {
- combineTransform(
- headsUpNotificationInteractor.get().isHeadsUpOrAnimatingAway,
- shadeInteractor.shadeExpansion,
- shadeInteractor.qsExpansion,
- ) { isHeadsUpOrAnimatingAway, shadeExpansion, qsExpansion ->
- if (isHeadsUpOrAnimatingAway) {
- // Ensure HUNs will be visible in QS shade (at least while unlocked)
- emit(1f)
- } else if (shadeExpansion > 0f || qsExpansion > 0f) {
- // Fade out as QS shade expands
- emit(1f - qsExpansion)
- }
- }
- } else {
- interactor.configurationBasedDimensions.flatMapLatest { configurationBasedDimensions
- ->
- combineTransform(shadeInteractor.shadeExpansion, shadeInteractor.qsExpansion) {
- shadeExpansion,
- qsExpansion ->
- if (shadeExpansion > 0f || qsExpansion > 0f) {
- if (configurationBasedDimensions.useSplitShade) {
- emit(1f)
- } else if (qsExpansion == 1f) {
+ if (SceneContainerFlag.isEnabled) {
+ shadeInteractor.shadeMode.flatMapLatest { shadeMode ->
+ when (shadeMode) {
+ Single ->
+ combineTransform(
+ shadeInteractor.shadeExpansion,
+ shadeInteractor.qsExpansion,
+ ) { shadeExpansion, qsExpansion ->
+ if (qsExpansion == 1f) {
// Ensure HUNs will be visible in QS shade (at least while unlocked)
emit(1f)
- } else {
+ } else if (shadeExpansion > 0f || qsExpansion > 0f) {
// Fade as QS shade expands
emit(1f - qsExpansion)
}
}
+ Split -> isAnyExpanded.filter { it }.map { 1f }
+ Dual ->
+ combineTransform(
+ headsUpNotificationInteractor.get().isHeadsUpOrAnimatingAway,
+ shadeInteractor.shadeExpansion,
+ shadeInteractor.qsExpansion,
+ ) { isHeadsUpOrAnimatingAway, shadeExpansion, qsExpansion ->
+ if (isHeadsUpOrAnimatingAway) {
+ // Ensure HUNs will be visible in QS shade (at least while unlocked)
+ emit(1f)
+ } else if (shadeExpansion > 0f || qsExpansion > 0f) {
+ // Fade out as QS shade expands
+ emit(1f - qsExpansion)
+ }
+ }
+ }
+ }
+ } else {
+ interactor.configurationBasedDimensions.flatMapLatest { configurationBasedDimensions ->
+ combineTransform(shadeInteractor.shadeExpansion, shadeInteractor.qsExpansion) {
+ shadeExpansion,
+ qsExpansion ->
+ if (shadeExpansion > 0f || qsExpansion > 0f) {
+ if (configurationBasedDimensions.useSplitShade) {
+ emit(1f)
+ } else if (qsExpansion == 1f) {
+ // Ensure HUNs will be visible in QS shade (at least while unlocked)
+ emit(1f)
+ } else {
+ // Fade as QS shade expands
+ emit(1f - qsExpansion)
+ }
}
}
}
- .onStart { emit(1f) }
- .dumpWhileCollecting("alphaForShadeAndQsExpansion")
+ }
+ .onStart { emit(1f) }
+ .dumpWhileCollecting("alphaForShadeAndQsExpansion")
val panelAlpha = keyguardInteractor.panelAlpha
@@ -672,6 +731,36 @@
.dumpWhileCollecting("maxNotifications")
}
+ /**
+ * Wallpaper needs the absolute bottom of notification stack to avoid occlusion
+ *
+ * @param calculateMaxNotifications is required by getMaxNotifications as calculateSpace by
+ * calling computeMaxKeyguardNotifications in NotificationStackSizeCalculator
+ * @param calculateHeight is calling computeHeight in NotificationStackSizeCalculator The edge
+ * case is that when maxNotifications is 0, we won't take shelfHeight into account
+ */
+ fun getNotificationStackAbsoluteBottom(
+ calculateMaxNotifications: (Float, Boolean) -> Int,
+ calculateHeight: (Int) -> Float,
+ shelfHeight: Float,
+ ): Flow<Float> {
+ SceneContainerFlag.assertInLegacyMode()
+
+ return combine(
+ getMaxNotifications(calculateMaxNotifications).map {
+ val height = calculateHeight(it)
+ if (it == 0) {
+ height - shelfHeight
+ } else {
+ height
+ }
+ },
+ bounds.map { it.top },
+ ) { height, top ->
+ top + height
+ }
+ }
+
fun notificationStackChanged() {
interactor.notificationStackChanged()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index b469e3d..65663fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -646,6 +646,8 @@
NavigationBarController navigationBarController,
AccessibilityFloatingMenuController accessibilityFloatingMenuController,
Lazy<AssistManager> assistManagerLazy,
+ // TODO: b/374267505 - Decouple the config change needed for shade window classes from
+ // the one for other windows.
ConfigurationController configurationController,
NotificationShadeWindowController notificationShadeWindowController,
Lazy<NotificationShadeWindowViewController> notificationShadeWindowViewControllerLazy,
@@ -1975,6 +1977,10 @@
* meantime, just update the things that we know change.
*/
void updateResources() {
+ // TODO: b/374267505 - we shouldn't propagate this from here. Each class should be
+ // listening at the correct configuration change. For example, shade window classes should
+ // be listening at @ShadeDisplayAware configurations (as it can be on a different display.
+
// Update the quick setting tiles
if (mQSPanelController != null) {
mQSPanelController.updateResources();
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
index 3d125b8..fa108842 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
@@ -105,8 +105,8 @@
scope.trySend(VolumeDialogEventModel.ShowSafetyWarning(flags))
}
- override fun onAccessibilityModeChanged(showA11yStream: Boolean) {
- scope.trySend(VolumeDialogEventModel.AccessibilityModeChanged(showA11yStream))
+ override fun onAccessibilityModeChanged(showA11yStream: Boolean?) {
+ scope.trySend(VolumeDialogEventModel.AccessibilityModeChanged(showA11yStream == true))
}
// Captions button is remove from the Volume Dialog
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
index 2668589b..fb108c5 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
@@ -60,7 +60,7 @@
) {
@SuppressLint("SharedFlowCreation")
- private val mutableDismissDialogEvents = MutableSharedFlow<Unit>()
+ private val mutableDismissDialogEvents = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
val dialogVisibility: Flow<VolumeDialogVisibilityModel> = repository.dialogVisibility
init {
@@ -74,7 +74,7 @@
.mapNotNull { it.toVisibilityModel() }
.onEach { model ->
updateVisibility { model }
- if (model is VolumeDialogVisibilityModel.Visible) {
+ if (model is Visible) {
resetDismissTimeout()
}
}
@@ -87,17 +87,17 @@
*/
fun dismissDialog(reason: Int) {
updateVisibility { visibilityModel ->
- if (visibilityModel is VolumeDialogVisibilityModel.Dismissed) {
+ if (visibilityModel is Dismissed) {
visibilityModel
} else {
- VolumeDialogVisibilityModel.Dismissed(reason)
+ Dismissed(reason)
}
}
}
/** Resets current dialog timeout. */
- suspend fun resetDismissTimeout() {
- mutableDismissDialogEvents.emit(Unit)
+ fun resetDismissTimeout() {
+ mutableDismissDialogEvents.tryEmit(Unit)
}
private fun updateVisibility(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
new file mode 100644
index 0000000..c05a0b2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.ringer.ui.binder
+
+import android.view.View
+import com.android.systemui.lifecycle.WindowLifecycleState
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.lifecycle.setSnapshotBinding
+import com.android.systemui.lifecycle.viewModel
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.ringer.ui.viewmodel.VolumeDialogRingerDrawerViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.awaitCancellation
+
+@VolumeDialogScope
+class VolumeDialogRingerViewBinder
+@Inject
+constructor(private val viewModelFactory: VolumeDialogRingerDrawerViewModel.Factory) {
+
+ fun bind(view: View) {
+ with(view) {
+ repeatWhenAttached {
+ viewModel(
+ traceName = "VolumeDialogRingerViewBinder",
+ minWindowLifecycleState = WindowLifecycleState.ATTACHED,
+ factory = { viewModelFactory.create() },
+ ) { viewModel ->
+ setSnapshotBinding {}
+ awaitCancellation()
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
index f78a8dc..876bf2c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
@@ -25,6 +25,7 @@
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.mapNotNull
/** Operates a state of particular slider of the Volume Dialog. */
@@ -37,12 +38,23 @@
) {
val slider: Flow<VolumeDialogStreamModel> =
- volumeDialogStateInteractor.volumeDialogState.mapNotNull {
- it.streamModels[sliderType.audioStream]
- }
+ volumeDialogStateInteractor.volumeDialogState
+ .mapNotNull {
+ it.streamModels[sliderType.audioStream]?.run {
+ if (level < levelMin || level > levelMax) {
+ copy(level = level.coerceIn(levelMin, levelMax))
+ } else {
+ this
+ }
+ }
+ }
+ .distinctUntilChanged()
fun setStreamVolume(userLevel: Int) {
- volumeDialogController.setStreamVolume(sliderType.audioStream, userLevel)
+ with(volumeDialogController) {
+ setStreamVolume(sliderType.audioStream, userLevel)
+ setActiveStream(sliderType.audioStream)
+ }
}
@VolumeDialogScope
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
index 25a5f28..5c4d53a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
@@ -16,32 +16,55 @@
package com.android.systemui.volume.dialog.sliders.ui
+import android.animation.Animator
+import android.animation.ObjectAnimator
import android.view.View
-import androidx.lifecycle.viewmodel.compose.viewModel
+import android.view.animation.DecelerateInterpolator
import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.lifecycle.setSnapshotBinding
import com.android.systemui.lifecycle.viewModel
+import com.android.systemui.res.R
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel
+import com.android.systemui.volume.dialog.ui.utils.JankListenerFactory
+import com.android.systemui.volume.dialog.ui.utils.awaitAnimation
+import com.google.android.material.slider.LabelFormatter
+import com.google.android.material.slider.Slider
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import kotlin.math.roundToInt
import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+
+private const val PROGRESS_CHANGE_ANIMATION_DURATION_MS = 80L
class VolumeDialogSliderViewBinder
@AssistedInject
-constructor(@Assisted private val viewModelProvider: () -> VolumeDialogSliderViewModel) {
+constructor(
+ @Assisted private val viewModelProvider: () -> VolumeDialogSliderViewModel,
+ private val jankListenerFactory: JankListenerFactory,
+) {
fun bind(view: View) {
with(view) {
+ val sliderView: Slider =
+ requireViewById<Slider>(R.id.volume_dialog_slider).apply {
+ labelBehavior = LabelFormatter.LABEL_GONE
+ }
repeatWhenAttached {
viewModel(
traceName = "VolumeDialogSliderViewBinder",
minWindowLifecycleState = WindowLifecycleState.ATTACHED,
factory = { viewModelProvider() },
) { viewModel ->
- setSnapshotBinding {}
+ sliderView.addOnChangeListener { _, value, fromUser ->
+ viewModel.setStreamVolume(value.roundToInt(), fromUser)
+ }
+
+ viewModel.model.onEach { it.bindToSlider(sliderView) }.launchIn(this)
awaitCancellation()
}
@@ -49,6 +72,19 @@
}
}
+ private suspend fun VolumeDialogStreamModel.bindToSlider(slider: Slider) {
+ with(slider) {
+ valueFrom = levelMin.toFloat()
+ valueTo = levelMax.toFloat()
+ // coerce the current value to the new value range before animating it
+ value = value.coerceIn(valueFrom, valueTo)
+ setValueAnimated(
+ level.toFloat(),
+ jankListenerFactory.update(this, PROGRESS_CHANGE_ANIMATION_DURATION_MS),
+ )
+ }
+ }
+
@AssistedFactory
@VolumeDialogScope
interface Factory {
@@ -58,3 +94,16 @@
): VolumeDialogSliderViewBinder
}
}
+
+private suspend fun Slider.setValueAnimated(
+ newValue: Float,
+ jankListener: Animator.AnimatorListener,
+) {
+ ObjectAnimator.ofFloat(value, newValue)
+ .apply {
+ duration = PROGRESS_CHANGE_ANIMATION_DURATION_MS
+ interpolator = DecelerateInterpolator()
+ addListener(jankListener)
+ }
+ .awaitAnimation<Float> { value = it }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
index 0a00f70..f486fe1 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
@@ -16,14 +16,20 @@
package com.android.systemui.volume.dialog.sliders.ui
+import android.view.LayoutInflater
import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.LayoutRes
+import androidx.compose.ui.util.fastForEachIndexed
import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.lifecycle.setSnapshotBinding
import com.android.systemui.lifecycle.viewModel
+import com.android.systemui.res.R
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSlidersViewModel
import javax.inject.Inject
+import kotlin.math.abs
import kotlinx.coroutines.awaitCancellation
@VolumeDialogScope
@@ -33,17 +39,44 @@
fun bind(view: View) {
with(view) {
+ val volumeDialog: View = requireViewById(R.id.volume_dialog)
+ val floatingSlidersContainer: ViewGroup =
+ requireViewById(R.id.volume_dialog_floating_sliders_container)
repeatWhenAttached {
viewModel(
traceName = "VolumeDialogSlidersViewBinder",
minWindowLifecycleState = WindowLifecycleState.ATTACHED,
factory = { viewModelFactory.create() },
) { viewModel ->
- setSnapshotBinding {}
+ setSnapshotBinding {
+ viewModel.uiModel?.sliderViewBinder?.bind(volumeDialog)
+ val floatingSliderViewBinders =
+ viewModel.uiModel?.floatingSliderViewBinders ?: emptyList()
+ floatingSlidersContainer.ensureChildCount(
+ viewLayoutId = R.layout.volume_dialog_slider_floating,
+ count = floatingSliderViewBinders.size,
+ )
+ floatingSliderViewBinders.fastForEachIndexed { index, viewBinder ->
+ viewBinder.bind(floatingSlidersContainer.getChildAt(index))
+ }
+ }
awaitCancellation()
}
}
}
}
}
+
+private fun ViewGroup.ensureChildCount(@LayoutRes viewLayoutId: Int, count: Int) {
+ val childCountDelta = childCount - count
+ when {
+ childCountDelta > 0 -> {
+ removeViews(0, childCountDelta)
+ }
+ childCountDelta < 0 -> {
+ val inflater = LayoutInflater.from(context)
+ repeat(abs(childCountDelta)) { inflater.inflate(viewLayoutId, this, true) }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
index 7ee722d..ea0b49d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
@@ -16,26 +16,85 @@
package com.android.systemui.volume.dialog.sliders.ui.viewmodel
+import com.android.systemui.util.time.SystemClock
+import com.android.systemui.volume.Events
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
+import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.stateIn
+/*
+ This prevents volume slider updates while user interacts with it. This is needed due to the
+ flawed VolumeDialogControllerImpl. It has a single threaded message queue that handles all state
+ updates and doesn't skip sequential updates of the same stream. This leads to a bottleneck when
+ user rigorously adjusts the slider.
+
+ Remove this when getting rid of the VolumeDialogControllerImpl as this doesn't happen in the
+ Volume Panel that uses the new coroutine-backed AudioRepository.
+*/
+// TODO(b/375355785) remove this
+private const val VOLUME_UPDATE_GRACE_PERIOD = 1000
+
+@OptIn(ExperimentalCoroutinesApi::class)
class VolumeDialogSliderViewModel
@AssistedInject
-constructor(@Assisted private val interactor: VolumeDialogSliderInteractor) {
+constructor(
+ @Assisted private val interactor: VolumeDialogSliderInteractor,
+ private val visibilityInteractor: VolumeDialogVisibilityInteractor,
+ @VolumeDialog private val coroutineScope: CoroutineScope,
+ private val systemClock: SystemClock,
+) {
- val model: Flow<VolumeDialogStreamModel> = interactor.slider
+ private val userVolumeUpdates = MutableStateFlow<VolumeUpdate?>(null)
- fun setStreamVolume(volume: Int) {
- interactor.setStreamVolume(volume)
+ val model: Flow<VolumeDialogStreamModel> =
+ interactor.slider
+ .filter {
+ val lastVolumeUpdateTime = userVolumeUpdates.value?.timestampMillis ?: 0
+ getTimestampMillis() - lastVolumeUpdateTime > VOLUME_UPDATE_GRACE_PERIOD
+ }
+ .stateIn(coroutineScope, SharingStarted.Eagerly, null)
+ .filterNotNull()
+
+ init {
+ userVolumeUpdates
+ .filterNotNull()
+ .mapLatest { volume ->
+ interactor.setStreamVolume(volume.newVolumeLevel)
+ Events.writeEvent(Events.EVENT_TOUCH_LEVEL_CHANGED, model.first().stream, volume)
+ }
+ .launchIn(coroutineScope)
}
+ fun setStreamVolume(volume: Int, fromUser: Boolean) {
+ if (fromUser) {
+ visibilityInteractor.resetDismissTimeout()
+ userVolumeUpdates.value =
+ VolumeUpdate(newVolumeLevel = volume, timestampMillis = getTimestampMillis())
+ }
+ }
+
+ private fun getTimestampMillis(): Long = systemClock.uptimeMillis()
+
@AssistedFactory
interface Factory {
fun create(interactor: VolumeDialogSliderInteractor): VolumeDialogSliderViewModel
}
+
+ private data class VolumeUpdate(val newVolumeLevel: Int, val timestampMillis: Long)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
index b5b292f..22cf89f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
@@ -16,6 +16,9 @@
package com.android.systemui.volume.dialog.sliders.ui.viewmodel
+import androidx.compose.runtime.getValue
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSlidersInteractor
@@ -24,10 +27,9 @@
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -39,9 +41,10 @@
private val sliderInteractorFactory: VolumeDialogSliderInteractor.Factory,
private val sliderViewModelFactory: VolumeDialogSliderViewModel.Factory,
private val sliderViewBinderFactory: VolumeDialogSliderViewBinder.Factory,
-) {
+) : ExclusiveActivatable() {
- val sliders: Flow<VolumeDialogSliderUiModel> =
+ private val hydrator = Hydrator("VolumeDialogSlidersViewModel")
+ private val slidersStateFlow: StateFlow<VolumeDialogSliderUiModel?> =
slidersInteractor.sliders
.distinctUntilChanged()
.map { slidersModel ->
@@ -52,7 +55,13 @@
)
}
.stateIn(coroutineScope, SharingStarted.Eagerly, null)
- .filterNotNull()
+
+ val uiModel: VolumeDialogSliderUiModel? by
+ hydrator.hydratedStateOf("VolumeDialogSlidersViewModel#uiModel", slidersStateFlow)
+
+ override suspend fun onActivated(): Nothing {
+ hydrator.activate()
+ }
private fun createSliderViewBinder(type: VolumeDialogSliderType): VolumeDialogSliderViewBinder =
sliderViewBinderFactory.create {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt
index 77733fe..cd535e4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt
@@ -20,13 +20,16 @@
import android.graphics.Color
import android.graphics.PixelFormat
import android.graphics.drawable.ColorDrawable
+import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.view.WindowManager
import com.android.systemui.res.R
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.ringer.ui.binder.VolumeDialogRingerViewBinder
import com.android.systemui.volume.dialog.settings.ui.binder.VolumeDialogSettingsButtonViewBinder
+import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSlidersViewBinder
import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogGravityViewModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -40,6 +43,8 @@
constructor(
@VolumeDialog private val coroutineScope: CoroutineScope,
private val volumeDialogViewBinder: VolumeDialogViewBinder,
+ private val slidersViewBinder: VolumeDialogSlidersViewBinder,
+ private val volumeDialogRingerViewBinder: VolumeDialogRingerViewBinder,
private val settingsButtonViewBinder: VolumeDialogSettingsButtonViewBinder,
private val gravityViewModel: VolumeDialogGravityViewModel,
) {
@@ -50,11 +55,12 @@
dialog.setContentView(R.layout.volume_dialog)
dialog.setCanceledOnTouchOutside(true)
- settingsButtonViewBinder.bind(dialog.requireViewById(R.id.volume_dialog_settings))
- volumeDialogViewBinder.bind(
- dialog,
- dialog.requireViewById(R.id.volume_dialog_container),
- )
+ with(dialog.requireViewById<View>(R.id.volume_dialog_container)) {
+ volumeDialogRingerViewBinder.bind(this)
+ slidersViewBinder.bind(this)
+ settingsButtonViewBinder.bind(this)
+ volumeDialogViewBinder.bind(dialog, this)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
index 54953c9..9055d18 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
@@ -35,6 +35,4 @@
override val wallpaperInfo: StateFlow<WallpaperInfo?> = MutableStateFlow(null).asStateFlow()
override val wallpaperSupportsAmbientMode = MutableStateFlow(false).asStateFlow()
override var rootView: View? = null
-
- override fun setNotificationStackAbsoluteBottom(bottom: Float) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
index 0f6e960..015b480 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
@@ -45,7 +45,6 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
@@ -64,12 +63,6 @@
/** Set rootView to get its windowToken afterwards */
var rootView: View?
-
- /**
- * Set bottom of notifications from notification stack, and Magic Portrait will layout base on
- * this value
- */
- fun setNotificationStackAbsoluteBottom(bottom: Float)
}
@SysUISingleton
@@ -106,7 +99,8 @@
.filter { it.selectionStatus == SelectionStatus.SELECTION_COMPLETE }
/** The bottom of notification stack respect to the top of screen. */
- private val notificationStackAbsoluteBottom: MutableStateFlow<Float> = MutableStateFlow(0F)
+ private val notificationStackAbsoluteBottom: StateFlow<Float> =
+ keyguardRepository.notificationStackAbsoluteBottom
/** The top of shortcut respect to the top of screen. */
private val shortcutAbsoluteTop: StateFlow<Float> = keyguardRepository.shortcutAbsoluteTop
@@ -206,10 +200,6 @@
initialValue = false,
)
- override fun setNotificationStackAbsoluteBottom(bottom: Float) {
- notificationStackAbsoluteBottom.value = bottom
- }
-
private suspend fun getWallpaper(selectedUser: SelectedUserModel): WallpaperInfo? {
return withContext(bgDispatcher) {
wallpaperManager.getWallpaperInfoForUser(selectedUser.userInfo.id)
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt
index fe6977c..88795ca 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt
@@ -21,10 +21,6 @@
import kotlinx.coroutines.flow.StateFlow
class WallpaperInteractor @Inject constructor(val wallpaperRepository: WallpaperRepository) {
- fun setNotificationStackAbsoluteBottom(bottom: Float) {
- wallpaperRepository.setNotificationStackAbsoluteBottom(bottom)
- }
-
val wallpaperSupportsAmbientMode: StateFlow<Boolean> =
wallpaperRepository.wallpaperSupportsAmbientMode
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
index 8aaa121..3c922dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
@@ -13,6 +13,7 @@
*/
package com.android.systemui.qs
+import android.content.res.Configuration
import android.graphics.Rect
import android.platform.test.flag.junit.FlagsParameterization
import android.testing.TestableContext
@@ -91,7 +92,9 @@
@After
fun tearDown() {
- ViewUtils.detachView(qsPanel)
+ if (qsPanel.isAttachedToWindow) {
+ ViewUtils.detachView(qsPanel)
+ }
}
@Test
@@ -119,7 +122,7 @@
qsPanel.tileLayout?.addTile(
QSPanelControllerBase.TileRecord(
mock(QSTile::class.java),
- QSTileViewImpl(themedContext)
+ QSTileViewImpl(themedContext),
)
)
@@ -129,7 +132,7 @@
qsPanel.setUsingHorizontalLayout(/* horizontal */ true, mediaView, /* force */ true)
qsPanel.measure(
/* width */ View.MeasureSpec.makeMeasureSpec(3000, View.MeasureSpec.EXACTLY),
- /* height */ View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY)
+ /* height */ View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY),
)
qsPanel.layout(0, 0, qsPanel.measuredWidth, qsPanel.measuredHeight)
@@ -147,7 +150,7 @@
val padding = 10
themedContext.orCreateTestableResources.addOverride(
R.dimen.qs_panel_padding_bottom,
- padding
+ padding,
)
qsPanel.updatePadding()
assertThat(qsPanel.paddingBottom).isEqualTo(padding)
@@ -160,7 +163,7 @@
themedContext.orCreateTestableResources.addOverride(R.dimen.qs_panel_padding_top, padding)
themedContext.orCreateTestableResources.addOverride(
R.dimen.qs_panel_padding_top,
- paddingCombined
+ paddingCombined,
)
qsPanel.updatePadding()
@@ -267,6 +270,62 @@
assertThat(qsPanel.tileLayout!!.maxColumns).isEqualTo(2)
}
+ @Test
+ fun noPendingConfigChangesAtBeginning() {
+ assertThat(qsPanel.hadConfigurationChangeWhileDetached()).isFalse()
+ }
+
+ @Test
+ fun configChangesWhileDetached_pendingConfigChanges() {
+ ViewUtils.detachView(qsPanel)
+
+ qsPanel.onConfigurationChanged(Configuration())
+
+ assertThat(qsPanel.hadConfigurationChangeWhileDetached()).isTrue()
+ }
+
+ @Test
+ fun configChangesWhileDetached_reattach_pendingConfigChanges() {
+ ViewUtils.detachView(qsPanel)
+
+ qsPanel.onConfigurationChanged(Configuration())
+ testableLooper.runWithLooper { ViewUtils.attachView(qsPanel) }
+
+ assertThat(qsPanel.hadConfigurationChangeWhileDetached()).isTrue()
+ }
+
+ @Test
+ fun configChangesWhileDetached_reattach_detach_pendingConfigChanges_reset() {
+ ViewUtils.detachView(qsPanel)
+
+ qsPanel.onConfigurationChanged(Configuration())
+
+ testableLooper.runWithLooper { ViewUtils.attachView(qsPanel) }
+ ViewUtils.detachView(qsPanel)
+
+ assertThat(qsPanel.hadConfigurationChangeWhileDetached()).isFalse()
+ }
+
+ @Test
+ fun configChangeWhileAttached_noPendingConfigChanges() {
+ qsPanel.onConfigurationChanged(Configuration())
+
+ assertThat(qsPanel.hadConfigurationChangeWhileDetached()).isFalse()
+ }
+
+ @Test
+ fun configChangeWhileAttachedWithPending_doesntResetPending() {
+ ViewUtils.detachView(qsPanel)
+
+ qsPanel.onConfigurationChanged(Configuration())
+
+ testableLooper.runWithLooper { ViewUtils.attachView(qsPanel) }
+
+ qsPanel.onConfigurationChanged(Configuration())
+
+ assertThat(qsPanel.hadConfigurationChangeWhileDetached()).isTrue()
+ }
+
companion object {
@Parameters(name = "{0}") @JvmStatic fun getParams() = parameterizeSceneContainerFlag()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 2cf599a..e21a005 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -45,6 +45,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -63,6 +64,7 @@
import android.app.NotificationManager;
import android.os.Handler;
import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
@@ -70,13 +72,13 @@
import android.testing.TestableLooper;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.LogBufferEulogizer;
@@ -129,6 +131,7 @@
@Mock private GroupCoalescer mGroupCoalescer;
@Spy private RecordingCollectionListener mCollectionListener;
@Mock private CollectionReadyForBuildListener mBuildListener;
+ @Mock private NotificationDismissibilityProvider mDismissibilityProvider;
@Spy private RecordingLifetimeExtender mExtender1 = new RecordingLifetimeExtender("Extender1");
@Spy private RecordingLifetimeExtender mExtender2 = new RecordingLifetimeExtender("Extender2");
@@ -160,6 +163,7 @@
allowTestableLooperAsMainThread();
when(mEulogizer.record(any(Exception.class))).thenAnswer(i -> i.getArguments()[0]);
+ doReturn(Boolean.TRUE).when(mDismissibilityProvider).isDismissable(any());
mListenerInOrder = inOrder(mCollectionListener);
@@ -172,7 +176,7 @@
mBgExecutor,
mEulogizer,
mock(DumpManager.class),
- mock(NotificationDismissibilityProvider.class));
+ mDismissibilityProvider);
mCollection.attach(mGroupCoalescer);
mCollection.addCollectionListener(mCollectionListener);
mCollection.setBuildListener(mBuildListener);
@@ -1287,8 +1291,8 @@
// WHEN both notifications are manually dismissed together
mCollection.dismissNotifications(
- List.of(new Pair<>(entry1, defaultStats(entry1)),
- new Pair<>(entry2, defaultStats(entry2))));
+ List.of(entryWithDefaultStats(entry1),
+ entryWithDefaultStats(entry2)));
// THEN build list is only called one time
verifyBuiltList(List.of(entry1, entry2));
@@ -1306,8 +1310,8 @@
DismissedByUserStats stats1 = defaultStats(entry1);
DismissedByUserStats stats2 = defaultStats(entry2);
mCollection.dismissNotifications(
- List.of(new Pair<>(entry1, defaultStats(entry1)),
- new Pair<>(entry2, defaultStats(entry2))));
+ List.of(entryWithDefaultStats(entry1),
+ entryWithDefaultStats(entry2)));
// THEN we send the dismissals to system server
FakeExecutor.exhaustExecutors(mBgExecutor);
@@ -1338,8 +1342,8 @@
// WHEN both notifications are manually dismissed together
mCollection.dismissNotifications(
- List.of(new Pair<>(entry1, defaultStats(entry1)),
- new Pair<>(entry2, defaultStats(entry2))));
+ List.of(entryWithDefaultStats(entry1),
+ entryWithDefaultStats(entry2)));
// THEN the entries are marked as dismissed
assertEquals(DISMISSED, entry1.getDismissState());
@@ -1363,8 +1367,8 @@
// WHEN both notifications are manually dismissed together
mCollection.dismissNotifications(
- List.of(new Pair<>(entry1, defaultStats(entry1)),
- new Pair<>(entry2, defaultStats(entry2))));
+ List.of(entryWithDefaultStats(entry1),
+ entryWithDefaultStats(entry2)));
// THEN all interceptors get checked
verify(mInterceptor1).shouldInterceptDismissal(entry1);
@@ -1379,6 +1383,43 @@
}
@Test
+ @EnableFlags(Flags.FLAG_NOTIFICATIONS_DISMISS_PRUNED_SUMMARIES)
+ public void testDismissNotificationsIncludesPrunedParents() {
+ // GIVEN a collection with 2 groups; one has a single child, one has two.
+ mCollection.addNotificationDismissInterceptor(mInterceptor1);
+
+ NotifEvent notif1summary = mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE, 1, "notif1summary").setGroup(mContext, "group1")
+ .setGroupSummary(mContext, true));
+ NotifEvent notif1child = mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE, 1, "notif1child").setGroup(mContext, "group1"));
+ NotifEvent notif2summary = mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE2, 2, "notif2summary").setGroup(mContext, "group2")
+ .setGroupSummary(mContext, true));
+ NotifEvent notif2child1 = mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE2, 2, "notif2child1").setGroup(mContext, "group2"));
+ NotifEvent notif2child2 = mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE2, 2, "notif2child2").setGroup(mContext, "group2"));
+ NotificationEntry entry1summary = mCollectionListener.getEntry(notif1summary.key);
+ NotificationEntry entry1child = mCollectionListener.getEntry(notif1child.key);
+ NotificationEntry entry2summary = mCollectionListener.getEntry(notif2summary.key);
+ NotificationEntry entry2child1 = mCollectionListener.getEntry(notif2child1.key);
+ NotificationEntry entry2child2 = mCollectionListener.getEntry(notif2child2.key);
+
+ // WHEN one child from each group are manually dismissed together
+ mCollection.dismissNotifications(
+ List.of(entryWithDefaultStats(entry1child),
+ entryWithDefaultStats(entry2child1)));
+
+ // THEN the summary for the singleton child is dismissed, but not the other summary
+ verify(mInterceptor1).shouldInterceptDismissal(entry1summary);
+ verify(mInterceptor1).shouldInterceptDismissal(entry1child);
+ verify(mInterceptor1, never()).shouldInterceptDismissal(entry2summary);
+ verify(mInterceptor1).shouldInterceptDismissal(entry2child1);
+ verify(mInterceptor1, never()).shouldInterceptDismissal(entry2child2);
+ }
+
+ @Test
public void testDismissAllNotificationsCallsRebuildOnce() {
// GIVEN a collection with a couple notifications
NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
@@ -1764,6 +1805,10 @@
NotificationVisibility.obtain(entry.getKey(), 7, 2, true));
}
+ private static EntryWithDismissStats entryWithDefaultStats(NotificationEntry entry) {
+ return new EntryWithDismissStats(entry, defaultStats(entry));
+ }
+
private CollectionEvent postNotif(NotificationEntryBuilder builder) {
clearInvocations(mCollectionListener);
NotifEvent rawEvent = mNoMan.postNotif(builder);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 0878649..693ec79 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -131,6 +131,10 @@
override val shortcutAbsoluteTop: StateFlow<Float>
get() = _shortcutAbsoluteTop.asStateFlow()
+ private val _notificationStackAbsoluteBottom = MutableStateFlow(0F)
+ override val notificationStackAbsoluteBottom: StateFlow<Float>
+ get() = _notificationStackAbsoluteBottom.asStateFlow()
+
private val _isKeyguardEnabled = MutableStateFlow(true)
override val isKeyguardEnabled: StateFlow<Boolean> = _isKeyguardEnabled.asStateFlow()
@@ -294,6 +298,10 @@
_shortcutAbsoluteTop.value = top
}
+ override fun setNotificationStackAbsoluteBottom(bottom: Float) {
+ _notificationStackAbsoluteBottom.value = bottom
+ }
+
override fun setCanIgnoreAuthAndReturnToGone(canWake: Boolean) {
_canIgnoreAuthAndReturnToGone.value = canWake
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
index 70b4f79..4976cc2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.data.repository
import android.annotation.FloatRange
+import com.android.systemui.Flags.transitionRaceCondition
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
@@ -88,6 +89,13 @@
)
)
override var currentTransitionInfoInternal = _currentTransitionInfo.asStateFlow()
+ override var currentTransitionInfo =
+ TransitionInfo(
+ ownerName = "",
+ from = KeyguardState.OFF,
+ to = KeyguardState.LOCKSCREEN,
+ animator = null,
+ )
init {
// Seed with a FINISHED transition in OFF, same as the real repository.
@@ -261,8 +269,13 @@
validateStep: Boolean = true,
) {
if (step.transitionState == TransitionState.STARTED) {
- _currentTransitionInfo.value =
- TransitionInfo(from = step.from, to = step.to, animator = null, ownerName = "")
+ if (transitionRaceCondition()) {
+ currentTransitionInfo =
+ TransitionInfo(from = step.from, to = step.to, animator = null, ownerName = "")
+ } else {
+ _currentTransitionInfo.value =
+ TransitionInfo(from = step.from, to = step.to, animator = null, ownerName = "")
+ }
}
_transitions.replayCache.last().let { lastStep ->
@@ -308,7 +321,11 @@
}
override suspend fun startTransition(info: TransitionInfo): UUID? {
- _currentTransitionInfo.value = info
+ if (transitionRaceCondition()) {
+ currentTransitionInfo = info
+ } else {
+ _currentTransitionInfo.value = info
+ }
if (sendTransitionStepsOnStartTransition) {
sendTransitionSteps(from = info.from, to = info.to, testScope = testScope)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
index 700d7e9..ff0f13e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
@@ -17,8 +17,6 @@
package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.communal.domain.interactor.communalSceneInteractor
-import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.keyguardRepository
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -39,8 +37,6 @@
powerInteractor = powerInteractor,
communalSceneInteractor = communalSceneInteractor,
keyguardOcclusionInteractor = keyguardOcclusionInteractor,
- biometricSettingsRepository = biometricSettingsRepository,
- keyguardRepository = keyguardRepository,
- keyguardEnabledInteractor = keyguardEnabledInteractor,
+ keyguardLockWhileAwakeInteractor = keyguardLockWhileAwakeInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt
new file mode 100644
index 0000000..39236c7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.keyguardLockWhileAwakeInteractor by
+ Kosmos.Fixture {
+ KeyguardLockWhileAwakeInteractor(
+ biometricSettingsRepository = biometricSettingsRepository,
+ keyguardEnabledInteractor = keyguardEnabledInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
index b45120e..43eb93e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
@@ -43,30 +43,36 @@
private var state = VolumeDialogController.State()
override fun setActiveStream(stream: Int) {
- // ensure streamState existence for the active stream
- state.states.getOrElse(stream) {
- VolumeDialogController.StreamState().also { streamState ->
- state.states.put(stream, streamState)
- }
- }
- state.activeStream = stream
- }
-
- override fun setStreamVolume(stream: Int, userLevel: Int) {
- val streamState =
- state.states.getOrElse(stream) {
+ updateState {
+ // ensure streamState existence for the active stream`
+ states.getOrElse(stream) {
VolumeDialogController.StreamState().also { streamState ->
state.states.put(stream, streamState)
}
}
- streamState.level = userLevel.coerceIn(streamState.levelMin, streamState.levelMax)
+ activeStream = stream
+ }
+ }
+
+ override fun setStreamVolume(stream: Int, userLevel: Int) {
+ updateState {
+ val streamState =
+ states.getOrElse(stream) {
+ VolumeDialogController.StreamState().also { streamState ->
+ states.put(stream, streamState)
+ }
+ }
+ streamState.level = userLevel.coerceIn(streamState.levelMin, streamState.levelMax)
+ }
}
override fun setRingerMode(ringerModeNormal: Int, external: Boolean) {
- if (external) {
- state.ringerModeExternal = ringerModeNormal
- } else {
- state.ringerModeInternal = ringerModeNormal
+ updateState {
+ if (external) {
+ ringerModeExternal = ringerModeNormal
+ } else {
+ ringerModeInternal = ringerModeNormal
+ }
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt
index a1f157f1..10534a0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt
@@ -40,7 +40,7 @@
val Kosmos.shadeStartable by Fixture {
ShadeStartable(
applicationScope = applicationCoroutineScope,
- applicationContext = applicationContext,
+ context = applicationContext,
touchLog = mock<LogBuffer>(),
configurationRepository = configurationRepository,
shadeRepository = shadeRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt
index 83fc3e9..b1e9d89 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt
@@ -23,7 +23,6 @@
import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.largeScreenHeaderHelper
import com.android.systemui.statusbar.policy.splitShadeStateController
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -33,7 +32,6 @@
SharedNotificationContainerInteractor(
context = applicationContext,
splitShadeStateController = { splitShadeStateController },
- shadeInteractor = { shadeInteractor },
configurationInteractor = configurationInteractor,
keyguardInteractor = keyguardInteractor,
deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
index a25a3c0..7fbf4e4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+import android.content.applicationContext
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.dump.dumpManager
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
@@ -49,6 +51,7 @@
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.shade.largeScreenHeaderHelper
import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
@@ -61,6 +64,8 @@
interactor = sharedNotificationContainerInteractor,
dumpManager = dumpManager,
applicationScope = applicationCoroutineScope,
+ context = applicationContext,
+ configurationInteractor = configurationInteractor,
keyguardInteractor = keyguardInteractor,
keyguardTransitionInteractor = keyguardTransitionInteractor,
shadeInteractor = shadeInteractor,
@@ -94,6 +99,7 @@
aodBurnInViewModel = aodBurnInViewModel,
communalSceneInteractor = communalSceneInteractor,
headsUpNotificationInteractor = { headsUpNotificationInteractor },
+ largeScreenHeaderHelperLazy = { largeScreenHeaderHelper },
unfoldTransitionInteractor = unfoldTransitionInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorKosmos.kt
new file mode 100644
index 0000000..423100a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.sliders.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.plugins.volumeDialogController
+import com.android.systemui.volume.dialog.domain.interactor.volumeDialogStateInteractor
+import com.android.systemui.volume.dialog.sliders.domain.model.volumeDialogSliderType
+
+val Kosmos.volumeDialogSliderInteractor: VolumeDialogSliderInteractor by
+ Kosmos.Fixture {
+ VolumeDialogSliderInteractor(
+ volumeDialogSliderType,
+ volumeDialogStateInteractor,
+ volumeDialogController,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderTypeKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderTypeKosmos.kt
new file mode 100644
index 0000000..cc8c1ea
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderTypeKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.volume.dialog.sliders.domain.model
+
+import android.media.AudioManager
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.volumeDialogSliderType: VolumeDialogSliderType by
+ Kosmos.Fixture { VolumeDialogSliderType.Stream(AudioManager.STREAM_SYSTEM) }
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
index 478bead..e0f9ec9 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
@@ -25,6 +25,7 @@
import androidx.test.platform.app.InstrumentationRegistry;
+import org.junit.AssumptionViolatedException;
import org.junit.runner.Description;
import org.junit.runners.model.TestClass;
@@ -134,8 +135,17 @@
if (scope == Scope.Instance && order == Order.Outer) {
// End of a test method.
runner.mState.exitTestMethod();
- RavenwoodTestStats.getInstance().onTestFinished(classDescription, description,
- th == null ? Result.Passed : Result.Failed);
+
+ final Result result;
+ if (th == null) {
+ result = Result.Passed;
+ } else if (th instanceof AssumptionViolatedException) {
+ result = Result.Skipped;
+ } else {
+ result = Result.Failed;
+ }
+
+ RavenwoodTestStats.getInstance().onTestFinished(classDescription, description, result);
}
// If RUN_DISABLED_TESTS is set, and the method did _not_ throw, make it an error.
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
index b4b8715..016de8e 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
@@ -90,9 +90,7 @@
// Create the "latest" symlink.
Path symlink = Paths.get(tmpdir, basename + "latest.csv");
try {
- if (Files.exists(symlink)) {
- Files.delete(symlink);
- }
+ Files.deleteIfExists(symlink);
Files.createSymbolicLink(symlink, Paths.get(mOutputFile.getName()));
} catch (IOException e) {
diff --git a/ravenwood/scripts/ravenwood-test-summary b/ravenwood/scripts/ravenwood-test-summary
new file mode 100755
index 0000000..602fbff
--- /dev/null
+++ b/ravenwood/scripts/ravenwood-test-summary
@@ -0,0 +1,75 @@
+#!/usr/bin/env python3
+#
+# 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.
+
+'''
+Print the latest Ravenwood test execution summary
+
+Usage: /ravenwood-test-summary
+
+Example output:
+Module Passed Failed Skipped
+android.test.mock.ravenwood.tests 2 0 0
+CarLibHostUnitTest 565 0 7
+CarServiceHostUnitTest 364 0 0
+CtsAccountManagerTestCasesRavenwood 4 0 0
+CtsAppTestCasesRavenwood 21 0 0
+
+Description:
+This script finds all the test execution result from /tmp/Ravenwood-stats*,
+and shows per-module summary.
+'''
+
+import csv
+import glob
+import sys
+
+# Find the latest stats files.
+stats_files = glob.glob('/tmp/Ravenwood-stats_*_latest.csv')
+
+if len(stats_files) == 0:
+ print("No log files found.", file=sys.stderr)
+ exit(1)
+
+
+def parse_stats(file, result):
+ module = '(unknwon)'
+ passed = 0
+ failed = 0
+ skipped = 0
+ with open(file) as csvfile:
+ reader = csv.reader(csvfile, delimiter=',')
+
+
+ for i, row in enumerate(reader):
+ if i == 0: continue # Skip header line
+ module = row[0]
+ passed += int(row[3])
+ failed += int(row[4])
+ skipped += int(row[5])
+
+ result[module] = (passed, failed, skipped)
+
+
+result = {}
+
+for file in stats_files:
+ parse_stats(file, result)
+
+print('%-60s %8s %8s %8s' % ("Module", "Passed", "Failed", "Skipped"))
+
+for module in sorted(result.keys(), key=str.casefold):
+ r = result[module]
+ print('%-60s %8d %8d %8d' % (module, r[0], r[1], r[2]))
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 6a1e319..1082e62 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -670,6 +670,17 @@
}
private void readAllPermissions() {
+ readAllPermissionsFromXml();
+ readAllPermissionsFromEnvironment();
+
+ // Apply global feature removal last, after all features have been read.
+ // This only needs to happen once.
+ for (String featureName : mUnavailableFeatures) {
+ removeFeature(featureName);
+ }
+ }
+
+ private void readAllPermissionsFromXml() {
final XmlPullParser parser = Xml.newPullParser();
// Read configuration from system
@@ -1730,7 +1741,13 @@
} finally {
IoUtils.closeQuietly(permReader);
}
+ }
+ // Add features or permission dependent on global system properties (as
+ // opposed to XML permission files).
+ // This only needs to be called once after all features have been parsed
+ // from various partition/apex sources.
+ private void readAllPermissionsFromEnvironment() {
// Some devices can be field-converted to FBE, so offer to splice in
// those features if not already defined by the static config
if (StorageManager.isFileEncrypted()) {
@@ -1771,10 +1788,6 @@
addFeature(PackageManager.FEATURE_EROFS_LEGACY, 0);
}
}
-
- for (String featureName : mUnavailableFeatures) {
- removeFeature(featureName);
- }
}
private @Nullable SignedPackage parseEnhancedConfirmationTrustedPackage(XmlPullParser parser,
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 0ca3b56..679c7ac 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -112,6 +112,7 @@
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.modules.expresslog.Histogram;
@@ -1226,7 +1227,17 @@
// been re-enabled (after being updated for example), then we just overwrite the old
// values.
for (Entry<String, Integer> entry : knownAuth.entrySet()) {
- accountsDb.insertOrReplaceMetaAuthTypeAndUid(entry.getKey(), entry.getValue());
+ String type = entry.getKey();
+ Integer newUid = entry.getValue();
+ if (!Objects.equals(metaAuthUid.get(type), newUid)) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.ACCOUNT_MANAGER_EVENT,
+ type,
+ newUid,
+ FrameworkStatsLog
+ .ACCOUNT_MANAGER_EVENT__EVENT_TYPE__AUTHENTICATOR_ADDED);
+ }
+ accountsDb.insertOrReplaceMetaAuthTypeAndUid(type, newUid);
}
final Map<Long, Account> accountsMap = accountsDb.findAllDeAccounts();
@@ -1945,6 +1956,11 @@
}
accounts.accountsDb.setTransactionSuccessful();
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.ACCOUNT_MANAGER_EVENT,
+ account.type,
+ callingUid,
+ FrameworkStatsLog.ACCOUNT_MANAGER_EVENT__EVENT_TYPE__ACCOUNT_ADDED);
logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS,
accountId,
accounts, callingUid);
@@ -2544,6 +2560,11 @@
}
String action = userUnlocked ? AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE
: AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE_DE;
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.ACCOUNT_MANAGER_EVENT,
+ account.type,
+ callingUid,
+ FrameworkStatsLog.ACCOUNT_MANAGER_EVENT__EVENT_TYPE__ACCOUNT_REMOVED);
logRecord(action, AccountsDb.TABLE_ACCOUNTS, accountId, accounts);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3e03045..87ce649 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2772,8 +2772,12 @@
// Add common services.
// IMPORTANT: Before adding services here, make sure ephemeral apps can access them too.
// Enable the check in ApplicationThread.bindApplication() to make sure.
- if (!android.server.Flags.removeJavaServiceManagerCache()) {
- addServiceToMap(mAppBindArgs, "permissionmgr");
+
+ // Removing User Service and App Ops Service from cache breaks boot for auto.
+ // Removing permissionmgr breaks tests for Android Auto due to SELinux restrictions.
+ // TODO: fix SELinux restrictions and remove caching for Android Auto.
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ || !android.server.Flags.removeJavaServiceManagerCache()) {
addServiceToMap(mAppBindArgs, Context.ALARM_SERVICE);
addServiceToMap(mAppBindArgs, Context.DISPLAY_SERVICE);
addServiceToMap(mAppBindArgs, Context.NETWORKMANAGEMENT_SERVICE);
@@ -2790,16 +2794,16 @@
addServiceToMap(mAppBindArgs, Context.POWER_SERVICE);
addServiceToMap(mAppBindArgs, "mount");
addServiceToMap(mAppBindArgs, Context.PLATFORM_COMPAT_SERVICE);
+ addServiceToMap(mAppBindArgs, "permissionmgr");
+ addServiceToMap(mAppBindArgs, Context.APP_OPS_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.USER_SERVICE);
}
// See b/79378449
// Getting the window service and package service binder from servicemanager
// is blocked for Apps. However they are necessary for apps.
- // Removing User Service and App Ops Service from cache breaks boot for auto.
// TODO: remove exception
- addServiceToMap(mAppBindArgs, Context.APP_OPS_SERVICE);
addServiceToMap(mAppBindArgs, "package");
addServiceToMap(mAppBindArgs, Context.WINDOW_SERVICE);
- addServiceToMap(mAppBindArgs, Context.USER_SERVICE);
}
return mAppBindArgs;
}
@@ -5551,6 +5555,8 @@
if (target instanceof PendingIntentRecord) {
final PendingIntentRecord originalRecord = (PendingIntentRecord) target;
+ addCreatorToken(intent, originalRecord.getPackageName());
+
// In multi-display scenarios, there can be background users who execute the
// PendingIntent. In these scenarios, we don't want to use the foreground user as the
// current user.
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 8d16eb5..42966a3 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -78,7 +78,6 @@
import static android.os.Process.THREAD_GROUP_TOP_APP;
import static android.os.Process.THREAD_PRIORITY_DISPLAY;
import static android.os.Process.THREAD_PRIORITY_TOP_APP_BOOST;
-import static android.os.Process.setProcessGroup;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
@@ -526,7 +525,7 @@
+ msg.obj + " to " + group);
}
try {
- setProcessGroup(pid, group);
+ android.os.Process.setProcessGroup(pid, group);
} catch (Exception e) {
if (DEBUG_ALL) {
Slog.w(TAG, "Failed setting process group of " + pid + " to " + group, e);
@@ -544,6 +543,11 @@
/ CACHED_APP_IMPORTANCE_LEVELS;
}
+ void setProcessGroup(int pid, int group, String processName) {
+ mProcessGroupHandler.sendMessage(mProcessGroupHandler.obtainMessage(
+ 0 /* unused */, pid, group, processName));
+ }
+
void initSettings() {
mCachedAppOptimizer.init();
mCacheOomRanker.init(ActivityThread.currentApplication().getMainExecutor());
@@ -3491,8 +3495,8 @@
processGroup = THREAD_GROUP_DEFAULT;
break;
}
- mProcessGroupHandler.sendMessage(mProcessGroupHandler.obtainMessage(
- 0 /* unused */, app.getPid(), processGroup, app.processName));
+ setProcessGroup(app.getPid(), processGroup, app.processName);
+ mService.mPhantomProcessList.setProcessGroupForPhantomProcessOfApp(app, processGroup);
try {
final int renderThreadTid = app.getRenderThreadTid();
if (curSchedGroup == SCHED_GROUP_TOP_APP) {
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 3fb06a7..4331e05 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -629,7 +629,8 @@
allIntents, allResolvedTypes, resultTo, mergedOptions, userId,
false /* validateIncomingUser */,
this /* originatingPendingIntent */,
- getBackgroundStartPrivilegesForActivitySender(allowlistToken));
+ getBackgroundStartPrivilegesForActivitySender(allowlistToken)
+ .allowsBackgroundActivityStarts());
} else {
res = controller.mAtmInternal.startActivityInPackage(uid, callingPid,
callingUid, key.packageName, key.featureId, finalIntent,
@@ -637,7 +638,8 @@
mergedOptions, userId, null, "PendingIntentRecord",
false /* validateIncomingUser */,
this /* originatingPendingIntent */,
- getBackgroundStartPrivilegesForActivitySender(allowlistToken));
+ getBackgroundStartPrivilegesForActivitySender(allowlistToken)
+ .allowsBackgroundActivityStarts());
}
} catch (RuntimeException e) {
Slog.w(TAG, "Unable to send startActivity intent", e);
diff --git a/services/core/java/com/android/server/am/PhantomProcessList.java b/services/core/java/com/android/server/am/PhantomProcessList.java
index 2ec1aed..bfdced7 100644
--- a/services/core/java/com/android/server/am/PhantomProcessList.java
+++ b/services/core/java/com/android/server/am/PhantomProcessList.java
@@ -514,6 +514,15 @@
app.killLocked("Caused by child process: " + msg, reasonCode, subReason, true);
}
+ @GuardedBy("mLock")
+ private SparseArray<PhantomProcessRecord> getPhantomProcessOfAppLocked(ProcessRecord app) {
+ int index = mAppPhantomProcessMap.indexOfKey(app.getPid());
+ if (index >= 0) {
+ return mAppPhantomProcessMap.valueAt(index);
+ }
+ return null;
+ }
+
/**
* Iterate all phantom process belonging to the given app, and invokve callback
* for each of them.
@@ -521,20 +530,35 @@
void forEachPhantomProcessOfApp(final ProcessRecord app,
final Function<PhantomProcessRecord, Boolean> callback) {
synchronized (mLock) {
- int index = mAppPhantomProcessMap.indexOfKey(app.getPid());
- if (index >= 0) {
- final SparseArray<PhantomProcessRecord> array =
- mAppPhantomProcessMap.valueAt(index);
- for (int i = array.size() - 1; i >= 0; i--) {
- final PhantomProcessRecord r = array.valueAt(i);
- if (!callback.apply(r)) {
- break;
- }
+ final SparseArray<PhantomProcessRecord> array = getPhantomProcessOfAppLocked(app);
+ if (array == null) {
+ return;
+ }
+ for (int i = array.size() - 1; i >= 0; i--) {
+ final PhantomProcessRecord r = array.valueAt(i);
+ if (!callback.apply(r)) {
+ break;
}
}
}
}
+ /**
+ * Set process group of phantom process belonging to the given app.
+ */
+ void setProcessGroupForPhantomProcessOfApp(final ProcessRecord app, final int group) {
+ synchronized (mLock) {
+ final SparseArray<PhantomProcessRecord> array = getPhantomProcessOfAppLocked(app);
+ if (array == null) {
+ return;
+ }
+ for (int i = array.size() - 1; i >= 0; i--) {
+ final PhantomProcessRecord r = array.valueAt(i);
+ mService.mOomAdjuster.setProcessGroup(r.mPid, group, r.mProcessName);
+ }
+ }
+ }
+
@GuardedBy("tracker")
void updateProcessCpuStatesLocked(ProcessCpuTracker tracker) {
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index bfef685..353cfbe 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1608,7 +1608,14 @@
if (dev == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
enabled = mAvrcpAbsVolSupported;
}
- mAudioSystem.setDeviceAbsoluteVolumeEnabled(dev, /*address=*/"", enabled, stream);
+ final int result = mAudioSystem.setDeviceAbsoluteVolumeEnabled(dev, /*address=*/"",
+ enabled, stream);
+ if (result != AudioSystem.AUDIO_STATUS_OK) {
+ sVolumeLogger.enqueueAndSlog(
+ new VolumeEvent(VolumeEvent.VOL_ABS_DEVICE_ENABLED_ERROR,
+ result, dev, enabled, stream).eventToString(), ALOGE, TAG);
+
+ }
});
}
}
@@ -2069,7 +2076,13 @@
if (dev == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
enabled = mAvrcpAbsVolSupported;
}
- mAudioSystem.setDeviceAbsoluteVolumeEnabled(dev, /*address=*/"", enabled, stream);
+ final int result = mAudioSystem.setDeviceAbsoluteVolumeEnabled(dev, /*address=*/"",
+ enabled, stream);
+ if (result != AudioSystem.AUDIO_STATUS_OK) {
+ sVolumeLogger.enqueueAndSlog(
+ new VolumeEvent(VolumeEvent.VOL_ABS_DEVICE_ENABLED_ERROR,
+ result, dev, enabled, stream).eventToString(), ALOGE, TAG);
+ }
});
}
@@ -4917,15 +4930,27 @@
private void onUpdateContextualVolumes() {
final int streamType = getBluetoothContextualVolumeStream();
+ Log.i(TAG,
+ "onUpdateContextualVolumes: absolute volume driving streams " + streamType
+ + " avrcp supported: " + mAvrcpAbsVolSupported);
synchronized (mCachedAbsVolDrivingStreamsLock) {
mCachedAbsVolDrivingStreams.replaceAll((absDev, stream) -> {
boolean enabled = true;
if (absDev == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
enabled = mAvrcpAbsVolSupported;
+ if (!enabled) {
+ Log.w(TAG, "Updating avrcp not supported in onUpdateContextualVolumes");
+ }
}
- if (stream != streamType || !enabled) {
- mAudioSystem.setDeviceAbsoluteVolumeEnabled(absDev, /*address=*/"",
- enabled, streamType);
+ if (stream != streamType) {
+ final int result = mAudioSystem.setDeviceAbsoluteVolumeEnabled(absDev,
+ /*address=*/"", enabled, streamType);
+ if (result != AudioSystem.AUDIO_STATUS_OK) {
+ sVolumeLogger.enqueueAndSlog(
+ new VolumeEvent(VolumeEvent.VOL_ABS_DEVICE_ENABLED_ERROR,
+ result, absDev, enabled, streamType).eventToString(), ALOGE,
+ TAG);
+ }
}
return streamType;
});
@@ -10563,14 +10588,23 @@
}
/*package*/ void setAvrcpAbsoluteVolumeSupported(boolean support) {
+ Log.i(TAG, "setAvrcpAbsoluteVolumeSupported support " + support);
synchronized (mCachedAbsVolDrivingStreamsLock) {
mAvrcpAbsVolSupported = support;
if (absVolumeIndexFix()) {
int a2dpDev = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
mCachedAbsVolDrivingStreams.compute(a2dpDev, (dev, stream) -> {
if (!mAvrcpAbsVolSupported) {
- mAudioSystem.setDeviceAbsoluteVolumeEnabled(a2dpDev, /*address=*/
- "", /*enabled*/false, AudioSystem.STREAM_DEFAULT);
+ final int result = mAudioSystem.setDeviceAbsoluteVolumeEnabled(
+ a2dpDev, /*address=*/"", /*enabled*/false,
+ AudioSystem.STREAM_DEFAULT);
+ if (result != AudioSystem.AUDIO_STATUS_OK) {
+ sVolumeLogger.enqueueAndSlog(
+ new VolumeEvent(VolumeEvent.VOL_ABS_DEVICE_ENABLED_ERROR,
+ result, a2dpDev, /*enabled=*/false,
+ AudioSystem.STREAM_DEFAULT).eventToString(), ALOGE,
+ TAG);
+ }
return null;
}
// For A2DP and AVRCP we need to set the driving stream based on the
@@ -10578,8 +10612,14 @@
// and setStreamVolume that the driving abs volume stream is consistent.
int streamToDriveAbs = getBluetoothContextualVolumeStream();
if (stream == null || stream != streamToDriveAbs) {
- mAudioSystem.setDeviceAbsoluteVolumeEnabled(a2dpDev, /*address=*/
- "", /*enabled*/true, streamToDriveAbs);
+ final int result = mAudioSystem.setDeviceAbsoluteVolumeEnabled(a2dpDev,
+ /*address=*/"", /*enabled*/true, streamToDriveAbs);
+ if (result != AudioSystem.AUDIO_STATUS_OK) {
+ sVolumeLogger.enqueueAndSlog(
+ new VolumeEvent(VolumeEvent.VOL_ABS_DEVICE_ENABLED_ERROR,
+ result, a2dpDev, /*enabled=*/true,
+ streamToDriveAbs).eventToString(), ALOGE, TAG);
+ }
}
return streamToDriveAbs;
});
@@ -12679,7 +12719,7 @@
pw.println("\nLoudness alignment:");
mLoudnessCodecHelper.dump(pw);
- pw.println("\nAbsolute voume devices:");
+ pw.println("\nAbsolute volume devices with their volume driving streams:");
synchronized (mCachedAbsVolDrivingStreamsLock) {
mCachedAbsVolDrivingStreams.forEach((dev, stream) -> pw.println(
"Device type: 0x" + Integer.toHexString(dev) + ", driving stream " + stream));
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index 631d5d8..995e16b 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -234,6 +234,7 @@
static final int VOL_SET_LE_AUDIO_VOL = 10;
static final int VOL_ADJUST_GROUP_VOL = 11;
static final int VOL_MASTER_MUTE = 12;
+ static final int VOL_ABS_DEVICE_ENABLED_ERROR = 13;
final int mOp;
final int mStream;
@@ -365,6 +366,19 @@
logMetricEvent();
}
+ /** used for VOL_ABS_DEVICE_ENABLED_ERROR */
+ VolumeEvent(int op, int result, int device, boolean enabled, int streamType) {
+ mOp = op;
+ mStream = streamType;
+ mVal1 = device;
+ mVal2 = enabled ? 1 : 0;
+ mVal3 = result;
+ // unused
+ mCaller = null;
+ mGroupName = null;
+ logMetricEvent();
+ }
+
/**
* Audio Analytics unique Id.
@@ -481,6 +495,9 @@
case VOL_MASTER_MUTE:
// No value in logging metrics for this internal event
return;
+ case VOL_ABS_DEVICE_ENABLED_ERROR:
+ // No value in logging metrics for this internal event
+ return;
default:
return;
}
@@ -569,6 +586,13 @@
return new StringBuilder("Master mute:")
.append(mVal1 == 1 ? " muted)" : " unmuted)")
.toString();
+ case VOL_ABS_DEVICE_ENABLED_ERROR:
+ return new StringBuilder("setDeviceAbsoluteVolumeEnabled failed with ")
+ .append(mVal3)
+ .append(" for dev: 0x").append(Integer.toHexString(mVal1))
+ .append(" enabled: ").append(mVal2)
+ .append(" streamType: ").append(mStream)
+ .toString();
default: return new StringBuilder("FIXME invalid op:").append(mOp).toString();
}
}
diff --git a/services/core/java/com/android/server/display/DisplayTopology.java b/services/core/java/com/android/server/display/DisplayTopology.java
index b01d617..fdadafe 100644
--- a/services/core/java/com/android/server/display/DisplayTopology.java
+++ b/services/core/java/com/android/server/display/DisplayTopology.java
@@ -16,7 +16,13 @@
package com.android.server.display;
+import static com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_BOTTOM;
+import static com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_LEFT;
+import static com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_TOP;
+import static com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_RIGHT;
+
import android.annotation.Nullable;
+import android.graphics.RectF;
import android.util.IndentingPrintWriter;
import android.util.Pair;
import android.util.Slog;
@@ -25,16 +31,21 @@
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
+import java.util.ArrayDeque;
import java.util.ArrayList;
-import java.util.LinkedList;
+import java.util.Comparator;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Queue;
/**
* Represents the relative placement of extended displays.
+ * Does not support concurrent calls, so a lock should be held when calling into this class.
*/
class DisplayTopology {
private static final String TAG = "DisplayTopology";
+ private static final float EPSILON = 0.0001f;
/**
* The topology tree
@@ -58,7 +69,7 @@
* @param width The width of the display
* @param height The height of the display
*/
- void addDisplay(int displayId, double width, double height) {
+ void addDisplay(int displayId, float width, float height) {
addDisplay(displayId, width, height, /* shouldLog= */ true);
}
@@ -69,10 +80,10 @@
* @param displayId The logical display ID
*/
void removeDisplay(int displayId) {
- if (!isDisplayPresent(displayId, mRoot)) {
+ if (findDisplay(displayId, mRoot) == null) {
return;
}
- Queue<TreeNode> queue = new LinkedList<>();
+ Queue<TreeNode> queue = new ArrayDeque<>();
queue.add(mRoot);
mRoot = null;
while (!queue.isEmpty()) {
@@ -115,7 +126,11 @@
}
}
- private void addDisplay(int displayId, double width, double height, boolean shouldLog) {
+ private void addDisplay(int displayId, float width, float height, boolean shouldLog) {
+ if (findDisplay(displayId, mRoot) != null) {
+ throw new IllegalArgumentException(
+ "DisplayTopology: attempting to add a display that already exists");
+ }
if (mRoot == null) {
mRoot = new TreeNode(displayId, width, height, /* position= */ null, /* offset= */ 0);
mPrimaryDisplayId = displayId;
@@ -124,9 +139,8 @@
}
} else if (mRoot.mChildren.isEmpty()) {
// This is the 2nd display. Align the middles of the top and bottom edges.
- double offset = mRoot.mWidth / 2 - width / 2;
- TreeNode display = new TreeNode(displayId, width, height,
- TreeNode.Position.POSITION_TOP, offset);
+ float offset = mRoot.mWidth / 2 - width / 2;
+ TreeNode display = new TreeNode(displayId, width, height, POSITION_TOP, offset);
mRoot.mChildren.add(display);
if (shouldLog) {
Slog.i(TAG, "Second display added: " + display + ", parent ID: "
@@ -134,8 +148,8 @@
}
} else {
TreeNode rightMostDisplay = findRightMostDisplay(mRoot, mRoot.mWidth).first;
- TreeNode newDisplay = new TreeNode(displayId, width, height,
- TreeNode.Position.POSITION_RIGHT, /* offset= */ 0);
+ TreeNode newDisplay = new TreeNode(displayId, width, height, POSITION_RIGHT,
+ /* offset= */ 0);
rightMostDisplay.mChildren.add(newDisplay);
if (shouldLog) {
Slog.i(TAG, "Display added: " + newDisplay + ", parent ID: "
@@ -150,11 +164,11 @@
* @return The display that is the furthest to the right and the x position of the right edge
* of that display
*/
- private Pair<TreeNode, Double> findRightMostDisplay(TreeNode display, double xPos) {
- Pair<TreeNode, Double> result = new Pair<>(display, xPos);
+ private static Pair<TreeNode, Float> findRightMostDisplay(TreeNode display, float xPos) {
+ Pair<TreeNode, Float> result = new Pair<>(display, xPos);
for (TreeNode child : display.mChildren) {
// The x position of the right edge of the child
- double childXPos;
+ float childXPos;
switch (child.mPosition) {
case POSITION_LEFT -> childXPos = xPos - display.mWidth;
case POSITION_TOP, POSITION_BOTTOM ->
@@ -164,7 +178,7 @@
}
// Recursive call - find the rightmost display starting from the child
- Pair<TreeNode, Double> childResult = findRightMostDisplay(child, childXPos);
+ Pair<TreeNode, Float> childResult = findRightMostDisplay(child, childXPos);
// Check if the one found is further right
if (childResult.second > result.second) {
result = new Pair<>(childResult.first, childResult.second);
@@ -173,19 +187,200 @@
return result;
}
- private boolean isDisplayPresent(int displayId, TreeNode node) {
- if (node == null) {
- return false;
+ @Nullable
+ private static TreeNode findDisplay(int displayId, TreeNode startingNode) {
+ if (startingNode == null) {
+ return null;
}
- if (node.mDisplayId == displayId) {
- return true;
+ if (startingNode.mDisplayId == displayId) {
+ return startingNode;
}
- for (TreeNode child : node.mChildren) {
- if (isDisplayPresent(displayId, child)) {
- return true;
+ for (TreeNode child : startingNode.mChildren) {
+ TreeNode display = findDisplay(displayId, child);
+ if (display != null) {
+ return display;
}
}
- return false;
+ return null;
+ }
+
+ /**
+ * Get information about the topology that will be used for the normalization algorithm.
+ * Assigns origins to each display to compute the bounds.
+ * @param bounds The map where the bounds of each display will be put
+ * @param depths The map where the depths of each display in the tree will be put
+ * @param parents The map where the parent of each display will be put
+ * @param display The starting node
+ * @param x The starting x position
+ * @param y The starting y position
+ * @param depth The starting depth
+ */
+ private static void getInfo(Map<TreeNode, RectF> bounds, Map<TreeNode, Integer> depths,
+ Map<TreeNode, TreeNode> parents, TreeNode display, float x, float y, int depth) {
+ bounds.put(display, new RectF(x, y, x + display.mWidth, y + display.mHeight));
+ depths.put(display, depth);
+ for (TreeNode child : display.mChildren) {
+ parents.put(child, display);
+ if (child.mPosition == POSITION_LEFT) {
+ getInfo(bounds, depths, parents, child, x - child.mWidth, y + child.mOffset,
+ depth + 1);
+ } else if (child.mPosition == POSITION_RIGHT) {
+ getInfo(bounds, depths, parents, child, x + display.mWidth, y + child.mOffset,
+ depth + 1);
+ } else if (child.mPosition == POSITION_TOP) {
+ getInfo(bounds, depths, parents, child, x + child.mOffset, y - child.mHeight,
+ depth + 1);
+ } else if (child.mPosition == POSITION_BOTTOM) {
+ getInfo(bounds, depths, parents, child, x + child.mOffset, y + display.mHeight,
+ depth + 1);
+ }
+ }
+ }
+
+ /**
+ * Update the topology to remove any overlaps between displays.
+ */
+ @VisibleForTesting
+ void normalize() {
+ if (mRoot == null) {
+ return;
+ }
+ Map<TreeNode, RectF> bounds = new HashMap<>();
+ Map<TreeNode, Integer> depths = new HashMap<>();
+ Map<TreeNode, TreeNode> parents = new HashMap<>();
+ getInfo(bounds, depths, parents, mRoot, /* x= */ 0, /* y= */ 0, /* depth= */ 0);
+
+ // Sort the displays first by their depth in the tree, then by the distance of their top
+ // left point from the root display's origin (0, 0). This way we process the displays
+ // starting at the root and we push out a display if necessary.
+ Comparator<TreeNode> comparator = (d1, d2) -> {
+ if (d1 == d2) {
+ return 0;
+ }
+
+ int compareDepths = Integer.compare(depths.get(d1), depths.get(d2));
+ if (compareDepths != 0) {
+ return compareDepths;
+ }
+
+ RectF bounds1 = bounds.get(d1);
+ RectF bounds2 = bounds.get(d2);
+ return Double.compare(Math.hypot(bounds1.left, bounds1.top),
+ Math.hypot(bounds2.left, bounds2.top));
+ };
+ List<TreeNode> displays = new ArrayList<>(bounds.keySet());
+ displays.sort(comparator);
+
+ for (int i = 1; i < displays.size(); i++) {
+ TreeNode targetDisplay = displays.get(i);
+ TreeNode lastIntersectingSourceDisplay = null;
+ float lastOffsetX = 0;
+ float lastOffsetY = 0;
+
+ for (int j = 0; j < i; j++) {
+ TreeNode sourceDisplay = displays.get(j);
+ RectF sourceBounds = bounds.get(sourceDisplay);
+ RectF targetBounds = bounds.get(targetDisplay);
+
+ if (!RectF.intersects(sourceBounds, targetBounds)) {
+ continue;
+ }
+
+ // Find the offset by which to move the display. Pick the smaller one among the x
+ // and y axes.
+ float offsetX = targetBounds.left >= 0
+ ? sourceBounds.right - targetBounds.left
+ : sourceBounds.left - targetBounds.right;
+ float offsetY = targetBounds.top >= 0
+ ? sourceBounds.bottom - targetBounds.top
+ : sourceBounds.top - targetBounds.bottom;
+ if (Math.abs(offsetX) <= Math.abs(offsetY)) {
+ targetBounds.left += offsetX;
+ targetBounds.right += offsetX;
+ // We need to also update the offset in the tree
+ if (targetDisplay.mPosition == POSITION_TOP
+ || targetDisplay.mPosition == POSITION_BOTTOM) {
+ targetDisplay.mOffset += offsetX;
+ }
+ offsetY = 0;
+ } else {
+ targetBounds.top += offsetY;
+ targetBounds.bottom += offsetY;
+ // We need to also update the offset in the tree
+ if (targetDisplay.mPosition == POSITION_LEFT
+ || targetDisplay.mPosition == POSITION_RIGHT) {
+ targetDisplay.mOffset += offsetY;
+ }
+ offsetX = 0;
+ }
+
+ lastIntersectingSourceDisplay = sourceDisplay;
+ lastOffsetX = offsetX;
+ lastOffsetY = offsetY;
+ }
+
+ // Now re-parent the target display to the last intersecting source display if it no
+ // longer touches its parent.
+ if (lastIntersectingSourceDisplay == null) {
+ // There was no overlap.
+ continue;
+ }
+ TreeNode parent = parents.get(targetDisplay);
+ if (parent == lastIntersectingSourceDisplay) {
+ // The displays are moved in such a way that they're adjacent to the intersecting
+ // display. If the last intersecting display happens to be the parent then we
+ // already know that the display is adjacent to its parent.
+ continue;
+ }
+
+ RectF childBounds = bounds.get(targetDisplay);
+ RectF parentBounds = bounds.get(parent);
+ // Check that the edges are on the same line
+ boolean areTouching = switch (targetDisplay.mPosition) {
+ case POSITION_LEFT -> floatEquals(parentBounds.left, childBounds.right);
+ case POSITION_RIGHT -> floatEquals(parentBounds.right, childBounds.left);
+ case POSITION_TOP -> floatEquals(parentBounds.top, childBounds.bottom);
+ case POSITION_BOTTOM -> floatEquals(parentBounds.bottom, childBounds.top);
+ };
+ // Check that the offset is within bounds
+ areTouching &= switch (targetDisplay.mPosition) {
+ case POSITION_LEFT, POSITION_RIGHT ->
+ childBounds.bottom + EPSILON >= parentBounds.top
+ && childBounds.top <= parentBounds.bottom + EPSILON;
+ case POSITION_TOP, POSITION_BOTTOM ->
+ childBounds.right + EPSILON >= parentBounds.left
+ && childBounds.left <= parentBounds.right + EPSILON;
+ };
+
+ if (!areTouching) {
+ // Re-parent the display.
+ parent.mChildren.remove(targetDisplay);
+ RectF lastIntersectingSourceDisplayBounds =
+ bounds.get(lastIntersectingSourceDisplay);
+ lastIntersectingSourceDisplay.mChildren.add(targetDisplay);
+
+ if (lastOffsetX != 0) {
+ targetDisplay.mPosition = lastOffsetX > 0 ? POSITION_RIGHT : POSITION_LEFT;
+ targetDisplay.mOffset =
+ childBounds.top - lastIntersectingSourceDisplayBounds.top;
+ } else if (lastOffsetY != 0) {
+ targetDisplay.mPosition = lastOffsetY > 0 ? POSITION_BOTTOM : POSITION_TOP;
+ targetDisplay.mOffset =
+ childBounds.left - lastIntersectingSourceDisplayBounds.left;
+ }
+ }
+ }
+ }
+
+ /**
+ * Tests whether two brightness float values are within a small enough tolerance
+ * of each other.
+ * @param a first float to compare
+ * @param b second float to compare
+ * @return whether the two values are within a small enough tolerance value
+ */
+ public static boolean floatEquals(float a, float b) {
+ return a == b || Float.isNaN(a) && Float.isNaN(b) || Math.abs(a - b) < EPSILON;
}
@VisibleForTesting
@@ -201,13 +396,13 @@
* The width of the display in density-independent pixels (dp).
*/
@VisibleForTesting
- double mWidth;
+ float mWidth;
/**
* The height of the display in density-independent pixels (dp).
*/
@VisibleForTesting
- double mHeight;
+ float mHeight;
/**
* The position of this display relative to its parent.
@@ -222,13 +417,12 @@
* used is density-independent pixels (dp).
*/
@VisibleForTesting
- double mOffset;
+ float mOffset;
@VisibleForTesting
final List<TreeNode> mChildren = new ArrayList<>();
- TreeNode(int displayId, double width, double height, Position position,
- double offset) {
+ TreeNode(int displayId, float width, float height, Position position, float offset) {
mDisplayId = displayId;
mWidth = width;
mHeight = height;
diff --git a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
index 46358dfd..b101e58 100644
--- a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
+++ b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
@@ -89,8 +89,8 @@
* @param info The display info
* @return The width of the display in dp
*/
- private double getWidth(DisplayInfo info) {
- return info.logicalWidth * (double) DisplayMetrics.DENSITY_DEFAULT
+ private float getWidth(DisplayInfo info) {
+ return info.logicalWidth * (float) DisplayMetrics.DENSITY_DEFAULT
/ info.logicalDensityDpi;
}
@@ -98,8 +98,8 @@
* @param info The display info
* @return The height of the display in dp
*/
- private double getHeight(DisplayInfo info) {
- return info.logicalHeight * (double) DisplayMetrics.DENSITY_DEFAULT
+ private float getHeight(DisplayInfo info) {
+ return info.logicalHeight * (float) DisplayMetrics.DENSITY_DEFAULT
/ info.logicalDensityDpi;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index d0ad6fc..b696c54 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -444,14 +444,62 @@
new Runnable() {
@Override
public void run() {
- if (!isActiveSource()) {
+ if (isActiveSource()) {
+ return;
+ }
+
+ if (getActiveSource().logicalAddress != Constants.ADDR_TV) {
startHdmiCecActiveSourceLostActivity();
mDelayedStandbyOnActiveSourceLostHandler
.removeCallbacksAndMessages(null);
mDelayedStandbyOnActiveSourceLostHandler.postDelayed(
new DelayedStandbyOnActiveSourceLostRunnable(),
STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ return;
}
+
+ // We observed specific TV panels (old models) that send faulty CEC
+ // source changing messages, especially during wake-up.
+ // This request helps to check if the TV correctly asserted active
+ // source or not. If the request times out, active source is
+ // asserted by the local device.
+ addAndStartAction(new RequestActiveSourceAction(mService.playback(),
+ new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ // If a device answers to <Request Active Source>, the
+ // pop-up should be triggered.
+ // During this action, the TV can switch to an HDMI input
+ // with a non-CEC capable device that won't be able to
+ // answer this request.
+ // In this case, the known active source would be
+ // represented by a valid physical address, but invalid
+ // logical address. The pop-up will be shown and the local
+ // device will not assert active source.
+ if (result == HdmiControlManager.RESULT_SUCCESS
+ || getActiveSource().logicalAddress
+ != Constants.ADDR_TV) {
+ startHdmiCecActiveSourceLostActivity();
+ mDelayedStandbyOnActiveSourceLostHandler
+ .removeCallbacksAndMessages(null);
+ mDelayedStandbyOnActiveSourceLostHandler
+ .postDelayed(
+ new DelayedStandbyOnActiveSourceLostRunnable(),
+ STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ } else {
+ // The request times out and the local device is not
+ // active source, but the TV previously asserted active
+ // source.
+ if (getActiveSource().logicalAddress
+ == Constants.ADDR_TV) {
+ mService.setAndBroadcastActiveSource(
+ mService.getPhysicalAddress(),
+ getDeviceInfo().getDeviceType(),
+ Constants.ADDR_BROADCAST,
+ "RequestActiveSourceAction#RESULT_TIMEOUT");
+ }
+ }
+ }}));
}
}, POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
return;
@@ -698,6 +746,7 @@
removeAction(HotplugDetectionAction.class);
removeAction(NewDeviceAction.class);
removeAction(PowerStatusMonitorActionFromPlayback.class);
+ removeAction(RequestActiveSourceAction.class);
super.disableDevice(initiatedByCec, callback);
clearDeviceInfoList();
checkIfPendingActionsCleared();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index aae7b59..5682c33 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -471,6 +471,10 @@
void startRoutingControl(int oldPath, int newPath, IHdmiControlCallback callback) {
assertRunOnServiceThread();
if (oldPath == newPath) {
+ HdmiCecMessage setStreamPath =
+ HdmiCecMessageBuilder.buildSetStreamPath(getDeviceInfo().getLogicalAddress(),
+ oldPath);
+ mService.sendCecCommand(setStreamPath);
return;
}
HdmiCecMessage routingChange =
@@ -642,7 +646,8 @@
int address = message.getSource();
int type = message.getParams()[2];
- if (!mService.getHdmiCecNetwork().isInDeviceList(address, path)) {
+ if (!ActiveSource.of(address, path).equals(getActiveSource())) {
+ HdmiLogger.debug("Check if a new device is connected to the active path");
handleNewDeviceAtTheTailOfActivePath(path);
}
startNewDeviceAction(ActiveSource.of(address, path), type);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 81be0ba..0766c3a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1988,7 +1988,6 @@
void setAudioStatus(boolean mute, int volume) {
if (!isTvDeviceEnabled()
|| !tv().isSystemAudioActivated()
- || !tv().isArcEstablished() // Don't update TV volume when SAM is on and ARC is off
|| getHdmiCecVolumeControl()
== HdmiControlManager.VOLUME_CONTROL_DISABLED) {
return;
@@ -4344,6 +4343,7 @@
if (deviceType == HdmiDeviceInfo.DEVICE_PLAYBACK) {
HdmiCecLocalDevicePlayback playback = playback();
playback.dismissUiOnActiveSourceStatusRecovered();
+ playback.removeAction(RequestActiveSourceAction.class);
playback.setActiveSource(playback.getDeviceInfo().getLogicalAddress(), physicalAddress,
caller);
playback.wakeUpIfActiveSource();
diff --git a/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
index a33d70a..b0e9398 100644
--- a/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
@@ -22,11 +22,11 @@
import com.android.internal.annotations.VisibleForTesting;
/**
- * Feature action that sends <Request Active Source> message and waits for <Active Source> on TV
- * panels.
- * This action has a delay before sending <Request Active Source>. This is because it should wait
- * for a possible request from LauncherX and can be cancelled if an <Active Source> message was
- * received or the TV switched to another input.
+ * Feature action that sends <Request Active Source> message and waits for <Active Source>.
+ *
+ * For TV panels, this action has a delay before sending <Request Active Source>. This is because it
+ * should wait for a possible request from LauncherX and can be cancelled if an <Active Source>
+ * message was received or the TV switched to another input.
*/
public class RequestActiveSourceAction extends HdmiCecFeatureAction {
private static final String TAG = "RequestActiveSourceAction";
@@ -55,6 +55,13 @@
boolean start() {
Slog.v(TAG, "RequestActiveSourceAction started.");
+ if (localDevice().mService.isPlaybackDevice()) {
+ mState = STATE_WAIT_FOR_ACTIVE_SOURCE;
+ sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress()));
+ addTimer(mState, HdmiConfig.TIMEOUT_MS);
+ return true;
+ }
+
mState = STATE_WAIT_FOR_LAUNCHERX_API_CALL;
// We wait for default timeout to allow the message triggered by the LauncherX API call to
diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
index e40d855..1c5bd59 100644
--- a/services/core/java/com/android/server/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -127,6 +127,13 @@
*/
public abstract void notifyInputMethodConnectionActive(boolean connectionIsActive);
+ /**
+ * Notify user id changes to input.
+ *
+ * TODO(b/362473586): Cleanup after input shifts to Lifecycle with user change callbacks
+ */
+ public abstract void setCurrentUser(@UserIdInt int newUserId);
+
/** Callback interface for notifications relating to the lid switch. */
public interface LidSwitchCallback {
/**
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index bea520f..a421d04 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -175,6 +175,7 @@
private static final int MSG_DELIVER_INPUT_DEVICES_CHANGED = 1;
private static final int MSG_RELOAD_DEVICE_ALIASES = 2;
private static final int MSG_DELIVER_TABLET_MODE_CHANGED = 3;
+ private static final int MSG_CURRENT_USER_CHANGED = 4;
private static final int DEFAULT_VIBRATION_MAGNITUDE = 192;
private static final AdditionalDisplayInputProperties
@@ -184,6 +185,8 @@
private final Context mContext;
private final InputManagerHandler mHandler;
+ @UserIdInt
+ private int mCurrentUserId = UserHandle.USER_SYSTEM;
private DisplayManagerInternal mDisplayManagerInternal;
private WindowManagerInternal mWindowManagerInternal;
@@ -2982,6 +2985,10 @@
mKeyGestureController.unregisterKeyGestureHandler(handler, Binder.getCallingPid());
}
+ private void handleCurrentUserChanged(@UserIdInt int userId) {
+ mCurrentUserId = userId;
+ }
+
/**
* Callback interface implemented by the Window Manager.
*/
@@ -3150,6 +3157,9 @@
boolean inTabletMode = (boolean) args.arg1;
deliverTabletModeChanged(whenNanos, inTabletMode);
break;
+ case MSG_CURRENT_USER_CHANGED:
+ handleCurrentUserChanged((int) msg.obj);
+ break;
}
}
}
@@ -3513,6 +3523,11 @@
}
@Override
+ public void setCurrentUser(@UserIdInt int newUserId) {
+ mHandler.obtainMessage(MSG_CURRENT_USER_CHANGED, newUserId).sendToTarget();
+ }
+
+ @Override
public boolean setKernelWakeEnabled(int deviceId, boolean enabled) {
return mNative.setKernelWakeEnabled(deviceId, enabled);
}
diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java
index bd1625e..4d93e65 100644
--- a/services/core/java/com/android/server/input/KeyGestureController.java
+++ b/services/core/java/com/android/server/input/KeyGestureController.java
@@ -1012,16 +1012,16 @@
if (device == null) {
return;
}
+ KeyGestureEvent keyGestureEvent = new KeyGestureEvent(event);
if (event.action == KeyGestureEvent.ACTION_GESTURE_COMPLETE) {
KeyboardMetricsCollector.logKeyboardSystemsEventReportedAtom(device, event.keycodes,
- event.modifierState,
- KeyGestureEvent.keyGestureTypeToLogEvent(event.gestureType));
+ event.modifierState, keyGestureEvent.getLogEvent());
}
notifyAllListeners(event);
while (mLastHandledEvents.size() >= MAX_TRACKED_EVENTS) {
mLastHandledEvents.removeFirst();
}
- mLastHandledEvents.addLast(new KeyGestureEvent(event));
+ mLastHandledEvents.addLast(keyGestureEvent);
}
@MainThread
diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java b/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java
index 595a035..408df5a 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java
@@ -19,8 +19,7 @@
import android.annotation.Nullable;
/**
- * A wrapper class to represent an indexing range that is identified by the {@link
- * RuleIndexingController}.
+ * A wrapper class to represent an indexing range.
*/
public class RuleIndexRange {
private int mStartIndex;
diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
deleted file mode 100644
index 348a03b..0000000
--- a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
+++ /dev/null
@@ -1,128 +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.server.integrity.parser;
-
-import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
-import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY;
-import static com.android.server.integrity.parser.BinaryFileOperations.getIntValue;
-import static com.android.server.integrity.parser.BinaryFileOperations.getStringValue;
-
-import android.content.integrity.AppInstallMetadata;
-
-import com.android.server.integrity.model.BitInputStream;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.stream.Collectors;
-
-/** Helper class to identify the necessary indexes that needs to be read. */
-public class RuleIndexingController {
-
- private static LinkedHashMap<String, Integer> sPackageNameBasedIndexes;
- private static LinkedHashMap<String, Integer> sAppCertificateBasedIndexes;
- private static LinkedHashMap<String, Integer> sUnindexedRuleIndexes;
-
- /**
- * Provide the indexing file to read and the object will be constructed by reading and
- * identifying the indexes.
- */
- public RuleIndexingController(InputStream inputStream) throws IOException {
- BitInputStream bitInputStream = new BitInputStream(inputStream);
- sPackageNameBasedIndexes = getNextIndexGroup(bitInputStream);
- sAppCertificateBasedIndexes = getNextIndexGroup(bitInputStream);
- sUnindexedRuleIndexes = getNextIndexGroup(bitInputStream);
- }
-
- /**
- * Returns a list of integers with the starting and ending bytes of the rules that needs to be
- * read and evaluated.
- */
- public List<RuleIndexRange> identifyRulesToEvaluate(AppInstallMetadata appInstallMetadata) {
- List<RuleIndexRange> indexRanges = new ArrayList<>();
-
- // Add the range for package name indexes rules.
- indexRanges.add(
- searchIndexingKeysRangeContainingKey(
- sPackageNameBasedIndexes, appInstallMetadata.getPackageName()));
-
- // Add the range for app certificate indexes rules of all certificates.
- for (String appCertificate : appInstallMetadata.getAppCertificates()) {
- indexRanges.add(
- searchIndexingKeysRangeContainingKey(
- sAppCertificateBasedIndexes, appCertificate));
- }
-
- // Add the range for unindexed rules.
- indexRanges.add(
- new RuleIndexRange(
- sUnindexedRuleIndexes.get(START_INDEXING_KEY),
- sUnindexedRuleIndexes.get(END_INDEXING_KEY)));
-
- return indexRanges;
- }
-
- private LinkedHashMap<String, Integer> getNextIndexGroup(BitInputStream bitInputStream)
- throws IOException {
- LinkedHashMap<String, Integer> keyToIndexMap = new LinkedHashMap<>();
- while (bitInputStream.hasNext()) {
- String key = getStringValue(bitInputStream);
- int value = getIntValue(bitInputStream);
-
- keyToIndexMap.put(key, value);
-
- if (key.matches(END_INDEXING_KEY)) {
- break;
- }
- }
- if (keyToIndexMap.size() < 2) {
- throw new IllegalStateException("Indexing file is corrupt.");
- }
- return keyToIndexMap;
- }
-
- private static RuleIndexRange searchIndexingKeysRangeContainingKey(
- LinkedHashMap<String, Integer> indexMap, String searchedKey) {
- List<String> keys = indexMap.keySet().stream().collect(Collectors.toList());
- List<String> identifiedKeyRange =
- searchKeysRangeContainingKey(keys, searchedKey, 0, keys.size() - 1);
- return new RuleIndexRange(
- indexMap.get(identifiedKeyRange.get(0)), indexMap.get(identifiedKeyRange.get(1)));
- }
-
- private static List<String> searchKeysRangeContainingKey(
- List<String> sortedKeyList, String key, int startIndex, int endIndex) {
- if (endIndex <= startIndex) {
- throw new IllegalStateException("Indexing file is corrupt.");
- }
- if (endIndex - startIndex == 1) {
- return Arrays.asList(sortedKeyList.get(startIndex), sortedKeyList.get(endIndex));
- }
-
- int midKeyIndex = startIndex + ((endIndex - startIndex) / 2);
- String midKey = sortedKeyList.get(midKeyIndex);
-
- if (key.compareTo(midKey) >= 0) {
- return searchKeysRangeContainingKey(sortedKeyList, key, midKeyIndex, endIndex);
- } else {
- return searchKeysRangeContainingKey(sortedKeyList, key, startIndex, midKeyIndex);
- }
- }
-}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 48cc032..abf3da4 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -49,10 +49,6 @@
import static android.app.Notification.FLAG_PROMOTED_ONGOING;
import static android.app.Notification.FLAG_USER_INITIATED_JOB;
import static android.app.NotificationChannel.CONVERSATION_CHANNEL_ID_FORMAT;
-import static android.app.NotificationChannel.NEWS_ID;
-import static android.app.NotificationChannel.PROMOTIONS_ID;
-import static android.app.NotificationChannel.RECS_ID;
-import static android.app.NotificationChannel.SOCIAL_MEDIA_ID;
import static android.app.NotificationChannel.SYSTEM_RESERVED_IDS;
import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED;
import static android.app.NotificationManager.ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED;
@@ -108,9 +104,7 @@
import static android.os.UserHandle.USER_SYSTEM;
import static android.service.notification.Adjustment.KEY_TYPE;
import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
-import static android.service.notification.Adjustment.TYPE_NEWS;
import static android.service.notification.Adjustment.TYPE_PROMOTION;
-import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA;
import static android.service.notification.Flags.callstyleCallbackApi;
import static android.service.notification.Flags.notificationClassification;
import static android.service.notification.Flags.notificationForceGrouping;
@@ -6924,21 +6918,19 @@
@GuardedBy("mNotificationLock")
@Nullable
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
private NotificationChannel getClassificationChannelLocked(NotificationRecord r,
Bundle adjustments) {
int type = adjustments.getInt(KEY_TYPE);
- if (TYPE_NEWS == type) {
- return mPreferencesHelper.getNotificationChannel(
- r.getSbn().getPackageName(), r.getUid(), NEWS_ID, false);
- } else if (TYPE_PROMOTION == type) {
- return mPreferencesHelper.getNotificationChannel(
- r.getSbn().getPackageName(), r.getUid(), PROMOTIONS_ID, false);
- } else if (TYPE_SOCIAL_MEDIA == type) {
- return mPreferencesHelper.getNotificationChannel(
- r.getSbn().getPackageName(), r.getUid(), SOCIAL_MEDIA_ID, false);
- } else if (TYPE_CONTENT_RECOMMENDATION == type) {
- return mPreferencesHelper.getNotificationChannel(
- r.getSbn().getPackageName(), r.getUid(), RECS_ID, false);
+ if (type >= TYPE_PROMOTION && type <= TYPE_CONTENT_RECOMMENDATION) {
+ NotificationChannel channel = mPreferencesHelper.getReservedChannel(
+ r.getSbn().getPackageName(), r.getUid(), type);
+ if (channel == null) {
+ channel = mPreferencesHelper.createReservedChannel(
+ r.getSbn().getPackageName(), r.getUid(), type);
+ handleSavePolicyFile();
+ }
+ return channel;
}
return null;
}
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 964a5d0..d26a5aa 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -17,6 +17,7 @@
package com.android.server.notification;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
import static android.app.NotificationChannel.NEWS_ID;
import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID;
import static android.app.NotificationChannel.PROMOTIONS_ID;
@@ -32,6 +33,10 @@
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.os.UserHandle.USER_SYSTEM;
+import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
+import static android.service.notification.Adjustment.TYPE_NEWS;
+import static android.service.notification.Adjustment.TYPE_PROMOTION;
+import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA;
import static android.service.notification.Flags.notificationClassification;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
@@ -66,6 +71,7 @@
import android.os.UserHandle;
import android.permission.PermissionManager;
import android.provider.Settings;
+import android.service.notification.Adjustment;
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.NotificationListenerService;
import android.service.notification.RankingHelperProto;
@@ -549,10 +555,6 @@
Slog.e(TAG, "createDefaultChannelIfNeededLocked - Exception: " + e);
}
- if (notificationClassification()) {
- addReservedChannelsLocked(r);
- }
-
if (r.uid == UNKNOWN_UID) {
if (Flags.persistIncompleteRestoreData()) {
r.userId = userId;
@@ -587,7 +589,7 @@
private boolean deleteDefaultChannelIfNeededLocked(PackagePreferences r) throws
PackageManager.NameNotFoundException {
- if (!r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
+ if (!r.channels.containsKey(DEFAULT_CHANNEL_ID)) {
// Not present
return false;
}
@@ -598,7 +600,7 @@
}
// Remove Default Channel.
- r.channels.remove(NotificationChannel.DEFAULT_CHANNEL_ID);
+ r.channels.remove(DEFAULT_CHANNEL_ID);
return true;
}
@@ -609,8 +611,8 @@
return false;
}
- if (r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
- r.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID).setName(mContext.getString(
+ if (r.channels.containsKey(DEFAULT_CHANNEL_ID)) {
+ r.channels.get(DEFAULT_CHANNEL_ID).setName(mContext.getString(
com.android.internal.R.string.default_notification_channel_label));
return false;
}
@@ -623,7 +625,7 @@
// Create Default Channel
NotificationChannel channel;
channel = new NotificationChannel(
- NotificationChannel.DEFAULT_CHANNEL_ID,
+ DEFAULT_CHANNEL_ID,
mContext.getString(R.string.default_notification_channel_label),
r.importance);
channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
@@ -642,38 +644,25 @@
return true;
}
- private void addReservedChannelsLocked(PackagePreferences p) {
- if (!p.channels.containsKey(NotificationChannel.PROMOTIONS_ID)) {
- NotificationChannel channel = new NotificationChannel(
- NotificationChannel.PROMOTIONS_ID,
- mContext.getString(R.string.promotional_notification_channel_label),
- IMPORTANCE_LOW);
- p.channels.put(channel.getId(), channel);
+ private NotificationChannel addReservedChannelLocked(PackagePreferences p, String channelId) {
+ String label = "";
+ switch (channelId) {
+ case PROMOTIONS_ID:
+ label = mContext.getString(R.string.promotional_notification_channel_label);
+ break;
+ case RECS_ID:
+ label = mContext.getString(R.string.recs_notification_channel_label);
+ break;
+ case NEWS_ID:
+ label = mContext.getString(R.string.news_notification_channel_label);
+ break;
+ case SOCIAL_MEDIA_ID:
+ label = mContext.getString(R.string.social_notification_channel_label);
+ break;
}
-
- if (!p.channels.containsKey(NotificationChannel.RECS_ID)) {
- NotificationChannel channel = new NotificationChannel(
- NotificationChannel.RECS_ID,
- mContext.getString(R.string.recs_notification_channel_label),
- IMPORTANCE_LOW);
- p.channels.put(channel.getId(), channel);
- }
-
- if (!p.channels.containsKey(NotificationChannel.NEWS_ID)) {
- NotificationChannel channel = new NotificationChannel(
- NotificationChannel.NEWS_ID,
- mContext.getString(R.string.news_notification_channel_label),
- IMPORTANCE_LOW);
- p.channels.put(channel.getId(), channel);
- }
-
- if (!p.channels.containsKey(NotificationChannel.SOCIAL_MEDIA_ID)) {
- NotificationChannel channel = new NotificationChannel(
- NotificationChannel.SOCIAL_MEDIA_ID,
- mContext.getString(R.string.social_notification_channel_label),
- IMPORTANCE_LOW);
- p.channels.put(channel.getId(), channel);
- }
+ NotificationChannel channel = new NotificationChannel(channelId, label, IMPORTANCE_LOW);
+ p.channels.put(channelId, channel);
+ return channel;
}
public void writeXml(TypedXmlSerializer out, boolean forBackup, int userId) throws IOException {
@@ -1078,7 +1067,7 @@
if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) {
throw new IllegalArgumentException("NotificationChannelGroup doesn't exist");
}
- if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) {
+ if (DEFAULT_CHANNEL_ID.equals(channel.getId())) {
throw new IllegalArgumentException("Reserved id");
}
// Only the user can update bundle channel settings
@@ -1411,6 +1400,54 @@
}
}
+ private @Nullable String getChannelIdForBundleType(@Adjustment.Types int type) {
+ switch (type) {
+ case TYPE_CONTENT_RECOMMENDATION:
+ return RECS_ID;
+ case TYPE_NEWS:
+ return NEWS_ID;
+ case TYPE_PROMOTION:
+ return PROMOTIONS_ID;
+ case TYPE_SOCIAL_MEDIA:
+ return SOCIAL_MEDIA_ID;
+ }
+ return null;
+ }
+
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public NotificationChannel getReservedChannel(String pkg, int uid,
+ @Adjustment.Types int type) {
+ if (!notificationClassification()) {
+ return null;
+ }
+ Objects.requireNonNull(pkg);
+ String channelId = getChannelIdForBundleType(type);
+ if (channelId == null) {
+ return null;
+ }
+ NotificationChannel channel =
+ getConversationNotificationChannel(pkg, uid, channelId, null, true, false);
+ return channel;
+ }
+
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public NotificationChannel createReservedChannel(String pkg, int uid,
+ @Adjustment.Types int type) {
+ if (!notificationClassification()) {
+ return null;
+ }
+ Objects.requireNonNull(pkg);
+ PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
+ if (r == null) {
+ return null;
+ }
+ String channelId = getChannelIdForBundleType(type);
+ if (channelId == null) {
+ return null;
+ }
+ return addReservedChannelLocked(r, channelId);
+ }
+
@Override
public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
boolean includeDeleted) {
@@ -1429,7 +1466,7 @@
return null;
}
if (channelId == null) {
- channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
+ channelId = DEFAULT_CHANNEL_ID;
}
NotificationChannel channel = null;
if (conversationId != null) {
@@ -1540,7 +1577,7 @@
int N = r.channels.size() - 1;
for (int i = N; i >= 0; i--) {
String key = r.channels.keyAt(i);
- if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(key)) {
+ if (!DEFAULT_CHANNEL_ID.equals(key)) {
r.channels.remove(key);
}
}
@@ -1658,10 +1695,7 @@
&& (activeChannelFilter == null
|| (includeBlocked && nc.getImportance() == IMPORTANCE_NONE)
|| activeChannelFilter.contains(nc.getId()))
- && !PROMOTIONS_ID.equals(nc.getId())
- && !NEWS_ID.equals(nc.getId())
- && !SOCIAL_MEDIA_ID.equals(nc.getId())
- && !RECS_ID.equals(nc.getId());
+ && !SYSTEM_RESERVED_IDS.contains(nc.getId());
if (includeChannel) {
if (nc.getGroup() != null) {
if (r.groups.get(nc.getGroup()) != null) {
@@ -1924,9 +1958,23 @@
public boolean onlyHasDefaultChannel(String pkg, int uid) {
synchronized (mLock) {
PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
- if (r.channels.size() == (notificationClassification() ? 5 : 1)
- && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
- return true;
+ if (r.channels.containsKey(DEFAULT_CHANNEL_ID)) {
+ if (r.channels.size() == 1) {
+ return true;
+ }
+ if (notificationClassification()) {
+ if (r.channels.size() <= 5) {
+ for (NotificationChannel c : r.channels.values()) {
+ if (!SYSTEM_RESERVED_IDS.contains(c.getId()) &&
+ !DEFAULT_CHANNEL_ID.equals(c.getId())) {
+ return false;
+ }
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
}
return false;
}
@@ -2744,9 +2792,9 @@
PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
if (PackagePreferences.channels.containsKey(
- NotificationChannel.DEFAULT_CHANNEL_ID)) {
+ DEFAULT_CHANNEL_ID)) {
PackagePreferences.channels.get(
- NotificationChannel.DEFAULT_CHANNEL_ID).setName(
+ DEFAULT_CHANNEL_ID).setName(
context.getResources().getString(
R.string.default_notification_channel_label));
}
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
index 5ac883c..9c24abc 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -316,6 +316,8 @@
/* letsPersonalDataIntoProfile= */ true)
.addAction(MediaStore.ACTION_IMAGE_CAPTURE)
.addAction(MediaStore.ACTION_IMAGE_CAPTURE_SECURE)
+ .addAction(MediaStore.ACTION_MOTION_PHOTO_CAPTURE)
+ .addAction(MediaStore.ACTION_MOTION_PHOTO_CAPTURE_SECURE)
.addAction(MediaStore.ACTION_VIDEO_CAPTURE)
.addAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION)
.addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
@@ -438,6 +440,8 @@
/* letsPersonalDataIntoProfile= */ false)
.addAction(MediaStore.ACTION_IMAGE_CAPTURE)
.addAction(MediaStore.ACTION_IMAGE_CAPTURE_SECURE)
+ .addAction(MediaStore.ACTION_MOTION_PHOTO_CAPTURE)
+ .addAction(MediaStore.ACTION_MOTION_PHOTO_CAPTURE_SECURE)
.addAction(MediaStore.ACTION_VIDEO_CAPTURE)
.addAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION)
.addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index f6a808b..a59f4bd 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -39,6 +39,7 @@
import static org.xmlpull.v1.XmlPullParser.START_TAG;
import android.Manifest;
+import android.annotation.EnforcePermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -87,6 +88,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.ParcelableException;
+import android.os.PermissionEnforcer;
import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteCallbackList;
@@ -314,6 +316,8 @@
public PackageInstallerService(Context context, PackageManagerService pm,
Supplier<PackageParser2> apexParserSupplier) {
+ super(PermissionEnforcer.fromContext(context));
+
mContext = context;
mPm = pm;
@@ -1877,23 +1881,20 @@
}
@Override
+ @EnforcePermission(android.Manifest.permission.VERIFICATION_AGENT)
public @PackageInstaller.VerificationPolicy int getVerificationPolicy() {
- if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("You need the "
- + "com.android.permission.VERIFICATION_AGENT permission "
- + "to get the verification policy");
- }
+ getVerificationPolicy_enforcePermission();
return mVerificationPolicy.get();
}
@Override
+ @EnforcePermission(android.Manifest.permission.VERIFICATION_AGENT)
public boolean setVerificationPolicy(@PackageInstaller.VerificationPolicy int policy) {
- if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("You need the "
- + "com.android.permission.VERIFICATION_AGENT permission "
- + "to set the verification policy");
+ setVerificationPolicy_enforcePermission();
+ final int callingUid = getCallingUid();
+ // Only the verifier currently bound by the system can change the policy, except for Shell
+ if (!PackageManagerServiceUtils.isRootOrShell(callingUid)) {
+ mVerifierController.assertCallerIsCurrentVerifier(callingUid);
}
if (!isValidVerificationPolicy(policy)) {
return false;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 9a9e434..6ea5369 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2884,14 +2884,13 @@
}
// Send the request to the verifier and wait for its response before the rest of
// the installation can proceed.
+ final VerifierCallback verifierCallback = new VerifierCallback();
if (!mVerifierController.startVerificationSession(mPm::snapshotComputer, userId,
sessionId, getPackageName(), Uri.fromFile(stageDir), signingInfo,
declaredLibraries, mVerificationPolicy.get(), /* extensionParams= */ null,
- new VerifierCallback(), /* retry= */ false)) {
- // A verifier is installed but cannot be connected. Installation disallowed.
- onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR,
- "A verifier agent is available on device but cannot be connected.",
- /* extras= */ null);
+ verifierCallback, /* retry= */ false)) {
+ // A verifier is installed but cannot be connected.
+ verifierCallback.onConnectionFailed();
}
} else {
// No need to check with verifier. Proceed with the rest of the verification.
@@ -2995,7 +2994,6 @@
onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
"A verifier agent is available on device but cannot be connected.",
bundle);
-
});
}
/**
@@ -4447,9 +4445,7 @@
* @return the uid of the owner this session
*/
public int getInstallerUid() {
- synchronized (mLock) {
- return mInstallerUid;
- }
+ return mInstallerUid;
}
/**
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d78f122..807445e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4310,6 +4310,10 @@
if (intent == null) {
return;
}
+ final String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
Uri data = intent.getData();
if (data == null) {
return;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 4652c3a..f8e56e1 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -399,6 +399,10 @@
return runUnarchive();
case "get-domain-verification-agent":
return runGetDomainVerificationAgent();
+ case "get-verification-policy":
+ return runGetVerificationPolicy();
+ case "set-verification-policy":
+ return runSetVerificationPolicy();
default: {
if (ART_SERVICE_COMMANDS.contains(cmd)) {
return runArtServiceCommand();
@@ -4645,6 +4649,86 @@
return 0;
}
+ private int runGetVerificationPolicy() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ int userId = UserHandle.USER_ALL;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ if (userId != UserHandle.USER_ALL && userId != UserHandle.USER_CURRENT) {
+ UserManagerInternal umi =
+ LocalServices.getService(UserManagerInternal.class);
+ UserInfo userInfo = umi.getUserInfo(userId);
+ if (userInfo == null) {
+ pw.println("Failure [user " + userId + " doesn't exist]");
+ return 1;
+ }
+ }
+ } else {
+ pw.println("Error: Unknown option: " + opt);
+ return 1;
+ }
+ }
+ final int translatedUserId =
+ translateUserId(userId, UserHandle.USER_SYSTEM, "runGetVerificationPolicy");
+ try {
+ final IPackageInstaller installer = mInterface.getPackageInstaller();
+ // TODO(b/360129657): global verification policy should be per user
+ final int policy = installer.getVerificationPolicy();
+ pw.println(policy);
+ } catch (Exception e) {
+ pw.println("Failure [" + e.getMessage() + "]");
+ return 1;
+ }
+ return 0;
+ }
+
+ private int runSetVerificationPolicy() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ int userId = UserHandle.USER_ALL;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ if (userId != UserHandle.USER_ALL && userId != UserHandle.USER_CURRENT) {
+ UserManagerInternal umi =
+ LocalServices.getService(UserManagerInternal.class);
+ UserInfo userInfo = umi.getUserInfo(userId);
+ if (userInfo == null) {
+ pw.println("Failure [user " + userId + " doesn't exist]");
+ return 1;
+ }
+ }
+ } else {
+ pw.println("Error: Unknown option: " + opt);
+ return 1;
+ }
+ }
+ final String policyStr = getNextArg();
+ if (policyStr == null) {
+ pw.println("Error: policy not specified");
+ return 1;
+ }
+ final int translatedUserId =
+ translateUserId(userId, UserHandle.USER_SYSTEM, "runSetVerificationPolicy");
+ try {
+ final IPackageInstaller installer = mInterface.getPackageInstaller();
+ // TODO(b/360129657): global verification policy should be per user
+ final boolean success = installer.setVerificationPolicy(Integer.parseInt(policyStr));
+ if (!success) {
+ pw.println("Failure setting verification policy.");
+ return 1;
+ }
+ } catch (Exception e) {
+ pw.println("Failure [" + e.getMessage() + "]");
+ return 1;
+ }
+ return 0;
+ }
+
@Override
public void onHelp() {
final PrintWriter pw = getOutPrintWriter();
@@ -5073,6 +5157,14 @@
pw.println(" --user: return the agent of the given user (SYSTEM_USER if unspecified)");
pw.println(" get-package-storage-stats [--user <USER_ID>] <PACKAGE>");
pw.println(" Return the storage stats for the given app, if present");
+ pw.println(" get-verification-policy [--user USER_ID]");
+ pw.println(" Display current verification enforcement policy which will be applied to");
+ pw.println(" all the future installation sessions");
+ pw.println(" --user: show the policy of the given user (SYSTEM_USER if unspecified)");
+ pw.println(" set-verification-policy POLICY [--user USER_ID]");
+ pw.println(" Sets the verification policy of all the future installation sessions.");
+ pw.println(" --user: set the policy of the given user (SYSTEM_USER if unspecified)");
+ pw.println("");
pw.println("");
printArtServiceHelp();
pw.println("");
diff --git a/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java b/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java
index a35618b..78849d2 100644
--- a/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java
+++ b/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java
@@ -18,13 +18,12 @@
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
-import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
@@ -114,10 +113,17 @@
private final Context mContext;
private final Handler mHandler;
+ // Guards the remote service object, as well as the verifier name and UID, which should all be
+ // changed at the same time.
+ private final Object mLock = new Object();
@Nullable
+ @GuardedBy("mLock")
private ServiceConnector<IVerifierService> mRemoteService;
@Nullable
+ @GuardedBy("mLock")
private ComponentName mRemoteServiceComponentName;
+ @GuardedBy("mLock")
+ private int mRemoteServiceUid = INVALID_UID;
@NonNull
private Injector mInjector;
@@ -143,9 +149,11 @@
*/
@Nullable
public String getVerifierPackageName(Supplier<Computer> snapshotSupplier, int userId) {
- if (isVerifierConnected()) {
- // Verifier is connected or is being connected, so it must be installed.
- return mRemoteServiceComponentName.getPackageName();
+ synchronized (mLock) {
+ if (isVerifierConnectedLocked()) {
+ // Verifier is connected or is being connected, so it must be installed.
+ return mRemoteServiceComponentName.getPackageName();
+ }
}
// Verifier has been disconnected, or it hasn't been connected. Check if it's installed.
return mInjector.getVerifierPackageName(snapshotSupplier.get(), userId);
@@ -178,16 +186,29 @@
}
return true;
}
+ Computer snapshot = snapshotSupplier.get();
Pair<ServiceConnector<IVerifierService>, ComponentName> result =
- mInjector.getRemoteService(snapshotSupplier.get(), mContext, userId, mHandler);
+ mInjector.getRemoteService(snapshot, mContext, userId, mHandler);
if (result == null || result.first == null) {
if (DEBUG) {
Slog.i(TAG, "Unable to find a qualified verifier.");
}
return false;
}
- mRemoteService = result.first;
- mRemoteServiceComponentName = result.second;
+ final int verifierUid = snapshot.getPackageUidInternal(
+ result.second.getPackageName(), 0, userId, /* callingUid= */ SYSTEM_UID);
+ if (verifierUid == INVALID_UID) {
+ if (DEBUG) {
+ Slog.i(TAG, "Unable to find the UID of the qualified verifier.");
+ }
+ return false;
+ }
+ synchronized (mLock) {
+ mRemoteService = result.first;
+ mRemoteServiceComponentName = result.second;
+ mRemoteServiceUid = verifierUid;
+ }
+
if (DEBUG) {
Slog.i(TAG, "Connecting to a qualified verifier: " + mRemoteServiceComponentName);
}
@@ -212,10 +233,13 @@
}
private void destroy() {
- if (isVerifierConnected()) {
- mRemoteService.unbind();
- mRemoteService = null;
- mRemoteServiceComponentName = null;
+ synchronized (mLock) {
+ if (isVerifierConnectedLocked()) {
+ mRemoteService.unbind();
+ mRemoteService = null;
+ mRemoteServiceComponentName = null;
+ mRemoteServiceUid = INVALID_UID;
+ }
}
}
});
@@ -223,7 +247,8 @@
return true;
}
- private boolean isVerifierConnected() {
+ @GuardedBy("mLock")
+ private boolean isVerifierConnectedLocked() {
return mRemoteService != null && mRemoteServiceComponentName != null;
}
@@ -232,19 +257,21 @@
* requested for verification.
*/
public void notifyPackageNameAvailable(@NonNull String packageName) {
- if (!isVerifierConnected()) {
- if (DEBUG) {
- Slog.i(TAG, "Verifier is not connected. Not notifying package name available");
+ synchronized (mLock) {
+ if (!isVerifierConnectedLocked()) {
+ if (DEBUG) {
+ Slog.i(TAG, "Verifier is not connected. Not notifying package name available");
+ }
+ return;
}
- return;
+ // Best effort. We don't check for the result.
+ mRemoteService.run(service -> {
+ if (DEBUG) {
+ Slog.i(TAG, "Notifying package name available for " + packageName);
+ }
+ service.onPackageNameAvailable(packageName);
+ });
}
- // Best effort. We don't check for the result.
- mRemoteService.run(service -> {
- if (DEBUG) {
- Slog.i(TAG, "Notifying package name available for " + packageName);
- }
- service.onPackageNameAvailable(packageName);
- });
}
/**
@@ -253,27 +280,29 @@
* will no longer be requested for verification, possibly because the installation is canceled.
*/
public void notifyVerificationCancelled(@NonNull String packageName) {
- if (!isVerifierConnected()) {
- if (DEBUG) {
- Slog.i(TAG, "Verifier is not connected. Not notifying verification cancelled");
+ synchronized (mLock) {
+ if (!isVerifierConnectedLocked()) {
+ if (DEBUG) {
+ Slog.i(TAG, "Verifier is not connected. Not notifying verification cancelled");
+ }
+ return;
}
- return;
+ // Best effort. We don't check for the result.
+ mRemoteService.run(service -> {
+ if (DEBUG) {
+ Slog.i(TAG, "Notifying verification cancelled for " + packageName);
+ }
+ service.onVerificationCancelled(packageName);
+ });
}
- // Best effort. We don't check for the result.
- mRemoteService.run(service -> {
- if (DEBUG) {
- Slog.i(TAG, "Notifying verification cancelled for " + packageName);
- }
- service.onVerificationCancelled(packageName);
- });
}
/**
* Called to notify the bound verifier agent that a package that's pending installation needs
* to be verified right now.
* <p>The verification request must be sent to the verifier as soon as the verifier is
- * connected. If the connection cannot be made within {@link #CONNECTION_TIMEOUT_SECONDS}</p>
- * of when the request is sent out, we consider the verification to be failed and notify the
+ * connected. If the connection cannot be made within the specified time limit from
+ * when the request is sent out, we consider the verification to be failed and notify the
* installation session.</p>
* <p>If a response is not returned from the verifier agent within a timeout duration from the
* time the request is sent to the verifier, the verification will be considered a failure.</p>
@@ -291,43 +320,48 @@
if (!bindToVerifierServiceIfNeeded(snapshotSupplier, userId)) {
return false;
}
- if (!isVerifierConnected()) {
- if (DEBUG) {
- Slog.i(TAG, "Verifier is not connected. Not notifying verification required");
- }
- // Normally this should not happen because we just tried to bind. But if the verifier
- // just crashed or just became unavailable, we should notify the installation session so
- // it can finish with a verification failure.
- return false;
- }
// For now, the verification id is the same as the installation session id.
final int verificationId = installationSessionId;
- final VerificationSession session = new VerificationSession(
- /* id= */ verificationId,
- /* installSessionId= */ installationSessionId,
- packageName, stagedPackageUri, signingInfo, declaredLibraries, extensionParams,
- verificationPolicy, new VerificationSessionInterface(callback));
- AndroidFuture<Void> unusedFuture = mRemoteService.post(service -> {
- if (!retry) {
+ synchronized (mLock) {
+ if (!isVerifierConnectedLocked()) {
if (DEBUG) {
- Slog.i(TAG, "Notifying verification required for session " + verificationId);
+ Slog.i(TAG, "Verifier is not connected. Not notifying verification required");
}
- service.onVerificationRequired(session);
- } else {
- if (DEBUG) {
- Slog.i(TAG, "Notifying verification retry for session " + verificationId);
+ // Normally this should not happen because we just tried to bind. But if the
+ // verifier just crashed or just became unavailable, we should notify the
+ // installation session so it can finish with a verification failure.
+ return false;
+ }
+ final VerificationSession session = new VerificationSession(
+ /* id= */ verificationId,
+ /* installSessionId= */ installationSessionId,
+ packageName, stagedPackageUri, signingInfo, declaredLibraries, extensionParams,
+ verificationPolicy, new VerificationSessionInterface(callback));
+ AndroidFuture<Void> unusedFuture = mRemoteService.post(service -> {
+ if (!retry) {
+ if (DEBUG) {
+ Slog.i(TAG, "Notifying verification required for session "
+ + verificationId);
+ }
+ service.onVerificationRequired(session);
+ } else {
+ if (DEBUG) {
+ Slog.i(TAG, "Notifying verification retry for session "
+ + verificationId);
+ }
+ service.onVerificationRetry(session);
}
- service.onVerificationRetry(session);
- }
- }).orTimeout(mInjector.getVerifierConnectionTimeoutMillis(), TimeUnit.MILLISECONDS)
- .whenComplete((res, err) -> {
- if (err != null) {
- Slog.e(TAG, "Error notifying verification request for session " + verificationId,
- err);
- // Notify the installation session so it can finish with verification failure.
- callback.onConnectionFailed();
- }
- });
+ }).orTimeout(mInjector.getVerifierConnectionTimeoutMillis(), TimeUnit.MILLISECONDS)
+ .whenComplete((res, err) -> {
+ if (err != null) {
+ Slog.e(TAG, "Error notifying verification request for session "
+ + verificationId, err);
+ // Notify the installation session so it can finish with verification
+ // failure.
+ callback.onConnectionFailed();
+ }
+ });
+ }
// Keep track of the session status with the ID. Start counting down the session timeout.
final long defaultTimeoutMillis = mInjector.getVerificationRequestTimeoutMillis();
final long maxExtendedTimeoutMillis = mInjector.getMaxVerificationExtendedTimeoutMillis();
@@ -369,24 +403,27 @@
* Called to notify the bound verifier agent that a verification request has timed out.
*/
public void notifyVerificationTimeout(int verificationId) {
- if (!isVerifierConnected()) {
- if (DEBUG) {
- Slog.i(TAG,
- "Verifier is not connected. Not notifying timeout for " + verificationId);
+ synchronized (mLock) {
+ if (!isVerifierConnectedLocked()) {
+ if (DEBUG) {
+ Slog.i(TAG,
+ "Verifier is not connected. Not notifying timeout for "
+ + verificationId);
+ }
+ return;
}
- return;
+ AndroidFuture<Void> unusedFuture = mRemoteService.post(service -> {
+ if (DEBUG) {
+ Slog.i(TAG, "Notifying timeout for " + verificationId);
+ }
+ service.onVerificationTimeout(verificationId);
+ }).whenComplete((res, err) -> {
+ if (err != null) {
+ Slog.e(TAG, "Error notifying VerificationTimeout for session "
+ + verificationId, err);
+ }
+ });
}
- AndroidFuture<Void> unusedFuture = mRemoteService.post(service -> {
- if (DEBUG) {
- Slog.i(TAG, "Notifying timeout for " + verificationId);
- }
- service.onVerificationTimeout(verificationId);
- }).whenComplete((res, err) -> {
- if (err != null) {
- Slog.e(TAG, "Error notifying VerificationTimeout for session "
- + verificationId, (Throwable) err);
- }
- });
}
/**
@@ -405,17 +442,19 @@
}
}
- @RequiresPermission(Manifest.permission.VERIFICATION_AGENT)
- private void checkCallerPermission() {
- // TODO: think of a better way to test it on non-eng builds
- if (Build.IS_ENG) {
- return;
- }
- if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("You need the"
- + " com.android.permission.VERIFICATION_AGENT permission"
- + " to use VerificationSession APIs.");
+ /**
+ * Assert that the calling UID is the same as the UID of the currently connected verifier.
+ */
+ public void assertCallerIsCurrentVerifier(int callingUid) {
+ synchronized (mLock) {
+ if (!isVerifierConnectedLocked()) {
+ throw new IllegalStateException(
+ "Unable to proceed because the verifier has been disconnected.");
+ }
+ if (callingUid != mRemoteServiceUid) {
+ throw new IllegalStateException(
+ "Calling uid " + callingUid + " is not the current verifier.");
+ }
}
}
@@ -429,7 +468,7 @@
@Override
public long getTimeoutTime(int verificationId) {
- checkCallerPermission();
+ assertCallerIsCurrentVerifier(getCallingUid());
synchronized (mVerificationStatus) {
final VerificationStatusTracker tracker = mVerificationStatus.get(verificationId);
if (tracker == null) {
@@ -442,7 +481,7 @@
@Override
public long extendTimeRemaining(int verificationId, long additionalMs) {
- checkCallerPermission();
+ assertCallerIsCurrentVerifier(getCallingUid());
synchronized (mVerificationStatus) {
final VerificationStatusTracker tracker = mVerificationStatus.get(verificationId);
if (tracker == null) {
@@ -456,7 +495,7 @@
@Override
public boolean setVerificationPolicy(int verificationId,
@PackageInstaller.VerificationPolicy int policy) {
- checkCallerPermission();
+ assertCallerIsCurrentVerifier(getCallingUid());
synchronized (mVerificationStatus) {
final VerificationStatusTracker tracker = mVerificationStatus.get(verificationId);
if (tracker == null) {
@@ -469,7 +508,7 @@
@Override
public void reportVerificationIncomplete(int id, int reason) {
- checkCallerPermission();
+ assertCallerIsCurrentVerifier(getCallingUid());
final VerificationStatusTracker tracker;
synchronized (mVerificationStatus) {
tracker = mVerificationStatus.get(id);
@@ -484,15 +523,9 @@
}
@Override
- public void reportVerificationComplete(int id, VerificationStatus verificationStatus) {
- reportVerificationCompleteWithExtensionResponse(id, verificationStatus,
- /* extensionResponse= */ null);
- }
-
- @Override
- public void reportVerificationCompleteWithExtensionResponse(int id,
- VerificationStatus verificationStatus, PersistableBundle extensionResponse) {
- checkCallerPermission();
+ public void reportVerificationComplete(int id, VerificationStatus verificationStatus,
+ @Nullable PersistableBundle extensionResponse) {
+ assertCallerIsCurrentVerifier(getCallingUid());
final VerificationStatusTracker tracker;
synchronized (mVerificationStatus) {
tracker = mVerificationStatus.get(id);
diff --git a/services/core/java/com/android/server/policy/ModifierShortcutManager.java b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
index 66ec53e..a1236e5 100644
--- a/services/core/java/com/android/server/policy/ModifierShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
@@ -31,6 +31,7 @@
import android.content.pm.PackageManager;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Icon;
+import android.hardware.input.AppLaunchData;
import android.hardware.input.KeyGestureEvent;
import android.os.Handler;
import android.os.RemoteException;
@@ -48,6 +49,7 @@
import android.view.KeyboardShortcutInfo;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.IShortcutService;
import com.android.internal.util.XmlUtils;
@@ -63,6 +65,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
/**
@@ -131,7 +134,10 @@
private boolean mConsumeSearchKeyUp = true;
private UserHandle mCurrentUser;
private final Map<Pair<Character, Boolean>, Bookmark> mBookmarks = new HashMap<>();
+ @GuardedBy("mAppIntentCache")
+ private final Map<AppLaunchData, Intent> mAppIntentCache = new HashMap<>();
+ @SuppressLint("MissingPermission")
ModifierShortcutManager(Context context, Handler handler, UserHandle currentUser) {
mContext = context;
mHandler = handler;
@@ -146,6 +152,17 @@
} else {
mRoleIntents.remove(roleName);
}
+ synchronized (mAppIntentCache) {
+ mAppIntentCache.entrySet().removeIf(
+ entry -> {
+ if (entry.getKey() instanceof AppLaunchData.RoleData) {
+ return Objects.equals(
+ ((AppLaunchData.RoleData) entry.getKey()).getRole(),
+ roleName);
+ }
+ return false;
+ });
+ }
}, UserHandle.ALL);
mCurrentUser = currentUser;
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
@@ -159,6 +176,10 @@
// so clear the cache.
clearRoleIntents();
clearComponentIntents();
+
+ synchronized (mAppIntentCache) {
+ mAppIntentCache.clear();
+ }
}
void clearRoleIntents() {
@@ -748,6 +769,46 @@
shortcuts);
}
+ private Intent getIntentFromAppLaunchData(@NonNull AppLaunchData data) {
+ Context context = mContext.createContextAsUser(mCurrentUser, 0);
+ synchronized (mAppIntentCache) {
+ Intent intent = mAppIntentCache.get(data);
+ if (intent != null) {
+ return intent;
+ }
+ if (data instanceof AppLaunchData.CategoryData) {
+ intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN,
+ ((AppLaunchData.CategoryData) data).getCategory());
+ } else if (data instanceof AppLaunchData.RoleData) {
+ intent = getRoleLaunchIntent(context, ((AppLaunchData.RoleData) data).getRole());
+ } else if (data instanceof AppLaunchData.ComponentData) {
+ AppLaunchData.ComponentData componentData = (AppLaunchData.ComponentData) data;
+ intent = resolveComponentNameIntent(context, componentData.getPackageName(),
+ componentData.getClassName());
+ }
+ if (intent != null) {
+ mAppIntentCache.put(data, intent);
+ }
+ return intent;
+ }
+ }
+
+ boolean launchApplication(@NonNull AppLaunchData data) {
+ Intent intent = getIntentFromAppLaunchData(data);
+ if (intent == null) {
+ return false;
+ }
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ mContext.startActivityAsUser(intent, mCurrentUser);
+ return true;
+ } catch (ActivityNotFoundException ex) {
+ Slog.w(TAG, "Not launching app because "
+ + "the activity to which it refers to was not found: " + data);
+ }
+ return false;
+ }
+
/**
* Given an intent to launch an application and the character and shift state that should
* trigger it, return a suitable {@link KeyboardShortcutInfo} that contains the label and
@@ -869,7 +930,7 @@
// TODO(b/280423320): Add new field package name associated in the
// KeyboardShortcutEvent atom and log it accordingly.
- return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME;
+ return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION;
}
@KeyGestureEvent.KeyGestureType
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6a7f22e..3ab1009 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -149,6 +149,7 @@
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPlaybackClient;
import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
+import android.hardware.input.AppLaunchData;
import android.hardware.input.InputManager;
import android.hardware.input.KeyGestureEvent;
import android.media.AudioManager;
@@ -4037,6 +4038,7 @@
case KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH:
case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT:
case KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION:
return true;
case KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD:
case KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD:
@@ -4279,6 +4281,14 @@
return true;
}
break;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION:
+ AppLaunchData data = event.getAppLaunchData();
+ if (complete && isUserSetupComplete() && !keyguardOn
+ && data != null && mModifierShortcutManager.launchApplication(data)) {
+ dismissKeyboardShortcutsMenu();
+ return true;
+ }
+ break;
}
return false;
}
@@ -6948,6 +6958,7 @@
if (modifierShortcutManagerMultiuser()) {
mModifierShortcutManager.setCurrentUser(UserHandle.of(newUserId));
}
+ mInputManagerInternal.setCurrentUser(newUserId);
}
@Override
diff --git a/services/core/java/com/android/server/security/forensic/DataAggregator.java b/services/core/java/com/android/server/security/forensic/DataAggregator.java
new file mode 100644
index 0000000..0079818
--- /dev/null
+++ b/services/core/java/com/android/server/security/forensic/DataAggregator.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.security.forensic;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.security.forensic.ForensicEvent;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.ServiceThread;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DataAggregator {
+ private static final String TAG = "Forensic DataAggregator";
+ private static final int MSG_SINGLE_DATA = 0;
+ private static final int MSG_BATCH_DATA = 1;
+ private static final int MSG_DISABLE = 2;
+
+ private static final int STORED_EVENTS_SIZE_LIMIT = 1024;
+ private final ForensicService mForensicService;
+ private final ArrayList<DataSource> mDataSources;
+
+ private List<ForensicEvent> mStoredEvents = new ArrayList<>();
+ private ServiceThread mHandlerThread;
+ private Handler mHandler;
+ public DataAggregator(ForensicService forensicService) {
+ mForensicService = forensicService;
+ mDataSources = new ArrayList<DataSource>();
+ }
+
+ @VisibleForTesting
+ void setHandler(Looper looper, ServiceThread serviceThread) {
+ mHandlerThread = serviceThread;
+ mHandler = new EventHandler(looper, this);
+ }
+
+ /**
+ * Initialize DataSources
+ * @return Whether the initialization succeeds.
+ */
+ // TODO: Add the corresponding data sources
+ public boolean initialize() {
+ return true;
+ }
+
+ /**
+ * Enable the data collection of all DataSources.
+ */
+ public void enable() {
+ mHandlerThread = new ServiceThread(TAG, android.os.Process.THREAD_PRIORITY_BACKGROUND,
+ /* allowIo */ false);
+ mHandlerThread.start();
+ mHandler = new EventHandler(mHandlerThread.getLooper(), this);
+ for (DataSource ds : mDataSources) {
+ ds.enable();
+ }
+ }
+
+ /**
+ * DataSource calls it to transmit a single event.
+ */
+ public void addSingleData(ForensicEvent event) {
+ mHandler.obtainMessage(MSG_SINGLE_DATA, event).sendToTarget();
+ }
+
+ /**
+ * DataSource calls it to transmit list of events.
+ */
+ public void addBatchData(List<ForensicEvent> events) {
+ mHandler.obtainMessage(MSG_BATCH_DATA, events).sendToTarget();
+ }
+
+ /**
+ * Disable the data collection of all DataSources.
+ */
+ public void disable() {
+ mHandler.obtainMessage(MSG_DISABLE).sendToTarget();
+ }
+
+ private void onNewSingleData(ForensicEvent event) {
+ if (mStoredEvents.size() < STORED_EVENTS_SIZE_LIMIT) {
+ mStoredEvents.add(event);
+ } else {
+ mForensicService.addNewData(mStoredEvents);
+ mStoredEvents = new ArrayList<>();
+ }
+ }
+
+ private void onNewBatchData(List<ForensicEvent> events) {
+ mForensicService.addNewData(events);
+ }
+
+ private void onDisable() {
+ for (DataSource ds : mDataSources) {
+ ds.disable();
+ }
+ mHandlerThread.quitSafely();
+ mHandlerThread = null;
+ }
+
+ private static class EventHandler extends Handler {
+ private final DataAggregator mDataAggregator;
+ EventHandler(Looper looper, DataAggregator dataAggregator) {
+ super(looper);
+ mDataAggregator = dataAggregator;
+ }
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SINGLE_DATA:
+ mDataAggregator.onNewSingleData((ForensicEvent) msg.obj);
+ break;
+ case MSG_BATCH_DATA:
+ mDataAggregator.onNewBatchData((List<ForensicEvent>) msg.obj);
+ break;
+ case MSG_DISABLE:
+ mDataAggregator.onDisable();
+ break;
+ default:
+ Slog.w(TAG, "Unknown message: " + msg.what);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/security/forensic/DataSource.java b/services/core/java/com/android/server/security/forensic/DataSource.java
new file mode 100644
index 0000000..da7ee21
--- /dev/null
+++ b/services/core/java/com/android/server/security/forensic/DataSource.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.security.forensic;
+
+public interface DataSource {
+ /**
+ * Enable the data collection.
+ */
+ void enable();
+
+ /**
+ * Disable the data collection.
+ */
+ void disable();
+}
diff --git a/services/core/java/com/android/server/security/forensic/ForensicService.java b/services/core/java/com/android/server/security/forensic/ForensicService.java
index 20c648e..53b07c0 100644
--- a/services/core/java/com/android/server/security/forensic/ForensicService.java
+++ b/services/core/java/com/android/server/security/forensic/ForensicService.java
@@ -22,6 +22,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.security.forensic.ForensicEvent;
import android.security.forensic.IForensicService;
import android.security.forensic.IForensicServiceCommandCallback;
import android.security.forensic.IForensicServiceStateCallback;
@@ -32,6 +33,7 @@
import com.android.server.SystemService;
import java.util.ArrayList;
+import java.util.List;
/**
* @hide
@@ -64,6 +66,7 @@
private final Context mContext;
private final Handler mHandler;
private final BackupTransportConnection mBackupTransportConnection;
+ private final DataAggregator mDataAggregator;
private final BinderService mBinderService;
private final ArrayList<IForensicServiceStateCallback> mStateMonitors = new ArrayList<>();
@@ -79,6 +82,7 @@
mContext = injector.getContext();
mHandler = new EventHandler(injector.getLooper(), this);
mBackupTransportConnection = injector.getBackupTransportConnection();
+ mDataAggregator = injector.getDataAggregator(this);
mBinderService = new BinderService(this);
}
@@ -167,6 +171,9 @@
Slog.e(TAG, "RemoteException", e);
}
break;
+ case MSG_BACKUP:
+ mService.backup((List<ForensicEvent>) msg.obj);
+ break;
default:
Slog.w(TAG, "Unknown message: " + msg.what);
}
@@ -192,6 +199,10 @@
private void makeVisible(IForensicServiceCommandCallback callback) throws RemoteException {
switch (mState) {
case STATE_INVISIBLE:
+ if (!mDataAggregator.initialize()) {
+ callback.onFailure(ERROR_DATA_SOURCE_UNAVAILABLE);
+ break;
+ }
mState = STATE_VISIBLE;
notifyStateMonitors();
callback.onSuccess();
@@ -227,6 +238,7 @@
callback.onFailure(ERROR_BACKUP_TRANSPORT_UNAVAILABLE);
break;
}
+ mDataAggregator.enable();
mState = STATE_ENABLED;
notifyStateMonitors();
callback.onSuccess();
@@ -243,6 +255,7 @@
switch (mState) {
case STATE_ENABLED:
mBackupTransportConnection.release();
+ mDataAggregator.disable();
mState = STATE_VISIBLE;
notifyStateMonitors();
callback.onSuccess();
@@ -255,6 +268,17 @@
}
}
+ /**
+ * Add a list of ForensicEvent.
+ */
+ public void addNewData(List<ForensicEvent> events) {
+ mHandler.obtainMessage(MSG_BACKUP, events).sendToTarget();
+ }
+
+ private void backup(List<ForensicEvent> events) {
+ mBackupTransportConnection.addData(events);
+ }
+
@Override
public void onStart() {
try {
@@ -275,6 +299,8 @@
Looper getLooper();
BackupTransportConnection getBackupTransportConnection();
+
+ DataAggregator getDataAggregator(ForensicService forensicService);
}
private static final class InjectorImpl implements Injector {
@@ -303,6 +329,11 @@
public BackupTransportConnection getBackupTransportConnection() {
return new BackupTransportConnection(mContext);
}
+
+ @Override
+ public DataAggregator getDataAggregator(ForensicService forensicService) {
+ return new DataAggregator(forensicService);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 25dfbd7..af7b8d6 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1792,8 +1792,6 @@
}
prevDc.onRunningActivityChanged();
- // TODO(b/169035022): move to a more-appropriate place.
- mTransitionController.collect(this);
if (prevDc.mOpeningApps.remove(this)) {
// Transfer opening transition to new display.
mDisplayContent.mOpeningApps.add(this);
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index a2c2dfc..a82aae7 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -34,7 +34,6 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
-import android.app.BackgroundStartPrivileges;
import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -280,8 +279,8 @@
* @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
* @param originatingPendingIntent PendingIntentRecord that originated this activity start or
* null if not originated by PendingIntent
- * @param forcedBalByPiSender If set to allow, the
- * PendingIntent's sender will try to force allow background activity starts.
+ * @param allowBalExemptionForSystemProcess If set to {@code true}, the
+ * PendingIntent's sender will allow additional exemptions.
* This is only possible if the sender of the PendingIntent is a system process.
*/
final int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
@@ -289,7 +288,7 @@
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges forcedBalByPiSender) {
+ boolean allowBalExemptionForSystemProcess) {
userId = checkTargetUser(userId, validateIncomingUser, realCallingPid, realCallingUid,
reason);
@@ -310,7 +309,7 @@
.setUserId(userId)
.setInTask(inTask)
.setOriginatingPendingIntent(originatingPendingIntent)
- .setBackgroundStartPrivileges(forcedBalByPiSender)
+ .setAllowBalExemptionForSystemProcess(allowBalExemptionForSystemProcess)
.execute();
}
@@ -325,18 +324,18 @@
* @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
* @param originatingPendingIntent PendingIntentRecord that originated this activity start or
* null if not originated by PendingIntent
- * @param forcedBalByPiSender If set to allow, the
- * PendingIntent's sender will try to force allow background activity starts.
+ * @param allowBalExemptionForSystemProcess If set to {@code true}, the
+ * PendingIntent's sender will allow additional exemptions.
* This is only possible if the sender of the PendingIntent is a system process.
*/
final int startActivitiesInPackage(int uid, String callingPackage,
@Nullable String callingFeatureId, Intent[] intents, String[] resolvedTypes,
IBinder resultTo, SafeActivityOptions options, int userId, boolean validateIncomingUser,
PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges forcedBalByPiSender) {
+ boolean allowBalExemptionForSystemProcess) {
return startActivitiesInPackage(uid, 0 /* realCallingPid */, -1 /* realCallingUid */,
callingPackage, callingFeatureId, intents, resolvedTypes, resultTo, options, userId,
- validateIncomingUser, originatingPendingIntent, forcedBalByPiSender);
+ validateIncomingUser, originatingPendingIntent, allowBalExemptionForSystemProcess);
}
/**
@@ -351,15 +350,15 @@
* @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
* @param originatingPendingIntent PendingIntentRecord that originated this activity start or
* null if not originated by PendingIntent
- * @param forcedBalByPiSender If set to allow, the
- * PendingIntent's sender will try to force allow background activity starts.
+ * @param allowBalExemptionForSystemProcess If set to {@code true}, the
+ * PendingIntent's sender will allow additional exemptions.
* This is only possible if the sender of the PendingIntent is a system process.
*/
final int startActivitiesInPackage(int uid, int realCallingPid, int realCallingUid,
String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges forcedBalByPiSender) {
+ boolean allowBalExemptionForSystemProcess) {
final String reason = "startActivityInPackage";
@@ -369,14 +368,14 @@
// TODO: Switch to user app stacks here.
return startActivities(null, uid, realCallingPid, realCallingUid, callingPackage,
callingFeatureId, intents, resolvedTypes, resultTo, options, userId, reason,
- originatingPendingIntent, forcedBalByPiSender);
+ originatingPendingIntent, allowBalExemptionForSystemProcess);
}
int startActivities(IApplicationThread caller, int callingUid, int incomingRealCallingPid,
int incomingRealCallingUid, String callingPackage, @Nullable String callingFeatureId,
Intent[] intents, String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options,
int userId, String reason, PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges forcedBalByPiSender) {
+ boolean allowBalExemptionForSystemProcess) {
if (intents == null) {
throw new NullPointerException("intents is null");
}
@@ -518,7 +517,7 @@
// top one as otherwise an activity below might consume it.
.setAllowPendingRemoteAnimationRegistryLookup(top /* allowLookup*/)
.setOriginatingPendingIntent(originatingPendingIntent)
- .setBackgroundStartPrivileges(forcedBalByPiSender);
+ .setAllowBalExemptionForSystemProcess(allowBalExemptionForSystemProcess);
}
// Log if the activities to be started have different uids.
if (startingUidPkgs.size() > 1) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 8dab717..6b482f9 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -95,7 +95,6 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
-import android.app.BackgroundStartPrivileges;
import android.app.IApplicationThread;
import android.app.PendingIntent;
import android.app.ProfilerInfo;
@@ -430,7 +429,7 @@
WaitResult waitResult;
int filterCallingUid;
PendingIntentRecord originatingPendingIntent;
- BackgroundStartPrivileges forcedBalByPiSender;
+ boolean allowBalExemptionForSystemProcess;
boolean freezeScreen;
final StringBuilder logMessage = new StringBuilder();
@@ -496,7 +495,7 @@
allowPendingRemoteAnimationRegistryLookup = true;
filterCallingUid = UserHandle.USER_NULL;
originatingPendingIntent = null;
- forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ allowBalExemptionForSystemProcess = false;
freezeScreen = false;
errorCallbackToken = null;
}
@@ -540,7 +539,7 @@
= request.allowPendingRemoteAnimationRegistryLookup;
filterCallingUid = request.filterCallingUid;
originatingPendingIntent = request.originatingPendingIntent;
- forcedBalByPiSender = request.forcedBalByPiSender;
+ allowBalExemptionForSystemProcess = request.allowBalExemptionForSystemProcess;
freezeScreen = request.freezeScreen;
errorCallbackToken = request.errorCallbackToken;
}
@@ -1275,8 +1274,8 @@
"Creator PermissionPolicyService.checkStartActivity Caused abortion.",
intent, intentCreatorUid, intentCreatorPackage, callingUid, callingPackage);
}
- intent.removeCreatorTokenInfo();
}
+ intent.removeCreatorToken();
// Merge the two options bundles, while realCallerOptions takes precedence.
ActivityOptions checkedOptions = options != null
@@ -1298,7 +1297,7 @@
realCallingPid,
callerApp,
request.originatingPendingIntent,
- request.forcedBalByPiSender,
+ request.allowBalExemptionForSystemProcess,
resultRecord,
intent,
checkedOptions);
@@ -3533,8 +3532,9 @@
return this;
}
- ActivityStarter setBackgroundStartPrivileges(BackgroundStartPrivileges forcedBalByPiSender) {
- mRequest.forcedBalByPiSender = forcedBalByPiSender;
+ ActivityStarter setAllowBalExemptionForSystemProcess(
+ boolean allowBalExemptionForSystemProcess) {
+ mRequest.allowBalExemptionForSystemProcess = allowBalExemptionForSystemProcess;
return this;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 3560565..0a57cb5 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -21,7 +21,6 @@
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AppProtoEnums;
-import android.app.BackgroundStartPrivileges;
import android.app.IActivityManager;
import android.app.IAppTask;
import android.app.IApplicationThread;
@@ -179,15 +178,15 @@
* @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
* @param originatingPendingIntent PendingIntentRecord that originated this activity start or
* null if not originated by PendingIntent
- * @param forcedBalByPiSender If set to allow, the
- * PendingIntent's sender will try to force allow background activity starts.
+ * @param allowBalExemptionForSystemProcess If set to {@code true}, the
+ * PendingIntent's sender will allow additional exemptions.
* This is only possible if the sender of the PendingIntent is a system process.
*/
public abstract int startActivitiesInPackage(int uid, int realCallingPid, int realCallingUid,
String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges forcedBalByPiSender);
+ boolean allowBalExemptionForSystemProcess);
/**
* Start intent as a package.
@@ -202,8 +201,8 @@
* @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
* @param originatingPendingIntent PendingIntentRecord that originated this activity start or
* null if not originated by PendingIntent
- * @param forcedBalByPiSender If set to allow, the
- * PendingIntent's sender will try to force allow background activity starts.
+ * @param allowBalExemptionForSystemProcess If set to {@code true}, the
+ * PendingIntent's sender will allow additional exemptions.
* This is only possible if the sender of the PendingIntent is a system process.
*/
public abstract int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
@@ -211,7 +210,7 @@
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges forcedBalByPiSender);
+ boolean allowBalExemptionForSystemProcess);
/**
* Callback to be called on certain activity start scenarios.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 111e74e..96cb2f2 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -145,7 +145,6 @@
import android.app.AnrController;
import android.app.AppGlobals;
import android.app.AppOpsManager;
-import android.app.BackgroundStartPrivileges;
import android.app.Dialog;
import android.app.IActivityClientController;
import android.app.IActivityController;
@@ -1228,7 +1227,6 @@
String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
Bundle bOptions) {
- mAmInternal.addCreatorToken(intent, callingPackage);
return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,
UserHandle.getCallingUserId());
@@ -1251,7 +1249,7 @@
return getActivityStartController().startActivities(caller, -1, 0, -1, callingPackage,
callingFeatureId, intents, resolvedTypes, resultTo,
SafeActivityOptions.fromBundle(bOptions), userId, reason,
- null /* originatingPendingIntent */, BackgroundStartPrivileges.NONE);
+ null /* originatingPendingIntent */, false);
}
@Override
@@ -1560,7 +1558,7 @@
// To start the dream from background, we need to start it from a persistent
// system process. Here we set the real calling uid to the system server uid
.setRealCallingUid(Binder.getCallingUid())
- .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
+ .setAllowBalExemptionForSystemProcess(true)
.execute();
final ActivityRecord started = outActivity[0];
@@ -1711,7 +1709,7 @@
.setFilterCallingUid(isResolver ? 0 /* system */ : targetUid)
// The target may well be in the background, which would normally prevent it
// from starting an activity. Here we definitely want the start to succeed.
- .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
+ .setAllowBalExemptionForSystemProcess(true)
.execute();
} catch (SecurityException e) {
// XXX need to figure out how to propagate to original app.
@@ -1757,7 +1755,7 @@
.setProfilerInfo(profilerInfo)
.setActivityOptions(createSafeActivityOptionsWithBalAllowed(bOptions))
.setUserId(userId)
- .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
+ .setAllowBalExemptionForSystemProcess(true)
.execute();
}
@@ -1784,7 +1782,7 @@
.setResolvedType(resolvedType)
.setActivityOptions(createSafeActivityOptionsWithBalAllowed(bOptions))
.setUserId(userId)
- .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
+ .setAllowBalExemptionForSystemProcess(true)
.execute();
} finally {
Binder.restoreCallingIdentity(origId);
@@ -2256,7 +2254,7 @@
-1,
callerApp,
null,
- BackgroundStartPrivileges.NONE,
+ false,
null,
null,
null);
@@ -6066,7 +6064,7 @@
intents, resolvedTypes, null /* resultTo */,
SafeActivityOptions.fromBundle(bOptions), userId,
false /* validateIncomingUser */, null /* originatingPendingIntent */,
- BackgroundStartPrivileges.NONE);
+ false);
}
@Override
@@ -6074,12 +6072,12 @@
String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges forcedBalByPiSender) {
+ boolean allowBalExemptionForSystemProcess) {
assertPackageMatchesCallingUid(callingPackage);
return getActivityStartController().startActivitiesInPackage(uid, realCallingPid,
realCallingUid, callingPackage, callingFeatureId, intents, resolvedTypes,
resultTo, options, userId, validateIncomingUser, originatingPendingIntent,
- forcedBalByPiSender);
+ allowBalExemptionForSystemProcess);
}
@Override
@@ -6088,13 +6086,13 @@
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges forcedBalByPiSender) {
+ boolean allowBalExemptionForSystemProcess) {
assertPackageMatchesCallingUid(callingPackage);
return getActivityStartController().startActivityInPackage(uid, realCallingPid,
realCallingUid, callingPackage, callingFeatureId, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, options, userId, inTask,
reason, validateIncomingUser, originatingPendingIntent,
- forcedBalByPiSender);
+ allowBalExemptionForSystemProcess);
}
@Override
@@ -6125,7 +6123,7 @@
.setActivityOptions(createSafeActivityOptionsWithBalAllowed(options))
.setRealCallingUid(Binder.getCallingUid())
.setUserId(userId)
- .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
+ .setAllowBalExemptionForSystemProcess(true)
.setFreezeScreen(true)
.execute();
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 6a1f28d..18b23ad 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -96,7 +96,6 @@
import android.app.ActivityOptions;
import android.app.AppOpsManager;
import android.app.AppOpsManagerInternal;
-import android.app.BackgroundStartPrivileges;
import android.app.IActivityClientController;
import android.app.ProfilerInfo;
import android.app.ResultInfo;
@@ -242,6 +241,8 @@
static {
ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_IMAGE_CAPTURE,
Manifest.permission.CAMERA);
+ ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_MOTION_PHOTO_CAPTURE,
+ Manifest.permission.CAMERA);
ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_VIDEO_CAPTURE,
Manifest.permission.CAMERA);
ACTION_TO_RUNTIME_PERMISSION.put(Intent.ACTION_CALL,
@@ -2873,7 +2874,7 @@
callingPid, callingUid, callingPackage, callingFeatureId, intent, null, null,
null, 0, 0, options, userId, task, "startActivityFromRecents",
false /* validateIncomingUser */, null /* originatingPendingIntent */,
- BackgroundStartPrivileges.NONE);
+ /* allowBalExemptionForSystemProcess */ false);
} finally {
SaferIntentUtils.DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.set(false);
synchronized (mService.mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index eee4c86..ef34dab 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -17,12 +17,11 @@
package com.android.server.wm;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
-import static com.android.server.wm.BackgroundActivityStartController.BalVerdict;
import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
+import static com.android.server.wm.BackgroundActivityStartController.BalVerdict;
import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
import android.app.ActivityManager;
-import android.app.BackgroundStartPrivileges;
import android.app.IAppTask;
import android.app.IApplicationThread;
import android.content.Intent;
@@ -136,7 +135,7 @@
-1,
callerApp,
null,
- BackgroundStartPrivileges.NONE,
+ false,
null,
null,
null);
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index f9902cf..3e553ad 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -304,7 +304,7 @@
private final @ActivityManager.ProcessState int mRealCallingUidProcState;
private final boolean mIsRealCallingUidPersistentSystemProcess;
private final PendingIntentRecord mOriginatingPendingIntent;
- private final BackgroundStartPrivileges mForcedBalByPiSender;
+ private final boolean mAllowBalExemptionForSystemProcess;
private final Intent mIntent;
private final WindowProcessController mCallerApp;
private final WindowProcessController mRealCallerApp;
@@ -319,7 +319,7 @@
int realCallingUid, int realCallingPid,
WindowProcessController callerApp,
PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges forcedBalByPiSender,
+ boolean allowBalExemptionForSystemProcess,
ActivityRecord resultRecord,
Intent intent,
ActivityOptions checkedOptions) {
@@ -329,7 +329,7 @@
mRealCallingUid = realCallingUid;
mRealCallingPid = realCallingPid;
mCallerApp = callerApp;
- mForcedBalByPiSender = forcedBalByPiSender;
+ mAllowBalExemptionForSystemProcess = allowBalExemptionForSystemProcess;
mOriginatingPendingIntent = originatingPendingIntent;
mIntent = intent;
mRealCallingPackage = mService.getPackageNameIfUnique(realCallingUid, realCallingPid);
@@ -549,7 +549,8 @@
ActivityManager.class, "PROCESS_STATE_", mCallingUidProcState));
sb.append("; isCallingUidPersistentSystemProcess: ")
.append(mIsCallingUidPersistentSystemProcess);
- sb.append("; forcedBalByPiSender: ").append(mForcedBalByPiSender);
+ sb.append("; allowBalExemptionForSystemProcess: ")
+ .append(mAllowBalExemptionForSystemProcess);
sb.append("; intent: ").append(mIntent);
sb.append("; callerApp: ").append(mCallerApp);
if (mCallerApp != null) {
@@ -704,8 +705,8 @@
* @param callerApp The process that calls this method (only if not a PendingIntent)
* @param originatingPendingIntent PendingIntentRecord that originated this activity start or
* null if not originated by PendingIntent
- * @param forcedBalByPiSender If set to allow, the
- * PendingIntent's sender will try to force allow background activity starts.
+ * @param allowBalExemptionForSystemProcess If set to true, the
+ * PendingIntent's sender will allow additional exemptions.
* This is only possible if the sender of the PendingIntent is a system process.
* @param resultRecord If not null, this indicates that the caller expects a result.
* @param intent Intent that should be started.
@@ -722,7 +723,7 @@
int realCallingPid,
WindowProcessController callerApp,
PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges forcedBalByPiSender,
+ boolean allowBalExemptionForSystemProcess,
ActivityRecord resultRecord,
Intent intent,
ActivityOptions checkedOptions) {
@@ -734,7 +735,7 @@
BalState state = new BalState(callingUid, callingPid, callingPackage,
realCallingUid, realCallingPid, callerApp, originatingPendingIntent,
- forcedBalByPiSender, resultRecord, intent, checkedOptions);
+ allowBalExemptionForSystemProcess, resultRecord, intent, checkedOptions);
// In the case of an SDK sandbox calling uid, check if the corresponding app uid has a
// visible window.
@@ -1069,7 +1070,7 @@
// if the realCallingUid is a persistent system process, abort if the IntentSender
// wasn't allowed to start an activity
- if (state.mForcedBalByPiSender.allowsBackgroundActivityStarts()
+ if (state.mAllowBalExemptionForSystemProcess
&& state.mIsRealCallingUidPersistentSystemProcess) {
return new BalVerdict(BAL_ALLOW_ALLOWLISTED_UID,
/*background*/ false,
@@ -1385,7 +1386,7 @@
String packageName = mService.mContext.getPackageManager().getNameForUid(callingUid);
BalState state = new BalState(callingUid, callingPid, packageName, INVALID_UID,
- INVALID_PID, null, null, null, null, null, ActivityOptions.makeBasic());
+ INVALID_PID, null, null, false, null, null, ActivityOptions.makeBasic());
@BalCode int balCode = checkBackgroundActivityStartAllowedByCaller(state).mCode;
if (balCode == BAL_ALLOW_ALLOWLISTED_UID
|| balCode == BAL_ALLOW_ALLOWLISTED_COMPONENT
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index c849a37..258a87e 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -156,7 +156,7 @@
SurfaceControl surface, int touchSource, int touchDeviceId, int touchPointerId,
float touchX, float touchY, float thumbCenterX, float thumbCenterY, ClipData data) {
if (DEBUG_DRAG) {
- Slog.d(TAG_WM, "perform drag: win=" + window + " surface=" + surface + " flags=" +
+ Slog.d(TAG_WM, "perform drag: win=" + window + " surface=" + surface + " flags=0x" +
Integer.toHexString(flags) + " data=" + data + " touch(" + touchX + ","
+ touchY + ") thumb center(" + thumbCenterX + "," + thumbCenterY + ")");
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 95cf6bc..6c92ae6 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1117,6 +1117,7 @@
*/
void onDisplayChanged(DisplayContent dc) {
if (mDisplayContent != null && mDisplayContent != dc) {
+ mTransitionController.collect(this);
// Cancel any change transition queued-up for this container on the old display when
// this container is moved from the old display.
mDisplayContent.mClosingChangingContainers.remove(this);
diff --git a/services/core/lint-baseline.xml b/services/core/lint-baseline.xml
index 3b81f0a..4c1ac39 100644
--- a/services/core/lint-baseline.xml
+++ b/services/core/lint-baseline.xml
@@ -178,4 +178,675 @@
column="51"/>
</issue>
-</issues>
\ No newline at end of file
+ <issue
+ id="MissingPermissionAnnotation"
+ message="onShellCommand should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+ errorLine1=" @Override"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java"
+ line="128"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="MissingPermissionAnnotation"
+ message="monitorState should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+ errorLine1=" @Override"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/security/forensic/ForensicService.java"
+ line="95"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="MissingPermissionAnnotation"
+ message="makeVisible should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+ errorLine1=" @Override"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/security/forensic/ForensicService.java"
+ line="100"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="MissingPermissionAnnotation"
+ message="makeInvisible should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+ errorLine1=" @Override"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/security/forensic/ForensicService.java"
+ line="105"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="MissingPermissionAnnotation"
+ message="enable should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+ errorLine1=" @Override"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/security/forensic/ForensicService.java"
+ line="110"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="MissingPermissionAnnotation"
+ message="disable should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+ errorLine1=" @Override"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/security/forensic/ForensicService.java"
+ line="115"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="MissingPermissionAnnotation"
+ message="getTimeoutTime should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+ errorLine1=" @Override"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java"
+ line="430"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="MissingPermissionAnnotation"
+ message="extendTimeRemaining should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+ errorLine1=" @Override"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java"
+ line="443"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="MissingPermissionAnnotation"
+ message="setVerificationPolicy should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+ errorLine1=" @Override"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java"
+ line="456"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="MissingPermissionAnnotation"
+ message="reportVerificationIncomplete should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+ errorLine1=" @Override"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java"
+ line="470"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="MissingPermissionAnnotation"
+ message="reportVerificationComplete should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+ errorLine1=" @Override"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java"
+ line="486"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="MissingPermissionAnnotation"
+ message="reportVerificationCompleteWithExtensionResponse should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+ errorLine1=" @Override"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java"
+ line="492"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityClientController permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mService.mAmInternal.enforceCallingPermission("
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityClientController.java"
+ line="592"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityClientController permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mService.mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityClientController.java"
+ line="1636"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityClientController permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mService.mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityClientController.java"
+ line="1654"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="1820"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="1875"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="1980"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(REMOVE_TASKS, "removeTask()");"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="2116"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(REMOVE_TASKS, "removeAllVisibleRecentTasks()");"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="2144"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(android.Manifest.permission.FORCE_BACK,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="2206"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(android.Manifest.permission.REORDER_TASKS,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="2228"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="2371"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(INTERNAL_SYSTEM_WINDOW, "moveRootTaskToDisplay()");"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="3103"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="3157"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(CONTROL_KEYGUARD, "unlock keyguard");"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="3640"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_TASKS,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="3683"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_TASKS,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="3701"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="3904"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="3978"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, "takeTaskSnapshot()");"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="4000"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission("
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="4029"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="4057"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="4074"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, "stopAppSwitches");"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="4136"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, "resumeAppSwitches");"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="4147"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="4198"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="4215"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="4280"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(DETECT_SCREEN_CAPTURE,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="5836"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(DETECT_SCREEN_CAPTURE,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="5849"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IColorDisplayManager permission check should be converted to @EnforcePermission annotation"
+ errorLine1=" getContext().enforceCallingOrSelfPermission("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/display/color/ColorDisplayService.java"
+ line="2152"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IMediaProjectionManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" if (mContext.checkCallingPermission(MANAGE_MEDIA_PROJECTION)"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java"
+ line="779"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IMediaProjectionManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" if (mContext.checkCallingPermission(MANAGE_MEDIA_PROJECTION)"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java"
+ line="820"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IMediaProjectionManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" if (mContext.checkCallingPermission(MANAGE_MEDIA_PROJECTION)"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java"
+ line="906"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="INotificationManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" if (PERMISSION_GRANTED"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java"
+ line="6691"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="INotificationManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" if (PERMISSION_GRANTED"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java"
+ line="6712"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IOnDeviceIntelligenceManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mContext.enforceCallingPermission("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java"
+ line="237"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IPackageInstaller permission check should be converted to @EnforcePermission annotation"
+ errorLine1=" if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/pm/PackageInstallerService.java"
+ line="1881"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IPackageInstaller permission check should be converted to @EnforcePermission annotation"
+ errorLine1=" if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/pm/PackageInstallerService.java"
+ line="1892"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IPackageManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mContext.enforceCallingPermission(Manifest.permission.SEND_DEVICE_CUSTOMIZATION_READY,"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java"
+ line="5798"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IPackageManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mContext.enforceCallingPermission("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java"
+ line="6266"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="ITvInputManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" if (mContext.checkCallingPermission("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java"
+ line="1406"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="ITvInputManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" if (mContext.checkCallingPermission(android.Manifest.permission.NOTIFY_TV_INPUTS)"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java"
+ line="1427"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="ITvInputManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" if (mContext.checkCallingPermission("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java"
+ line="1734"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="ITvInputManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE)"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java"
+ line="2509"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="ITvInputManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE)"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java"
+ line="2564"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="ITvInputManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" if (mContext.checkCallingPermission(android.Manifest.permission.ACCESS_TUNED_INFO)"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java"
+ line="2932"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IUriGrantsManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/uri/UriGrantsManagerService.java"
+ line="366"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IUriGrantsManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/uri/UriGrantsManagerService.java"
+ line="444"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IVcnManagementService permission check should be converted to @EnforcePermission annotation"
+ errorLine1=" mContext.enforceCallingOrSelfPermission(DUMP, TAG);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/VcnManagementService.java"
+ line="1329"
+ column="9"/>
+ </issue>
+
+</issues>
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 105147f..42c171b 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -26,6 +26,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.camera2.CameraManager;
+import android.hardware.usb.UsbManager;
import android.os.Handler;
import android.os.IBinder.DeathRecipient;
import android.os.Looper;
@@ -67,6 +68,8 @@
private int mUsageSetting;
private boolean mUploadEnabled;
+ private boolean mAdbActive;
+
private IProfCollectd mIProfcollect;
private static ProfcollectForwardingService sSelfService;
private final Handler mHandler = new ProfcollectdHandler(IoThread.getHandler().getLooper());
@@ -84,6 +87,14 @@
Log.d(LOG_TAG, "Received broadcast to pack and upload reports");
createAndUploadReport(sSelfService);
}
+ if (UsbManager.ACTION_USB_STATE.equals(intent.getAction())) {
+ boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
+ boolean isADB = intent.getBooleanExtra(UsbManager.USB_FUNCTION_ADB, false);
+ if (isADB) {
+ Log.d(LOG_TAG, "Received broadcast that ADB became " + connected);
+ mAdbActive = connected;
+ }
+ }
}
};
@@ -106,8 +117,12 @@
mUploadEnabled =
context.getResources().getBoolean(R.bool.config_profcollectReportUploaderEnabled);
+ // TODO: ADB might already be active when our service started.
+ mAdbActive = false;
+
final IntentFilter filter = new IntentFilter();
filter.addAction(INTENT_UPLOAD_PROFILES);
+ filter.addAction(UsbManager.ACTION_USB_STATE);
context.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
}
@@ -281,6 +296,9 @@
if (mIProfcollect == null) {
return;
}
+ if (mAdbActive) {
+ return;
+ }
if (Utils.withFrequency("applaunch_trace_freq", 5)) {
Utils.traceSystem(mIProfcollect, "applaunch");
}
@@ -303,6 +321,9 @@
if (mIProfcollect == null) {
return;
}
+ if (mAdbActive) {
+ return;
+ }
if (Utils.withFrequency("dex2oat_trace_freq", 25)) {
// Dex2oat could take a while before it starts. Add a short delay before start tracing.
Utils.traceSystem(mIProfcollect, "dex2oat", /* delayMs */ 1000);
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java
index 2461798..3046d4b 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java
@@ -24,6 +24,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -49,6 +50,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.infra.ServiceConnector;
@@ -122,6 +124,10 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ // Mock that the UID of this test becomes the UID of the verifier
+ when(mSnapshot.getPackageUidInternal(anyString(), anyLong(), anyInt(), anyInt()))
+ .thenReturn(InstrumentationRegistry.getInstrumentation().getContext()
+ .getApplicationInfo().uid);
when(mInjector.getVerifierPackageName(any(Computer.class), anyInt())).thenReturn(
TEST_VERIFIER_COMPONENT_NAME.getPackageName());
when(mInjector.getRemoteService(
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
index 17af633..85e7356 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
@@ -21,7 +21,7 @@
import android.view.DisplayInfo
import org.junit.Before
import org.junit.Test
-import org.mockito.ArgumentMatchers.anyDouble
+import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
@@ -55,9 +55,9 @@
coordinator.onDisplayAdded(displayInfo)
- val widthDp = displayInfo.logicalWidth * (DisplayMetrics.DENSITY_DEFAULT.toDouble()
+ val widthDp = displayInfo.logicalWidth * (DisplayMetrics.DENSITY_DEFAULT.toFloat()
/ displayInfo.logicalDensityDpi)
- val heightDp = displayInfo.logicalHeight * (DisplayMetrics.DENSITY_DEFAULT.toDouble()
+ val heightDp = displayInfo.logicalHeight * (DisplayMetrics.DENSITY_DEFAULT.toFloat()
/ displayInfo.logicalDensityDpi)
verify(mockTopology).addDisplay(displayInfo.displayId, widthDp, heightDp)
}
@@ -68,7 +68,7 @@
coordinator.onDisplayAdded(displayInfo)
- verify(mockTopology, never()).addDisplay(anyInt(), anyDouble(), anyDouble())
+ verify(mockTopology, never()).addDisplay(anyInt(), anyFloat(), anyFloat())
}
@Test
@@ -78,6 +78,6 @@
coordinator.onDisplayAdded(displayInfo)
- verify(mockTopology, never()).addDisplay(anyInt(), anyDouble(), anyDouble())
+ verify(mockTopology, never()).addDisplay(anyInt(), anyFloat(), anyFloat())
}
}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyTest.kt
index f3a8d841..cd8c26d 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyTest.kt
@@ -17,6 +17,9 @@
package com.android.server.display
import android.view.Display
+import com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_BOTTOM
+import com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_TOP
+import com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_RIGHT
import com.google.common.truth.Truth.assertThat
import org.junit.Test
@@ -26,8 +29,8 @@
@Test
fun addOneDisplay() {
val displayId = 1
- val width = 800.0
- val height = 600.0
+ val width = 800f
+ val height = 600f
topology.addDisplay(displayId, width, height)
@@ -43,12 +46,12 @@
@Test
fun addTwoDisplays() {
val displayId1 = 1
- val width1 = 800.0
- val height1 = 600.0
+ val width1 = 800f
+ val height1 = 600f
val displayId2 = 2
- val width2 = 1000.0
- val height2 = 1500.0
+ val width2 = 1000f
+ val height2 = 1500f
topology.addDisplay(displayId1, width1, height1)
topology.addDisplay(displayId2, width2, height2)
@@ -66,20 +69,19 @@
assertThat(display2.mWidth).isEqualTo(width2)
assertThat(display2.mHeight).isEqualTo(height2)
assertThat(display2.mChildren).isEmpty()
- assertThat(display2.mPosition).isEqualTo(
- DisplayTopology.TreeNode.Position.POSITION_TOP)
+ assertThat(display2.mPosition).isEqualTo(POSITION_TOP)
assertThat(display2.mOffset).isEqualTo(width1 / 2 - width2 / 2)
}
@Test
fun addManyDisplays() {
val displayId1 = 1
- val width1 = 800.0
- val height1 = 600.0
+ val width1 = 800f
+ val height1 = 600f
val displayId2 = 2
- val width2 = 1000.0
- val height2 = 1500.0
+ val width2 = 1000f
+ val height2 = 1500f
topology.addDisplay(displayId1, width1, height1)
topology.addDisplay(displayId2, width2, height2)
@@ -102,8 +104,7 @@
assertThat(display2.mWidth).isEqualTo(width2)
assertThat(display2.mHeight).isEqualTo(height2)
assertThat(display2.mChildren).hasSize(1)
- assertThat(display2.mPosition).isEqualTo(
- DisplayTopology.TreeNode.Position.POSITION_TOP)
+ assertThat(display2.mPosition).isEqualTo(POSITION_TOP)
assertThat(display2.mOffset).isEqualTo(width1 / 2 - width2 / 2)
var display = display2
@@ -114,8 +115,7 @@
assertThat(display.mHeight).isEqualTo(height1)
// The last display should have no children
assertThat(display.mChildren).hasSize(if (i < noOfDisplays) 1 else 0)
- assertThat(display.mPosition).isEqualTo(
- DisplayTopology.TreeNode.Position.POSITION_RIGHT)
+ assertThat(display.mPosition).isEqualTo(POSITION_RIGHT)
assertThat(display.mOffset).isEqualTo(0)
}
}
@@ -123,12 +123,12 @@
@Test
fun removeDisplays() {
val displayId1 = 1
- val width1 = 800.0
- val height1 = 600.0
+ val width1 = 800f
+ val height1 = 600f
val displayId2 = 2
- val width2 = 1000.0
- val height2 = 1500.0
+ val width2 = 1000f
+ val height2 = 1500f
topology.addDisplay(displayId1, width1, height1)
topology.addDisplay(displayId2, width2, height2)
@@ -154,8 +154,7 @@
assertThat(display2.mWidth).isEqualTo(width2)
assertThat(display2.mHeight).isEqualTo(height2)
assertThat(display2.mChildren).hasSize(1)
- assertThat(display2.mPosition).isEqualTo(
- DisplayTopology.TreeNode.Position.POSITION_TOP)
+ assertThat(display2.mPosition).isEqualTo(POSITION_TOP)
assertThat(display2.mOffset).isEqualTo(width1 / 2 - width2 / 2)
var display = display2
@@ -169,8 +168,7 @@
assertThat(display.mHeight).isEqualTo(height1)
// The last display should have no children
assertThat(display.mChildren).hasSize(if (i < noOfDisplays) 1 else 0)
- assertThat(display.mPosition).isEqualTo(
- DisplayTopology.TreeNode.Position.POSITION_RIGHT)
+ assertThat(display.mPosition).isEqualTo(POSITION_RIGHT)
assertThat(display.mOffset).isEqualTo(0)
}
@@ -194,8 +192,7 @@
assertThat(display2.mWidth).isEqualTo(width2)
assertThat(display2.mHeight).isEqualTo(height2)
assertThat(display2.mChildren).hasSize(1)
- assertThat(display2.mPosition).isEqualTo(
- DisplayTopology.TreeNode.Position.POSITION_TOP)
+ assertThat(display2.mPosition).isEqualTo(POSITION_TOP)
assertThat(display2.mOffset).isEqualTo(width1 / 2 - width2 / 2)
display = display2
@@ -209,8 +206,7 @@
assertThat(display.mHeight).isEqualTo(height1)
// The last display should have no children
assertThat(display.mChildren).hasSize(if (i < noOfDisplays) 1 else 0)
- assertThat(display.mPosition).isEqualTo(
- DisplayTopology.TreeNode.Position.POSITION_RIGHT)
+ assertThat(display.mPosition).isEqualTo(POSITION_RIGHT)
assertThat(display.mOffset).isEqualTo(0)
}
}
@@ -218,8 +214,8 @@
@Test
fun removeAllDisplays() {
val displayId = 1
- val width = 800.0
- val height = 600.0
+ val width = 800f
+ val height = 600f
topology.addDisplay(displayId, width, height)
topology.removeDisplay(displayId)
@@ -231,8 +227,8 @@
@Test
fun removeDisplayThatDoesNotExist() {
val displayId = 1
- val width = 800.0
- val height = 600.0
+ val width = 800f
+ val height = 600f
topology.addDisplay(displayId, width, height)
topology.removeDisplay(3)
@@ -245,4 +241,236 @@
assertThat(display.mHeight).isEqualTo(height)
assertThat(display.mChildren).isEmpty()
}
+
+ @Test
+ fun removePrimaryDisplay() {
+ val displayId1 = 1
+ val displayId2 = 2
+ val width = 800f
+ val height = 600f
+
+ topology.addDisplay(displayId1, width, height)
+ topology.addDisplay(displayId2, width, height)
+ topology.mPrimaryDisplayId = displayId2
+ topology.removeDisplay(displayId2)
+
+ assertThat(topology.mPrimaryDisplayId).isEqualTo(displayId1)
+ val display = topology.mRoot!!
+ assertThat(display.mDisplayId).isEqualTo(displayId1)
+ assertThat(display.mWidth).isEqualTo(width)
+ assertThat(display.mHeight).isEqualTo(height)
+ assertThat(display.mChildren).isEmpty()
+ }
+
+ @Test
+ fun normalization_noOverlaps_leavesTopologyUnchanged() {
+ val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
+ /* height= */ 600f, /* position= */ null, /* offset= */ 0f)
+ topology.mRoot = display1
+
+ val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 600f,
+ /* height= */ 200f, POSITION_RIGHT, /* offset= */ 0f)
+ display1.mChildren.add(display2)
+
+ val primaryDisplayId = 3
+ val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f,
+ /* height= */ 200f, POSITION_RIGHT, /* offset= */ 400f)
+ display1.mChildren.add(display3)
+ topology.mPrimaryDisplayId = primaryDisplayId
+
+ val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f,
+ /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
+ display2.mChildren.add(display4)
+
+ topology.normalize()
+
+ assertThat(topology.mPrimaryDisplayId).isEqualTo(primaryDisplayId)
+
+ val actualDisplay1 = topology.mRoot!!
+ assertThat(actualDisplay1.mDisplayId).isEqualTo(1)
+ assertThat(actualDisplay1.mWidth).isEqualTo(200f)
+ assertThat(actualDisplay1.mHeight).isEqualTo(600f)
+ assertThat(actualDisplay1.mChildren).hasSize(2)
+
+ val actualDisplay2 = actualDisplay1.mChildren[0]
+ assertThat(actualDisplay2.mDisplayId).isEqualTo(2)
+ assertThat(actualDisplay2.mWidth).isEqualTo(600f)
+ assertThat(actualDisplay2.mHeight).isEqualTo(200f)
+ assertThat(actualDisplay2.mPosition).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay2.mOffset).isEqualTo(0f)
+ assertThat(actualDisplay2.mChildren).hasSize(1)
+
+ val actualDisplay3 = actualDisplay1.mChildren[1]
+ assertThat(actualDisplay3.mDisplayId).isEqualTo(3)
+ assertThat(actualDisplay3.mWidth).isEqualTo(600f)
+ assertThat(actualDisplay3.mHeight).isEqualTo(200f)
+ assertThat(actualDisplay3.mPosition).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay3.mOffset).isEqualTo(400f)
+ assertThat(actualDisplay3.mChildren).isEmpty()
+
+ val actualDisplay4 = actualDisplay2.mChildren[0]
+ assertThat(actualDisplay4.mDisplayId).isEqualTo(4)
+ assertThat(actualDisplay4.mWidth).isEqualTo(200f)
+ assertThat(actualDisplay4.mHeight).isEqualTo(600f)
+ assertThat(actualDisplay4.mPosition).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay4.mOffset).isEqualTo(0f)
+ assertThat(actualDisplay4.mChildren).isEmpty()
+ }
+
+ @Test
+ fun normalization_moveDisplayWithoutReparenting() {
+ val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
+ /* height= */ 600f, /* position= */ null, /* offset= */ 0f)
+ topology.mRoot = display1
+
+ val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 200f,
+ /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
+ display1.mChildren.add(display2)
+
+ val primaryDisplayId = 3
+ val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f,
+ /* height= */ 200f, POSITION_RIGHT, /* offset= */ 10f)
+ display1.mChildren.add(display3)
+ topology.mPrimaryDisplayId = primaryDisplayId
+
+ val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f,
+ /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
+ display2.mChildren.add(display4)
+
+ // Display 3 becomes a child of display 2. Display 4 gets moved without changing its parent.
+ topology.normalize()
+
+ assertThat(topology.mPrimaryDisplayId).isEqualTo(primaryDisplayId)
+
+ val actualDisplay1 = topology.mRoot!!
+ assertThat(actualDisplay1.mDisplayId).isEqualTo(1)
+ assertThat(actualDisplay1.mWidth).isEqualTo(200f)
+ assertThat(actualDisplay1.mHeight).isEqualTo(600f)
+ assertThat(actualDisplay1.mChildren).hasSize(1)
+
+ val actualDisplay2 = actualDisplay1.mChildren[0]
+ assertThat(actualDisplay2.mDisplayId).isEqualTo(2)
+ assertThat(actualDisplay2.mWidth).isEqualTo(200f)
+ assertThat(actualDisplay2.mHeight).isEqualTo(600f)
+ assertThat(actualDisplay2.mPosition).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay2.mOffset).isEqualTo(0f)
+ assertThat(actualDisplay2.mChildren).hasSize(2)
+
+ val actualDisplay3 = actualDisplay2.mChildren[1]
+ assertThat(actualDisplay3.mDisplayId).isEqualTo(3)
+ assertThat(actualDisplay3.mWidth).isEqualTo(600f)
+ assertThat(actualDisplay3.mHeight).isEqualTo(200f)
+ assertThat(actualDisplay3.mPosition).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay3.mOffset).isEqualTo(10f)
+ assertThat(actualDisplay3.mChildren).isEmpty()
+
+ val actualDisplay4 = actualDisplay2.mChildren[0]
+ assertThat(actualDisplay4.mDisplayId).isEqualTo(4)
+ assertThat(actualDisplay4.mWidth).isEqualTo(200f)
+ assertThat(actualDisplay4.mHeight).isEqualTo(600f)
+ assertThat(actualDisplay4.mPosition).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay4.mOffset).isEqualTo(210f)
+ assertThat(actualDisplay4.mChildren).isEmpty()
+ }
+
+ @Test
+ fun normalization_moveDisplayWithoutReparenting_offsetOutOfBounds() {
+ val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
+ /* height= */ 50f, /* position= */ null, /* offset= */ 0f)
+ topology.mRoot = display1
+
+ val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 600f,
+ /* height= */ 200f, POSITION_RIGHT, /* offset= */ 0f)
+ display1.mChildren.add(display2)
+
+ val primaryDisplayId = 3
+ val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f,
+ /* height= */ 200f, POSITION_RIGHT, /* offset= */ 10f)
+ display1.mChildren.add(display3)
+ topology.mPrimaryDisplayId = primaryDisplayId
+
+ // Display 3 gets moved and its left side is still on the same line as the right side
+ // of Display 1, but it no longer touches it (the offset is out of bounds), so Display 2
+ // becomes its new parent.
+ topology.normalize()
+
+ assertThat(topology.mPrimaryDisplayId).isEqualTo(primaryDisplayId)
+
+ val actualDisplay1 = topology.mRoot!!
+ assertThat(actualDisplay1.mDisplayId).isEqualTo(1)
+ assertThat(actualDisplay1.mWidth).isEqualTo(200f)
+ assertThat(actualDisplay1.mHeight).isEqualTo(50f)
+ assertThat(actualDisplay1.mChildren).hasSize(1)
+
+ val actualDisplay2 = actualDisplay1.mChildren[0]
+ assertThat(actualDisplay2.mDisplayId).isEqualTo(2)
+ assertThat(actualDisplay2.mWidth).isEqualTo(600f)
+ assertThat(actualDisplay2.mHeight).isEqualTo(200f)
+ assertThat(actualDisplay2.mPosition).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay2.mOffset).isEqualTo(0f)
+ assertThat(actualDisplay2.mChildren).hasSize(1)
+
+ val actualDisplay3 = actualDisplay2.mChildren[0]
+ assertThat(actualDisplay3.mDisplayId).isEqualTo(3)
+ assertThat(actualDisplay3.mWidth).isEqualTo(600f)
+ assertThat(actualDisplay3.mHeight).isEqualTo(200f)
+ assertThat(actualDisplay3.mPosition).isEqualTo(POSITION_BOTTOM)
+ assertThat(actualDisplay3.mOffset).isEqualTo(0f)
+ assertThat(actualDisplay3.mChildren).isEmpty()
+ }
+
+ @Test
+ fun normalization_moveAndReparentDisplay() {
+ val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
+ /* height= */ 600f, /* position= */ null, /* offset= */ 0f)
+ topology.mRoot = display1
+
+ val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 200f,
+ /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
+ display1.mChildren.add(display2)
+
+ val primaryDisplayId = 3
+ val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f,
+ /* height= */ 200f, POSITION_RIGHT, /* offset= */ 400f)
+ display1.mChildren.add(display3)
+ topology.mPrimaryDisplayId = primaryDisplayId
+
+ val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f,
+ /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
+ display2.mChildren.add(display4)
+
+ topology.normalize()
+
+ assertThat(topology.mPrimaryDisplayId).isEqualTo(primaryDisplayId)
+
+ val actualDisplay1 = topology.mRoot!!
+ assertThat(actualDisplay1.mDisplayId).isEqualTo(1)
+ assertThat(actualDisplay1.mWidth).isEqualTo(200f)
+ assertThat(actualDisplay1.mHeight).isEqualTo(600f)
+ assertThat(actualDisplay1.mChildren).hasSize(1)
+
+ val actualDisplay2 = actualDisplay1.mChildren[0]
+ assertThat(actualDisplay2.mDisplayId).isEqualTo(2)
+ assertThat(actualDisplay2.mWidth).isEqualTo(200f)
+ assertThat(actualDisplay2.mHeight).isEqualTo(600f)
+ assertThat(actualDisplay2.mPosition).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay2.mOffset).isEqualTo(0f)
+ assertThat(actualDisplay2.mChildren).hasSize(1)
+
+ val actualDisplay3 = actualDisplay2.mChildren[0]
+ assertThat(actualDisplay3.mDisplayId).isEqualTo(3)
+ assertThat(actualDisplay3.mWidth).isEqualTo(600f)
+ assertThat(actualDisplay3.mHeight).isEqualTo(200f)
+ assertThat(actualDisplay3.mPosition).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay3.mOffset).isEqualTo(400f)
+ assertThat(actualDisplay3.mChildren).hasSize(1)
+
+ val actualDisplay4 = actualDisplay3.mChildren[0]
+ assertThat(actualDisplay4.mDisplayId).isEqualTo(4)
+ assertThat(actualDisplay4.mWidth).isEqualTo(200f)
+ assertThat(actualDisplay4.mHeight).isEqualTo(600f)
+ assertThat(actualDisplay4.mPosition).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay4.mOffset).isEqualTo(-400f)
+ assertThat(actualDisplay4.mChildren).isEmpty()
+ }
}
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 6ab72cd..b005358 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -228,6 +228,8 @@
mock(BatteryStatsService.class));
setFieldValue(ActivityManagerService.class, mService, "mInjector",
new ActivityManagerService.Injector(mContext));
+ setFieldValue(ActivityManagerService.class, mService, "mPhantomProcessList",
+ new PhantomProcessList(mService));
doReturn(mock(AppOpsManager.class)).when(mService).getAppOpsManager();
doCallRealMethod().when(mService).enqueueOomAdjTargetLocked(any(ProcessRecord.class));
doCallRealMethod().when(mService).updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_ACTIVITY);
diff --git a/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java b/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
index 2b55303..feb00e7 100644
--- a/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
+++ b/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
@@ -19,23 +19,35 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Looper;
import android.os.RemoteException;
import android.os.test.TestLooper;
+import android.security.forensic.ForensicEvent;
import android.security.forensic.IForensicServiceCommandCallback;
import android.security.forensic.IForensicServiceStateCallback;
+import android.util.ArrayMap;
+
+import com.android.server.ServiceThread;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
public class ForensicServiceTest {
private static final int STATE_UNKNOWN = IForensicServiceStateCallback.State.UNKNOWN;
private static final int STATE_INVISIBLE = IForensicServiceStateCallback.State.INVISIBLE;
@@ -55,10 +67,12 @@
@Mock
private Context mContext;
private BackupTransportConnection mBackupTransportConnection;
-
+ private DataAggregator mDataAggregator;
private ForensicService mForensicService;
private TestLooper mTestLooper;
private Looper mLooper;
+ private TestLooper mTestLooperOfDataAggregator;
+ private Looper mLooperOfDataAggregator;
@SuppressLint("VisibleForTests")
@Before
@@ -67,6 +81,8 @@
mTestLooper = new TestLooper();
mLooper = mTestLooper.getLooper();
+ mTestLooperOfDataAggregator = new TestLooper();
+ mLooperOfDataAggregator = mTestLooperOfDataAggregator.getLooper();
mForensicService = new ForensicService(new MockInjector(mContext));
mForensicService.onStart();
}
@@ -121,6 +137,8 @@
assertEquals(STATE_INVISIBLE, scb1.mState);
assertEquals(STATE_INVISIBLE, scb2.mState);
+ doReturn(true).when(mDataAggregator).initialize();
+
CommandCallback ccb = new CommandCallback();
mForensicService.getBinderService().makeVisible(ccb);
mTestLooper.dispatchAll();
@@ -130,6 +148,29 @@
}
@Test
+ public void testMakeVisible_FromInvisible_TwoMonitors_DataSourceUnavailable()
+ throws RemoteException {
+ mForensicService.setState(STATE_INVISIBLE);
+ StateCallback scb1 = new StateCallback();
+ StateCallback scb2 = new StateCallback();
+ mForensicService.getBinderService().monitorState(scb1);
+ mForensicService.getBinderService().monitorState(scb2);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_INVISIBLE, scb1.mState);
+ assertEquals(STATE_INVISIBLE, scb2.mState);
+
+ doReturn(false).when(mDataAggregator).initialize();
+
+ CommandCallback ccb = new CommandCallback();
+ mForensicService.getBinderService().makeVisible(ccb);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_INVISIBLE, scb1.mState);
+ assertEquals(STATE_INVISIBLE, scb2.mState);
+ assertNotNull(ccb.mErrorCode);
+ assertEquals(ERROR_DATA_SOURCE_UNAVAILABLE, ccb.mErrorCode.intValue());
+ }
+
+ @Test
public void testMakeVisible_FromVisible_TwoMonitors() throws RemoteException {
mForensicService.setState(STATE_VISIBLE);
StateCallback scb1 = new StateCallback();
@@ -262,6 +303,8 @@
CommandCallback ccb = new CommandCallback();
mForensicService.getBinderService().enable(ccb);
mTestLooper.dispatchAll();
+
+ verify(mDataAggregator, times(1)).enable();
assertEquals(STATE_ENABLED, scb1.mState);
assertEquals(STATE_ENABLED, scb2.mState);
assertNull(ccb.mErrorCode);
@@ -361,14 +404,67 @@
doNothing().when(mBackupTransportConnection).release();
+ ServiceThread mockThread = spy(ServiceThread.class);
+ mDataAggregator.setHandler(mLooperOfDataAggregator, mockThread);
+
CommandCallback ccb = new CommandCallback();
mForensicService.getBinderService().disable(ccb);
mTestLooper.dispatchAll();
+ mTestLooperOfDataAggregator.dispatchAll();
+ // TODO: We can verify the data sources once we implement them.
+ verify(mockThread, times(1)).quitSafely();
assertEquals(STATE_VISIBLE, scb1.mState);
assertEquals(STATE_VISIBLE, scb2.mState);
assertNull(ccb.mErrorCode);
}
+ @Test
+ public void testDataAggregator_AddBatchData() {
+ mForensicService.setState(STATE_ENABLED);
+ ServiceThread mockThread = spy(ServiceThread.class);
+ mDataAggregator.setHandler(mLooperOfDataAggregator, mockThread);
+
+ String eventOneType = "event_one_type";
+ String eventOneMapKey = "event_one_map_key";
+ String eventOneMapVal = "event_one_map_val";
+ Map<String, String> eventOneMap = new ArrayMap<String, String>();
+ eventOneMap.put(eventOneMapKey, eventOneMapVal);
+ ForensicEvent eventOne = new ForensicEvent(eventOneType, eventOneMap);
+
+ String eventTwoType = "event_two_type";
+ String eventTwoMapKey = "event_two_map_key";
+ String eventTwoMapVal = "event_two_map_val";
+ Map<String, String> eventTwoMap = new ArrayMap<String, String>();
+ eventTwoMap.put(eventTwoMapKey, eventTwoMapVal);
+ ForensicEvent eventTwo = new ForensicEvent(eventTwoType, eventTwoMap);
+
+ List<ForensicEvent> events = new ArrayList<>();
+ events.add(eventOne);
+ events.add(eventTwo);
+
+ doReturn(true).when(mBackupTransportConnection).addData(any());
+
+ mDataAggregator.addBatchData(events);
+ mTestLooperOfDataAggregator.dispatchAll();
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<List<ForensicEvent>> captor = ArgumentCaptor.forClass(List.class);
+ verify(mBackupTransportConnection).addData(captor.capture());
+ List<ForensicEvent> receivedEvents = captor.getValue();
+ assertEquals(receivedEvents.size(), 2);
+
+ assertEquals(receivedEvents.getFirst().getType(), eventOneType);
+ assertEquals(receivedEvents.getFirst().getKeyValuePairs().size(), 1);
+ assertEquals(receivedEvents.getFirst().getKeyValuePairs().get(eventOneMapKey),
+ eventOneMapVal);
+
+ assertEquals(receivedEvents.getLast().getType(), eventTwoType);
+ assertEquals(receivedEvents.getLast().getKeyValuePairs().size(), 1);
+ assertEquals(receivedEvents.getLast().getKeyValuePairs().get(eventTwoMapKey),
+ eventTwoMapVal);
+
+ }
+
private class MockInjector implements ForensicService.Injector {
private final Context mContext;
@@ -393,6 +489,11 @@
return mBackupTransportConnection;
}
+ @Override
+ public DataAggregator getDataAggregator(ForensicService forensicService) {
+ mDataAggregator = spy(new DataAggregator(forensicService));
+ return mDataAggregator;
+ }
}
private static class StateCallback extends IForensicServiceStateCallback.Stub {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index a0005d9..3360e1d 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -413,7 +413,8 @@
.isEqualTo(Constants.HANDLED);
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
// After 30s of device inactivity, device would go to sleep.
- skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, false,
+ false);
assertThat(mPowerManager.isInteractive()).isFalse();
}
@@ -422,9 +423,8 @@
int newPlaybackPhysicalAddress = 0x2100;
int switchPhysicalAddress = 0x2000;
mNativeWrapper.setPhysicalAddress(newPlaybackPhysicalAddress);
- mHdmiControlService.onHotplug(newPlaybackPhysicalAddress, true);
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
-
+ mHdmiControlService.onHotplug(newPlaybackPhysicalAddress, true);
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
@@ -457,7 +457,8 @@
.isEqualTo(Constants.HANDLED);
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
// After 30s of device inactivity, device would go to sleep.
- skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, false,
+ false);
assertThat(mPowerManager.isInteractive()).isFalse();
}
@@ -617,7 +618,8 @@
.isEqualTo(Constants.HANDLED);
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
// After 30s of device inactivity, device would go to sleep.
- skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, false,
+ false);
assertThat(mPowerManager.isInteractive()).isFalse();
}
@@ -722,7 +724,8 @@
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(
ADDR_INVALID);
// After 30s of device inactivity, device would go to sleep.
- skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS,true,
+ true);
assertThat(mPowerManager.isInteractive()).isFalse();
}
@@ -1265,14 +1268,35 @@
assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message))
.isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
-
- // After 30s of device inactivity, device would go to sleep.
- skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ // After 30s of device inactivity, device would assert active source.
+ skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, true,
+ true);
assertThat(mPowerManager.isInteractive()).isFalse();
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
}
@Test
+ public void handleActiveSourceFromTv_tvNotAnswerRequest_assertActiveSource() {
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+ mPowerManager.setInteractive(true);
+ HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress);
+ assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message))
+ .isEqualTo(Constants.HANDLED);
+ message = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+ assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message))
+ .isEqualTo(Constants.HANDLED);
+ mTestLooper.dispatchAll();
+ // After 30s of device inactivity, device would assert active source.
+ skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, true,
+ false);
+ assertThat(mPowerManager.isInteractive()).isTrue();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue();
+ }
+
+ @Test
public void handleActiveSource_otherDevice_ActiveSource_mediaSessionsPaused() {
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
@@ -1343,7 +1367,8 @@
.isEqualTo(Constants.HANDLED);
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
// After 30s of device inactivity, device would go to sleep.
- skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, true,
+ true);
assertThat(mPowerManager.isInteractive()).isFalse();
mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
// 3. DUT becomes <AS> again.
@@ -1704,9 +1729,9 @@
assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(message))
.isEqualTo(Constants.HANDLED);
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
-
// After 30s of device inactivity, device would go to sleep.
- skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, false,
+ false);
assertThat(mPowerManager.isInteractive()).isFalse();
}
@@ -2323,11 +2348,197 @@
mTestLooper.dispatchAll();
// After 30s of device inactivity, device would go to sleep.
- skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, true,
+ true);
assertThat(mPowerManager.isInteractive()).isFalse();
}
@Test
+ public void onActiveSourceLostToTv_requestActiveSourceAnsweredFromTv_showPopup() {
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+
+ mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+ mPowerManager.setInteractive(true);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage activeSourceFromTv =
+ HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+ HdmiCecMessage requestActiveSource =
+ HdmiCecMessageBuilder.buildRequestActiveSource(mPlaybackLogicalAddress);
+
+ assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromTv))
+ .isEqualTo(Constants.HANDLED);
+ mTestLooper.dispatchAll();
+
+ mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ mTestLooper.dispatchAll();
+
+ // RequestActiveSourceAction is triggered.
+ assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+ mNativeWrapper.onCecMessage(activeSourceFromTv);
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(ADDR_TV);
+ assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
+ }
+
+ @Test
+ public void onActiveSourceLostToTv_requestActiveSourceNotAnswered_assertActiveSource() {
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+
+ mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+ mPowerManager.setInteractive(true);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage activeSourceFromTv =
+ HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+ HdmiCecMessage requestActiveSource =
+ HdmiCecMessageBuilder.buildRequestActiveSource(mPlaybackLogicalAddress);
+ HdmiCecMessage activeSourceFromPlayback =
+ HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress);
+ assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromTv))
+ .isEqualTo(Constants.HANDLED);
+ assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(ADDR_TV);
+ mTestLooper.dispatchAll();
+
+ mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ mTestLooper.dispatchAll();
+
+ // RequestActiveSourceAction is triggered.
+ assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ // Pop-up is not shown, playback device asserts active source since TV doesn't answer the
+ // request.
+ assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+ assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
+ assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
+ .isEqualTo(mPlaybackLogicalAddress);
+ assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().physicalAddress)
+ .isEqualTo(mPlaybackPhysicalAddress);
+ }
+
+ @Test
+ public void onActiveSourceLost_requestActiveSourceNotAnswered_playbackIsAS_dontShowPopup() {
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+
+ mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+ mPowerManager.setInteractive(true);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage activeSourceFromTv =
+ HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+ HdmiCecMessage requestActiveSource =
+ HdmiCecMessageBuilder.buildRequestActiveSource(mPlaybackLogicalAddress);
+ HdmiCecMessage setStreamPathToPlayback = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
+ mPlaybackPhysicalAddress);
+ HdmiCecMessage activeSourceFromPlayback =
+ HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress);
+ assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromTv))
+ .isEqualTo(Constants.HANDLED);
+ assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(ADDR_TV);
+ mTestLooper.dispatchAll();
+
+ mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ mTestLooper.dispatchAll();
+
+ // RequestActiveSourceAction is triggered.
+ assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(setStreamPathToPlayback))
+ .isEqualTo(Constants.HANDLED);
+ mTestLooper.dispatchAll();
+
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ // Pop-up is not shown since playback device is active source.
+ assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+ assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
+ assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
+ .isEqualTo(mPlaybackLogicalAddress);
+ assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().physicalAddress)
+ .isEqualTo(mPlaybackPhysicalAddress);
+ }
+
+ @Test
+ public void onActiveSourceLost_requestASNotAnswered_setStreamPathToNonCecInput_dontShowPopup() {
+ int otherPhysicalAddress = mPlaybackPhysicalAddress + 0x0100;
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+
+ mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+ mPowerManager.setInteractive(true);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage activeSourceFromTv =
+ HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+ HdmiCecMessage requestActiveSource =
+ HdmiCecMessageBuilder.buildRequestActiveSource(mPlaybackLogicalAddress);
+ HdmiCecMessage setStreamPathToOtherInput = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
+ otherPhysicalAddress);
+ HdmiCecMessage activeSourceFromPlayback =
+ HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress);
+ assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromTv))
+ .isEqualTo(Constants.HANDLED);
+ assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(ADDR_TV);
+ mTestLooper.dispatchAll();
+
+ mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ mTestLooper.dispatchAll();
+
+ // RequestActiveSourceAction is triggered.
+ assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(setStreamPathToOtherInput))
+ .isEqualTo(Constants.HANDLED);
+ mTestLooper.dispatchAll();
+
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ // Pop-up is shown, playback device doesn't assert active source since active path is
+ // switched to a non-CEC device.
+ assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
+ assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
+ .isEqualTo(ADDR_INVALID);
+ assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().physicalAddress)
+ .isEqualTo(otherPhysicalAddress);
+ }
+
+ @Test
public void onActiveSourceLost_interactionWithDut_noStandbyAfterTimeout() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
@@ -2350,7 +2561,7 @@
mTestLooper.dispatchAll();
// User interacted with the DUT, so the device will not go to standby.
- skipActiveSourceLostUi(0);
+ skipActiveSourceLostUi(0, true, true);
assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
assertThat(mPowerManager.isInteractive()).isTrue();
assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
@@ -2387,6 +2598,10 @@
// Pop-up is triggered.
mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
mTestLooper.dispatchAll();
+ // RequestActiveSourceAction is triggered and TV confirms active source.
+ mNativeWrapper.onCecMessage(activeSourceFromTv);
+ mTestLooper.dispatchAll();
+
assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(setStreamPathToPlayback))
@@ -2407,6 +2622,8 @@
@Test
public void onActiveSourceLost_incomingRoutingChangeToDut_noStandbyAfterTimeout() {
+ int otherPlaybackLogicalAddress = mPlaybackLogicalAddress == Constants.ADDR_PLAYBACK_2
+ ? Constants.ADDR_PLAYBACK_1 : Constants.ADDR_PLAYBACK_2;
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
@@ -2420,18 +2637,21 @@
mNativeWrapper.clearResultMessages();
mTestLooper.dispatchAll();
- HdmiCecMessage activeSourceFromTv =
- HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+ HdmiCecMessage activeSourceFromOtherPlayback =
+ HdmiCecMessageBuilder.buildActiveSource(otherPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress + 0x0100);
HdmiCecMessage activeSourceFromPlayback =
HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress);
HdmiCecMessage routingChangeToPlayback =
- HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000,
+ HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV,
+ mPlaybackPhysicalAddress + 0x0100,
mPlaybackPhysicalAddress);
- assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromTv))
+ assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromOtherPlayback))
.isEqualTo(Constants.HANDLED);
- assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(ADDR_TV);
+ assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(
+ otherPlaybackLogicalAddress);
mTestLooper.dispatchAll();
// Pop-up is triggered.
@@ -2600,13 +2820,30 @@
assertThat(mPowerManager.isInteractive()).isFalse();
}
- private void skipActiveSourceLostUi(long idleDuration) {
+ private void skipActiveSourceLostUi(long idleDuration, boolean activeSourceLostToTv,
+ boolean tvAnswersRequest) {
mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
mTestLooper.dispatchAll();
+ if (activeSourceLostToTv) {
+ // RequestActiveSourceAction is triggered.
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+ if (tvAnswersRequest) {
+ HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV,
+ 0x0000);
+ mNativeWrapper.onCecMessage(activeSource);
+ mTestLooper.dispatchAll();
+ } else {
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+ assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+ return;
+ }
+ }
assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
mPowerManagerInternal.setIdleDuration(idleDuration);
mTestLooper.moveTimeForward(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
mTestLooper.dispatchAll();
}
-}
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index 935c8b8..51276a4 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -2234,6 +2234,25 @@
}
@Test
+ public void handleReportPhysicalAddress_samePathAsActiveSource_differentLA_newActiveSource() {
+ // This scenario can be reproduced if active source is hotplugged out and replaced with
+ // another device that might have another LA.
+ int physicalAddress = 0x1000;
+ mHdmiControlService.setActiveSource(Constants.ADDR_PLAYBACK_1, physicalAddress,
+ "HdmiControlServiceTest");
+ mHdmiCecLocalDeviceTv.setActivePath(physicalAddress);
+ HdmiCecMessage reportPhysicalAddressFromPlayback2 =
+ HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(ADDR_PLAYBACK_2,
+ physicalAddress, HdmiDeviceInfo.DEVICE_PLAYBACK);
+ HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
+ physicalAddress);
+ mNativeWrapper.onCecMessage(reportPhysicalAddressFromPlayback2);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(setStreamPath);
+ }
+
+ @Test
public void onOneTouchPlay_wakeUp_addCecDevice() {
assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
.isEmpty();
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
deleted file mode 100644
index 370bd80..0000000
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
+++ /dev/null
@@ -1,216 +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.server.integrity.parser;
-
-import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
-import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
-import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY;
-import static com.android.server.integrity.utils.TestUtils.getBits;
-import static com.android.server.integrity.utils.TestUtils.getBytes;
-import static com.android.server.integrity.utils.TestUtils.getValueBits;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import android.content.integrity.AppInstallMetadata;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-@RunWith(JUnit4.class)
-public class RuleIndexingControllerTest {
-
- @Test
- public void verifyIndexRangeSearchIsCorrect() throws IOException {
- InputStream inputStream = obtainDefaultIndexingMapForTest();
-
- RuleIndexingController indexingController = new RuleIndexingController(inputStream);
-
- AppInstallMetadata appInstallMetadata =
- new AppInstallMetadata.Builder()
- .setPackageName("ddd")
- .setAppCertificates(Collections.singletonList("777"))
- .setAppCertificateLineage(Collections.singletonList("777"))
- .build();
-
- List<RuleIndexRange> resultingIndexes =
- indexingController.identifyRulesToEvaluate(appInstallMetadata);
-
- assertThat(resultingIndexes)
- .containsExactly(
- new RuleIndexRange(200, 300),
- new RuleIndexRange(700, 800),
- new RuleIndexRange(900, 945));
- }
-
- @Test
- public void verifyIndexRangeSearchIsCorrect_multipleAppCertificates() throws IOException {
- InputStream inputStream = obtainDefaultIndexingMapForTest();
-
- RuleIndexingController indexingController = new RuleIndexingController(inputStream);
-
- AppInstallMetadata appInstallMetadata =
- new AppInstallMetadata.Builder()
- .setPackageName("ddd")
- .setAppCertificates(Arrays.asList("777", "999"))
- .setAppCertificateLineage(Arrays.asList("777", "999"))
- .build();
-
- List<RuleIndexRange> resultingIndexes =
- indexingController.identifyRulesToEvaluate(appInstallMetadata);
-
- assertThat(resultingIndexes)
- .containsExactly(
- new RuleIndexRange(200, 300),
- new RuleIndexRange(700, 800),
- new RuleIndexRange(800, 900),
- new RuleIndexRange(900, 945));
- }
-
- @Test
- public void verifyIndexRangeSearchIsCorrect_keysInFirstAndLastBlock() throws IOException {
- InputStream inputStream = obtainDefaultIndexingMapForTest();
-
- RuleIndexingController indexingController = new RuleIndexingController(inputStream);
-
- AppInstallMetadata appInstallMetadata =
- new AppInstallMetadata.Builder()
- .setPackageName("bbb")
- .setAppCertificates(Collections.singletonList("999"))
- .setAppCertificateLineage(Collections.singletonList("999"))
- .build();
-
- List<RuleIndexRange> resultingIndexes =
- indexingController.identifyRulesToEvaluate(appInstallMetadata);
-
- assertThat(resultingIndexes)
- .containsExactly(
- new RuleIndexRange(100, 200),
- new RuleIndexRange(800, 900),
- new RuleIndexRange(900, 945));
- }
-
- @Test
- public void verifyIndexRangeSearchIsCorrect_keysMatchWithValues() throws IOException {
- InputStream inputStream = obtainDefaultIndexingMapForTest();
-
- RuleIndexingController indexingController = new RuleIndexingController(inputStream);
-
- AppInstallMetadata appInstallMetadata =
- new AppInstallMetadata.Builder()
- .setPackageName("ccc")
- .setAppCertificates(Collections.singletonList("444"))
- .setAppCertificateLineage(Collections.singletonList("444"))
- .build();
-
- List<RuleIndexRange> resultingIndexes =
- indexingController.identifyRulesToEvaluate(appInstallMetadata);
-
- assertThat(resultingIndexes)
- .containsExactly(
- new RuleIndexRange(200, 300),
- new RuleIndexRange(700, 800),
- new RuleIndexRange(900, 945));
- }
-
- @Test
- public void verifyIndexRangeSearchIsCorrect_noIndexesAvailable() throws IOException {
- byte[] stringBytes =
- getBytes(
- getKeyValueString(START_INDEXING_KEY, 100)
- + getKeyValueString(END_INDEXING_KEY, 500)
- + getKeyValueString(START_INDEXING_KEY, 500)
- + getKeyValueString(END_INDEXING_KEY, 900)
- + getKeyValueString(START_INDEXING_KEY, 900)
- + getKeyValueString(END_INDEXING_KEY, 945));
- ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
- rule.put(stringBytes);
- InputStream inputStream = new ByteArrayInputStream(rule.array());
-
- RuleIndexingController indexingController = new RuleIndexingController(inputStream);
-
- AppInstallMetadata appInstallMetadata =
- new AppInstallMetadata.Builder()
- .setPackageName("ccc")
- .setAppCertificates(Collections.singletonList("444"))
- .setAppCertificateLineage(Collections.singletonList("444"))
- .build();
-
- List<RuleIndexRange> resultingIndexes =
- indexingController.identifyRulesToEvaluate(appInstallMetadata);
-
- assertThat(resultingIndexes)
- .containsExactly(
- new RuleIndexRange(100, 500),
- new RuleIndexRange(500, 900),
- new RuleIndexRange(900, 945));
- }
-
- @Test
- public void verifyIndexingFileIsCorrupt() throws IOException {
- byte[] stringBytes =
- getBytes(
- getKeyValueString(START_INDEXING_KEY, 100)
- + getKeyValueString("ccc", 200)
- + getKeyValueString(END_INDEXING_KEY, 300)
- + getKeyValueString(END_INDEXING_KEY, 900));
- ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
- rule.put(stringBytes);
- InputStream inputStream = new ByteArrayInputStream(rule.array());
-
- assertThrows(IllegalStateException.class,
- () -> new RuleIndexingController(inputStream));
- }
-
- private static InputStream obtainDefaultIndexingMapForTest() {
- byte[] stringBytes =
- getBytes(
- getKeyValueString(START_INDEXING_KEY, 100)
- + getKeyValueString("ccc", 200)
- + getKeyValueString("eee", 300)
- + getKeyValueString("hhh", 400)
- + getKeyValueString(END_INDEXING_KEY, 500)
- + getKeyValueString(START_INDEXING_KEY, 500)
- + getKeyValueString("111", 600)
- + getKeyValueString("444", 700)
- + getKeyValueString("888", 800)
- + getKeyValueString(END_INDEXING_KEY, 900)
- + getKeyValueString(START_INDEXING_KEY, 900)
- + getKeyValueString(END_INDEXING_KEY, 945));
- ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
- rule.put(stringBytes);
- return new ByteArrayInputStream(rule.array());
- }
-
- private static String getKeyValueString(String key, int value) {
- String isNotHashed = "0";
- return isNotHashed
- + getBits(key.length(), VALUE_SIZE_BITS)
- + getValueBits(key)
- + getBits(value, /* numOfBits= */ 32);
- }
-}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index e845d80..dec7f09 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -45,6 +45,9 @@
import static android.app.Notification.VISIBILITY_PRIVATE;
import static android.app.NotificationChannel.NEWS_ID;
import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationChannel.PROMOTIONS_ID;
+import static android.app.NotificationChannel.RECS_ID;
+import static android.app.NotificationChannel.SOCIAL_MEDIA_ID;
import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE;
import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
@@ -98,7 +101,10 @@
import static android.service.notification.Adjustment.KEY_TEXT_REPLIES;
import static android.service.notification.Adjustment.KEY_TYPE;
import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
+import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
import static android.service.notification.Adjustment.TYPE_NEWS;
+import static android.service.notification.Adjustment.TYPE_PROMOTION;
+import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA;
import static android.service.notification.Condition.SOURCE_CONTEXT;
import static android.service.notification.Condition.SOURCE_USER_ACTION;
import static android.service.notification.Condition.STATE_TRUE;
@@ -16767,6 +16773,24 @@
r.applyAdjustments();
assertThat(r.getChannel().getId()).isEqualTo(NEWS_ID);
+
+ signals.putInt(KEY_TYPE, TYPE_PROMOTION);
+ mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+ waitForIdle();
+ r.applyAdjustments();
+ assertThat(r.getChannel().getId()).isEqualTo(PROMOTIONS_ID);
+
+ signals.putInt(KEY_TYPE, TYPE_SOCIAL_MEDIA);
+ mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+ waitForIdle();
+ r.applyAdjustments();
+ assertThat(r.getChannel().getId()).isEqualTo(SOCIAL_MEDIA_ID);
+
+ signals.putInt(KEY_TYPE, TYPE_CONTENT_RECOMMENDATION);
+ mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+ waitForIdle();
+ r.applyAdjustments();
+ assertThat(r.getChannel().getId()).isEqualTo(RECS_ID);
}
@Test
@@ -17066,7 +17090,7 @@
@Test
@EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
public void testAppCannotUseReservedBundleChannels() throws Exception {
- mBinderService.getBubblePreferenceForPackage(mPkg, mUid);
+ mService.mPreferencesHelper.createReservedChannel(mPkg, mUid, TYPE_NEWS);
NotificationChannel news = mBinderService.getNotificationChannel(
mPkg, mContext.getUserId(), mPkg, NEWS_ID);
assertThat(news).isNotNull();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 36fa1b8..1a1da0f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -54,6 +54,11 @@
import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_SYSTEM;
import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
+import static android.service.notification.Adjustment.TYPE_NEWS;
+import static android.service.notification.Adjustment.TYPE_OTHER;
+import static android.service.notification.Adjustment.TYPE_PROMOTION;
+import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA;
import static android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION;
import static android.service.notification.Flags.notificationClassification;
@@ -640,6 +645,7 @@
NotificationChannel updateNews = null;
if (notificationClassification()) {
+ mHelper.createReservedChannel(PKG_N_MR1, UID_N_MR1, TYPE_NEWS);
// change one of the reserved bundle channels to ensure changes are persisted across
// boot
updateNews = mHelper.getNotificationChannel(
@@ -1210,22 +1216,9 @@
+ "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" "
+ "sent_valid_msg=\"false\" user_demote_msg_app=\"false\" sent_valid_bubble"
+ "=\"false\" uid=\"10002\">\n"
- + (notificationClassification() ? "<channel id=\"android.app.social\" "
- + "name=\"Social\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" "
- + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"id\" name=\"name\" importance=\"2\" "
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" show_badge=\"true\" orig_imp=\"2\" />\n"
- + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" "
- + "importance=\"2\" sound=\"content://settings/system/notification_sound\" "
- + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "</package>\n"
+ "<package name=\"com.example.n_mr1\" show_badge=\"true\" "
+ "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" "
@@ -1233,10 +1226,6 @@
+ "=\"false\" uid=\"10001\">\n"
+ "<channelGroup id=\"1\" name=\"bye\" blocked=\"false\" locked=\"0\" />\n"
+ "<channelGroup id=\"2\" name=\"hello\" blocked=\"false\" locked=\"0\" />\n"
- + (notificationClassification() ? "<channel id=\"android.app.social\" "
- + "name=\"Social\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" "
- + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"id1\" name=\"name1\" importance=\"4\" show_badge=\"true\" "
+ "orig_imp=\"4\" />\n"
+ "<channel id=\"id2\" name=\"name2\" desc=\"descriptions for all\" "
@@ -1246,15 +1235,6 @@
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" vibration_enabled=\"true\" show_badge=\"true\" "
+ "orig_imp=\"4\" />\n"
- + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" "
- + "importance=\"2\" sound=\"content://settings/system/notification_sound\" "
- + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"miscellaneous\" name=\"Uncategorized\" "
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
@@ -1321,22 +1301,9 @@
+ "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" "
+ "sent_valid_msg=\"false\" user_demote_msg_app=\"false\" sent_valid_bubble"
+ "=\"false\">\n"
- + (notificationClassification() ? "<channel id=\"android.app.social\" "
- + "name=\"Social\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" "
- + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"id\" name=\"name\" importance=\"2\" "
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" show_badge=\"true\" orig_imp=\"2\" />\n"
- + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" "
- + "importance=\"2\" sound=\"content://settings/system/notification_sound\" "
- + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "</package>\n"
// Importance default because on in permission helper
+ "<package name=\"com.example.n_mr1\" importance=\"3\" show_badge=\"true\" "
@@ -1345,10 +1312,6 @@
+ "=\"false\">\n"
+ "<channelGroup id=\"1\" name=\"bye\" blocked=\"false\" locked=\"0\" />\n"
+ "<channelGroup id=\"2\" name=\"hello\" blocked=\"false\" locked=\"0\" />\n"
- + (notificationClassification() ? "<channel id=\"android.app.social\" "
- + "name=\"Social\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" "
- + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"id1\" name=\"name1\" importance=\"4\" show_badge=\"true\" "
+ "orig_imp=\"4\" />\n"
+ "<channel id=\"id2\" name=\"name2\" desc=\"descriptions for all\" "
@@ -1358,15 +1321,6 @@
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" vibration_enabled=\"true\" show_badge=\"true\" "
+ "orig_imp=\"4\" />\n"
- + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" "
- + "importance=\"2\" sound=\"content://settings/system/notification_sound\" "
- + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"miscellaneous\" name=\"Uncategorized\" "
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
@@ -1433,22 +1387,9 @@
+ "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" "
+ "sent_valid_msg=\"false\" user_demote_msg_app=\"false\" sent_valid_bubble"
+ "=\"false\">\n"
- + (notificationClassification() ? "<channel id=\"android.app.social\" "
- + "name=\"Social\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"id\" name=\"name\" importance=\"2\" "
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" show_badge=\"true\" orig_imp=\"2\" />\n"
- + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" "
- + "importance=\"2\" sound=\"content://settings/system/notification_sound\" "
- + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "</package>\n"
// Importance 0 because missing from permission helper
+ "<package name=\"com.example.n_mr1\" importance=\"0\" show_badge=\"true\" "
@@ -1457,10 +1398,6 @@
+ "=\"false\">\n"
+ "<channelGroup id=\"1\" name=\"bye\" blocked=\"false\" locked=\"0\" />\n"
+ "<channelGroup id=\"2\" name=\"hello\" blocked=\"false\" locked=\"0\" />\n"
- + (notificationClassification() ? "<channel id=\"android.app.social\" "
- + "name=\"Social\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"id1\" name=\"name1\" importance=\"4\" show_badge=\"true\" "
+ "orig_imp=\"4\" />\n"
+ "<channel id=\"id2\" name=\"name2\" desc=\"descriptions for all\" "
@@ -1470,15 +1407,6 @@
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" vibration_enabled=\"true\" show_badge=\"true\" "
+ "orig_imp=\"4\" />\n"
- + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" "
- + "importance=\"2\" sound=\"content://settings/system/notification_sound\" "
- + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"miscellaneous\" name=\"Uncategorized\" "
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
@@ -2183,10 +2111,10 @@
}
@Test
- @DisableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
public void testUpdate_preUpgrade_updatesAppFields() throws Exception {
assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
- assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1));
+ assertEquals(Notification.PRIORITY_DEFAULT,
+ mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1));
assertEquals(VISIBILITY_NO_OVERRIDE,
mHelper.getPackageVisibility(PKG_N_MR1, UID_N_MR1));
@@ -2549,7 +2477,7 @@
List<NotificationChannel> channels =
mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true).getList();
// Default channel + non-deleted channel + system defaults
- assertEquals(notificationClassification() ? 6 : 2, channels.size());
+ assertEquals(2, channels.size());
for (NotificationChannel nc : channels) {
if (channel2.getId().equals(nc.getId())) {
compareChannels(channel2, nc);
@@ -2559,7 +2487,7 @@
// Returns deleted channels too
channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true, true).getList();
// Includes system channel(s)
- assertEquals(notificationClassification() ? 7 : 3, channels.size());
+ assertEquals(3, channels.size());
for (NotificationChannel nc : channels) {
if (channel2.getId().equals(nc.getId())) {
compareChannels(channelMap.get(nc.getId()), nc);
@@ -3036,7 +2964,6 @@
}
@Test
- @DisableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
public void testOnlyHasDefaultChannel() throws Exception {
assertTrue(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
assertFalse(mHelper.onlyHasDefaultChannel(PKG_O, UID_O));
@@ -3047,6 +2974,18 @@
}
@Test
+ @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
+ public void testOnlyHasDefaultChannel_bundleExists() throws Exception {
+ mHelper.createReservedChannel(PKG_N_MR1, UID_N_MR1, TYPE_NEWS);
+ assertTrue(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
+ assertFalse(mHelper.onlyHasDefaultChannel(PKG_O, UID_O));
+
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false,
+ UID_N_MR1, false);
+ assertFalse(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
+ }
+
+ @Test
public void testCreateDeletedChannel() throws Exception {
long[] vibration = new long[]{100, 67, 145, 156};
NotificationChannel channel =
@@ -3315,7 +3254,7 @@
// user 0 records remain
for (int i = 0; i < user0Uids.length; i++) {
- assertEquals(notificationClassification() ? 5 : 1,
+ assertEquals(1,
mHelper.getNotificationChannels(PKG_N_MR1, user0Uids[i], false, true)
.getList().size());
}
@@ -3346,7 +3285,7 @@
assertFalse(mHelper.onPackagesChanged(false, USER_SYSTEM,
new String[]{PKG_N_MR1}, new int[]{UID_N_MR1}));
- assertEquals(notificationClassification() ? 6 : 2,
+ assertEquals(2,
mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true)
.getList().size());
}
@@ -3420,7 +3359,7 @@
@Test
public void testRecordDefaults() throws Exception {
assertEquals(true, mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
- assertEquals(notificationClassification() ? 5 : 1,
+ assertEquals(1,
mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true)
.getList().size());
}
@@ -3659,9 +3598,6 @@
new NotificationChannel("" + j, "a", IMPORTANCE_HIGH), true, false,
UID_N_MR1, false);
}
- if (notificationClassification()) {
- numChannels += 4;
- }
expectedChannels.put(pkgName, numChannels);
}
@@ -4883,10 +4819,6 @@
@Test
public void testTooManyChannels() {
int numToCreate = NOTIFICATION_CHANNEL_COUNT_LIMIT;
- if (notificationClassification()) {
- // reserved channels lower limit
- numToCreate -= 4;
- }
for (int i = 0; i < numToCreate; i++) {
NotificationChannel channel = new NotificationChannel(String.valueOf(i),
String.valueOf(i), NotificationManager.IMPORTANCE_HIGH);
@@ -4907,10 +4839,6 @@
@Test
public void testTooManyChannels_xml() throws Exception {
int numToCreate = NOTIFICATION_CHANNEL_COUNT_LIMIT;
- if (notificationClassification()) {
- // reserved channels lower limit
- numToCreate -= 4;
- }
String extraChannel = "EXTRA";
String extraChannel1 = "EXTRA1";
@@ -5928,9 +5856,7 @@
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackageChannelPreferencesStats(events);
- assertEquals("expected number of events",
- notificationClassification() ? 7 : 3,
- events.size());
+ assertEquals("expected number of events", 3, events.size());
for (StatsEvent ev : events) {
// all of these events should be of PackageNotificationChannelPreferences type,
// and therefore we expect the atom to have this field.
@@ -5971,17 +5897,11 @@
mHelper.createNotificationChannel(PKG_O, UID_O, channelC, true, false, UID_O, false);
List<String> channels = new LinkedList<>(Arrays.asList("a", "b", "c"));
- if (notificationClassification()) {
- channels.add(NEWS_ID);
- channels.add(PROMOTIONS_ID);
- channels.add(SOCIAL_MEDIA_ID);
- channels.add(RECS_ID);
- }
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackageChannelPreferencesStats(events);
- assertEquals("total events", notificationClassification() ? 7 : 3, events.size());
+ assertEquals("total events", 3, events.size());
for (StatsEvent ev : events) {
AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
assertTrue(atom.hasPackageNotificationChannelPreferences());
@@ -6015,7 +5935,7 @@
mHelper.pullPackageChannelPreferencesStats(events);
// In this case, we want to check the properties of the conversation channel (not parent)
- assertEquals("total events", notificationClassification() ? 6 : 2, events.size());
+ assertEquals("total events", 2, events.size());
for (StatsEvent ev : events) {
AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
assertTrue(atom.hasPackageNotificationChannelPreferences());
@@ -6047,9 +5967,7 @@
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackageChannelPreferencesStats(events);
- assertEquals("total events",
- notificationClassification() ? 6 : 2,
- events.size());
+ assertEquals("total events", 2, events.size());
for (StatsEvent ev : events) {
AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
assertTrue(atom.hasPackageNotificationChannelPreferences());
@@ -6080,9 +5998,7 @@
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackageChannelPreferencesStats(events);
- assertEquals("total events",
- notificationClassification() ? 6 : 2,
- events.size());
+ assertEquals("total events", 2, events.size());
for (StatsEvent ev : events) {
AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
assertTrue(atom.hasPackageNotificationChannelPreferences());
@@ -6367,8 +6283,7 @@
@Test
@EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
public void testGetNotificationChannels_omitBundleChannels() {
- // do something that triggers settings creation for an app
- mHelper.setShowBadge(PKG_O, UID_O, true);
+ mHelper.createReservedChannel(PKG_O, UID_O, TYPE_NEWS);
assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, false).getList()).isEmpty();
}
@@ -6376,18 +6291,34 @@
@Test
@EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
public void testNotificationBundles() {
- // do something that triggers settings creation for an app
- mHelper.setShowBadge(PKG_O, UID_O, true);
-
- // verify 4 reserved channels are created
+ mHelper.createReservedChannel(PKG_O, UID_O, TYPE_NEWS);
assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, NEWS_ID, false).getImportance())
.isEqualTo(IMPORTANCE_LOW);
- assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, PROMOTIONS_ID, false)
- .getImportance()).isEqualTo(IMPORTANCE_LOW);
- assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, SOCIAL_MEDIA_ID, false)
- .getImportance()).isEqualTo(IMPORTANCE_LOW);
+ assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size())
+ .isEqualTo(1);
+
+ mHelper.createReservedChannel(PKG_O, UID_O, TYPE_SOCIAL_MEDIA);
+ assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, SOCIAL_MEDIA_ID, false).
+ getImportance()).isEqualTo(IMPORTANCE_LOW);
+ assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size())
+ .isEqualTo(2);
+
+ mHelper.createReservedChannel(PKG_O, UID_O, TYPE_CONTENT_RECOMMENDATION);
assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, RECS_ID, false).getImportance())
.isEqualTo(IMPORTANCE_LOW);
+ assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size())
+ .isEqualTo(3);
+
+ mHelper.createReservedChannel(PKG_O, UID_O, TYPE_PROMOTION);
+ assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, PROMOTIONS_ID, false)
+ .getImportance()).isEqualTo(IMPORTANCE_LOW);
+ assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size())
+ .isEqualTo(4);
+
+ // only the first 4 types are created; no others
+ mHelper.createReservedChannel(PKG_O, UID_O, TYPE_OTHER);
+ assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size())
+ .isEqualTo(4);
}
@Test
@@ -6417,8 +6348,7 @@
@Test
@EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
public void testNotificationBundles_appsCannotUpdate() {
- // do something that triggers settings creation for an app
- mHelper.setShowBadge(PKG_O, UID_O, true);
+ mHelper.createReservedChannel(PKG_O, UID_O, TYPE_NEWS);
NotificationChannel fromApp =
new NotificationChannel(NEWS_ID, "The best channel", IMPORTANCE_HIGH);
@@ -6431,8 +6361,7 @@
@Test
@EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
public void testNotificationBundles_osCanAllowToBypassDnd() {
- // do something that triggers settings creation for an app
- mHelper.setShowBadge(PKG_O, UID_O, true);
+ mHelper.createReservedChannel(PKG_O, UID_O, TYPE_NEWS);
NotificationChannel fromApp =
new NotificationChannel(NEWS_ID, "The best channel", IMPORTANCE_HIGH);
@@ -6442,18 +6371,17 @@
@Test
@EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
public void testUnDeleteBundleChannelsOnLoadIfNotUserChange() throws Exception {
- mHelper.setShowBadge(PKG_P, UID_P, true);
// the public create/update methods should prevent this, so take advantage of the fact that
// the object is in the same process
- mHelper.getNotificationChannel(PKG_P, UID_P, SOCIAL_MEDIA_ID, true).setDeleted(true);
+ mHelper.createReservedChannel(PKG_N_MR1, UID_N_MR1, TYPE_SOCIAL_MEDIA).setDeleted(true);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false,
UserHandle.USER_ALL, SOCIAL_MEDIA_ID);
loadStreamXml(baos, false, UserHandle.USER_ALL);
- assertThat(mXmlHelper.getNotificationChannel(PKG_P, UID_P, SOCIAL_MEDIA_ID, true).
- isDeleted()).isFalse();
+ assertThat(mXmlHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, SOCIAL_MEDIA_ID, true)
+ .isDeleted()).isFalse();
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
index c186a03..28ae271 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
@@ -43,6 +43,8 @@
import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Intent;
+import android.hardware.input.AppLaunchData;
+import android.hardware.input.KeyGestureEvent;
import android.os.RemoteException;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
@@ -85,7 +87,8 @@
* Test meta+ shortcuts defined in bookmarks.xml.
*/
@Test
- public void testMetaShortcuts() {
+ @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
+ public void testMetaShortcuts_withoutKeyGestureEventHandling() {
for (int i = 0; i < INTENT_SHORTCUTS.size(); i++) {
final int keyCode = INTENT_SHORTCUTS.keyAt(i);
final String category = INTENT_SHORTCUTS.valueAt(i);
@@ -115,6 +118,49 @@
}
+ @Test
+ @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
+ public void testMetaShortcuts_withKeyGestureEventHandling() {
+ for (int i = 0; i < INTENT_SHORTCUTS.size(); i++) {
+ final String category = INTENT_SHORTCUTS.valueAt(i);
+ mPhoneWindowManager.sendKeyGestureEvent(
+ new KeyGestureEvent.Builder()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+ .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ .setAppLaunchData(AppLaunchData.createLaunchDataForCategory(category))
+ .build()
+ );
+ mPhoneWindowManager.assertLaunchCategory(category);
+ }
+
+ mPhoneWindowManager.overrideRoleManager();
+ for (int i = 0; i < ROLE_SHORTCUTS.size(); i++) {
+ final String role = ROLE_SHORTCUTS.valueAt(i);
+
+ mPhoneWindowManager.sendKeyGestureEvent(
+ new KeyGestureEvent.Builder()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+ .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ .setAppLaunchData(AppLaunchData.createLaunchDataForRole(role))
+ .build()
+ );
+ mPhoneWindowManager.assertLaunchRole(role);
+ }
+
+ mPhoneWindowManager.sendKeyGestureEvent(
+ new KeyGestureEvent.Builder()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+ .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ .setAppLaunchData(
+ new AppLaunchData.ComponentData("com.test",
+ "com.test.BookmarkTest"))
+ .build()
+ );
+ mPhoneWindowManager.assertActivityTargetLaunched(
+ new ComponentName("com.test", "com.test.BookmarkTest"));
+
+ }
+
/**
* ALT + TAB to show recent apps.
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
index 7509681..965b65c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
@@ -39,7 +39,6 @@
import android.app.ActivityOptions;
import android.app.AppOpsManager;
-import android.app.BackgroundStartPrivileges;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -223,12 +222,12 @@
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
assertWithMessage(balState.toString()).that(balState.isPendingIntent()).isTrue();
@@ -263,12 +262,12 @@
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -295,12 +294,12 @@
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -328,14 +327,14 @@
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions
.setPendingIntentCreatorBackgroundActivityStartMode(
MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE);
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -363,14 +362,14 @@
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions
.setPendingIntentCreatorBackgroundActivityStartMode(
MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE);
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -405,12 +404,12 @@
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -441,12 +440,12 @@
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, null,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -490,12 +489,12 @@
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, null,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -526,12 +525,12 @@
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, null,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -563,14 +562,14 @@
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions
.setPendingIntentBackgroundActivityStartMode(
MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE);
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, null,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -597,12 +596,12 @@
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, null,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -630,14 +629,14 @@
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
checkedOptions.setPendingIntentBackgroundActivityStartMode(
MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, null,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -664,12 +663,12 @@
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, null,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -697,14 +696,14 @@
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions =
mCheckedOptions.setPendingIntentBackgroundActivityStartMode(
MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, null,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -733,12 +732,12 @@
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, null,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -764,12 +763,12 @@
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, null,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -795,12 +794,12 @@
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, null,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -831,12 +830,12 @@
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, null,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java
index 23b1c4b..7f7462d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java
@@ -23,7 +23,6 @@
import static com.google.common.truth.Truth.assertThat;
import android.app.ActivityOptions;
-import android.app.BackgroundStartPrivileges;
import android.content.Intent;
import android.platform.test.annotations.Presubmit;
@@ -189,7 +188,7 @@
private void useIntent(int uid) {
mState = mController.new BalState(uid, APP1_PID,
"calling.package", uid, APP1_PID, null,
- null, BackgroundStartPrivileges.NONE, null, new Intent(),
+ null, false, null, new Intent(),
ActivityOptions.makeBasic());
}
@@ -200,7 +199,7 @@
private void usePendingIntent(int callerUid, int realCallerUid) {
mState = mController.new BalState(callerUid, APP1_PID,
"calling.package", realCallerUid, APP2_PID, null,
- mPendingIntentRecord, BackgroundStartPrivileges.NONE, null, new Intent(),
+ mPendingIntentRecord, false, null, new Intent(),
ActivityOptions.makeBasic());
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
index 6ec7895..7bc9f30 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
@@ -241,14 +241,14 @@
int realCallingUid = NO_UID;
int realCallingPid = NO_PID;
PendingIntentRecord originatingPendingIntent = null;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic();
// call
BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// assertions
@@ -276,14 +276,14 @@
int realCallingUid = NO_UID;
int realCallingPid = NO_PID;
PendingIntentRecord originatingPendingIntent = null;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic();
// call
BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// assertions
@@ -311,14 +311,14 @@
int realCallingUid = NO_UID;
int realCallingPid = NO_PID;
PendingIntentRecord originatingPendingIntent = null;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic();
// call
BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// assertions
@@ -346,14 +346,14 @@
int realCallingUid = NO_UID;
int realCallingPid = NO_PID;
PendingIntentRecord originatingPendingIntent = null;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic();
// call
BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// assertions
@@ -380,14 +380,14 @@
int realCallingUid = NO_UID;
int realCallingPid = NO_PID;
PendingIntentRecord originatingPendingIntent = null;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic();
// call
BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// assertions
@@ -418,7 +418,7 @@
int realCallingUid = NO_UID;
int realCallingPid = NO_PID;
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic()
.setPendingIntentBackgroundActivityStartMode(
@@ -429,7 +429,7 @@
// call
BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// assertions
@@ -457,7 +457,7 @@
int realCallingUid = NO_UID;
int realCallingPid = NO_PID;
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic()
.setPendingIntentCreatorBackgroundActivityStartMode(
@@ -466,7 +466,7 @@
// call
BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// assertions
@@ -494,7 +494,7 @@
int realCallingUid = NO_UID;
int realCallingPid = NO_PID;
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic()
.setPendingIntentBackgroundActivityStartMode(
@@ -503,7 +503,7 @@
// call
BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// assertions
@@ -530,7 +530,7 @@
int realCallingUid = NO_UID;
int realCallingPid = NO_PID;
PendingIntentRecord originatingPendingIntent = null;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic();
WindowProcessController callerApp = mCallerApp;
@@ -539,8 +539,8 @@
// call
BackgroundActivityStartController.BalState balState = mController
.new BalState(callingUid, callingPid, callingPackage, realCallingUid,
- realCallingPid, callerApp, originatingPendingIntent, forcedBalByPiSender,
- resultRecord, intent, checkedOptions);
+ realCallingPid, callerApp, originatingPendingIntent,
+ allowBalExemptionForSystemProcess, resultRecord, intent, checkedOptions);
// assertions
assertThat(balState.mAutoOptInReason).isEqualTo("notPendingIntent");
@@ -559,7 +559,7 @@
+ "callingUidHasAnyVisibleWindow: false; "
+ "callingUidProcState: NONEXISTENT; "
+ "isCallingUidPersistentSystemProcess: false; "
- + "forcedBalByPiSender: BSP.NONE; "
+ + "allowBalExemptionForSystemProcess: false; "
+ "intent: Intent { cmp=package.app3/someClass }; "
+ "callerApp: mCallerApp; "
+ "inVisibleTask: false; "
@@ -596,7 +596,7 @@
int realCallingUid = NO_UID;
int realCallingPid = NO_PID;
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic();
WindowProcessController callerApp = mCallerApp;
@@ -605,8 +605,8 @@
// call
BackgroundActivityStartController.BalState balState = mController
.new BalState(callingUid, callingPid, callingPackage, realCallingUid,
- realCallingPid, callerApp, originatingPendingIntent, forcedBalByPiSender,
- resultRecord, intent, checkedOptions);
+ realCallingPid, callerApp, originatingPendingIntent,
+ allowBalExemptionForSystemProcess, resultRecord, intent, checkedOptions);
// assertions
assertThat(balState.mAutoOptInReason).isEqualTo("callForResult");
@@ -629,7 +629,7 @@
int realCallingUid = NO_UID;
int realCallingPid = NO_PID;
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic();
WindowProcessController callerApp = mCallerApp;
@@ -638,8 +638,8 @@
// call
BackgroundActivityStartController.BalState balState = mController
.new BalState(callingUid, callingPid, callingPackage, realCallingUid,
- realCallingPid, callerApp, originatingPendingIntent, forcedBalByPiSender,
- resultRecord, intent, checkedOptions);
+ realCallingPid, callerApp, originatingPendingIntent,
+ allowBalExemptionForSystemProcess, resultRecord, intent, checkedOptions);
// assertions
assertThat(balState.mAutoOptInReason).isNull();
@@ -659,7 +659,7 @@
+ "callingUidHasAnyVisibleWindow: false; "
+ "callingUidProcState: NONEXISTENT; "
+ "isCallingUidPersistentSystemProcess: false; "
- + "forcedBalByPiSender: BSP.NONE; "
+ + "allowBalExemptionForSystemProcess: false; "
+ "intent: Intent { cmp=package.app3/someClass }; "
+ "callerApp: mCallerApp; "
+ "inVisibleTask: false; "
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 7196acc..e8779c2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -2354,6 +2354,11 @@
// ChangeInfo#mCommonAncestor should be set after reparent.
final Transition.ChangeInfo change = transition.mChanges.get(activity);
assertEquals(newParent.getDisplayArea(), change.mCommonAncestor);
+
+ // WindowContainer#onDisplayChanged should collect the moved task.
+ final DisplayContent newDisplay = createNewDisplay();
+ newParent.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
+ assertTrue(transition.mParticipants.contains(newParent));
}
@Test
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java
index b1165bb..dfd80a0 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java
@@ -36,6 +36,8 @@
import android.os.ServiceSpecificException;
import android.os.SystemClock;
+import com.android.server.FgThread;
+
public class SoundTriggerHw3Compat implements ISoundTriggerHal {
private final @NonNull ISoundTriggerHw mDriver;
private final @NonNull Runnable mRebootRunnable;
@@ -217,7 +219,12 @@
@Override
public void onResourcesAvailable() {
- mDelegate.onResourcesAvailable();
+ // This call does not need to be sequenced relative to sessions on the upper levels.
+ // That is, if a new session gets this callback or if a already detached session gets
+ // this callback, because it is delayed, it doesn't matter, since this callback is
+ // purely informative and does not mutate any state -- it merely causes an already legal
+ // operation to be possibly re-attempted.
+ FgThread.getExecutor().execute(mDelegate::onResourcesAvailable);
}
@Override
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index f803717..7adcd46 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -101,7 +101,7 @@
private Set<String> mPreviousExtraKeys;
private final Object mExtrasLock = new Object();
private Uri mAddress;
- private int mAddressPresentation;
+ private int mAddressPresentation = TelecomManager.PRESENTATION_UNKNOWN;
private String mCallerDisplayName;
private int mCallerDisplayNamePresentation;
private int mCallDirection;
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index ad7d987..29d3942 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -2164,7 +2164,7 @@
private CallAudioState mCallAudioState;
private CallEndpoint mCallEndpoint;
private Uri mAddress;
- private int mAddressPresentation;
+ private int mAddressPresentation = TelecomManager.PRESENTATION_UNKNOWN;
private String mCallerDisplayName;
private int mCallerDisplayNamePresentation;
private boolean mRingbackRequested = false;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a7fe0cb..fad59f8b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -17278,6 +17278,18 @@
}
/**
+ * Setup sISms for testing.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public static void setupISmsForTest(ISms iSms) {
+ synchronized (sCacheLock) {
+ sISms = iSms;
+ }
+ }
+
+ /**
* Whether device can connect to 5G network when two SIMs are active.
*
* @hide TODO b/153669716: remove or make system API.