Merge "a11y: Pause auto click with pause button" into main
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 4b1afa5..01b2953 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -146,6 +146,7 @@
int getFrontActivityScreenCompatMode();
void setFrontActivityScreenCompatMode(int mode);
void setFocusedTask(int taskId);
+ boolean setTaskIsPerceptible(int taskId, boolean isPerceptible);
boolean removeTask(int taskId);
void removeAllVisibleRecentTasks();
List<ActivityManager.RunningTaskInfo> getTasks(int maxNum, boolean filterOnlyVisibleRecents,
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 08719fc..500f7585 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -14200,6 +14200,9 @@
* <li>Manifest.permission.ACTIVITY_RECOGNITION</li>
* <li>Manifest.permission.BODY_SENSORS</li>
* </ul>
+ * On devices running {@link android.os.Build.VERSION_CODES#BAKLAVA}, the
+ * {@link android.health.connect.HealthPermissions} are also included in the
+ * restricted list.
* <p>
* A profile owner may not grant these permissions (i.e. call this method with any of the
* permissions listed above and {@code grantState} of {@code #PERMISSION_GRANT_STATE_GRANTED}),
diff --git a/core/java/android/app/contextualsearch/flags.aconfig b/core/java/android/app/contextualsearch/flags.aconfig
index bc1f7ce..1de034b 100644
--- a/core/java/android/app/contextualsearch/flags.aconfig
+++ b/core/java/android/app/contextualsearch/flags.aconfig
@@ -24,7 +24,7 @@
}
flag {
- name: "contextual_search_window_layer"
+ name: "contextual_search_prevent_self_capture"
namespace: "sysui_integrations"
description: "Identify live contextual search UI to exclude from contextual search screenshot."
bug: "390176823"
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index ef200c3..2e09994 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -2358,8 +2358,13 @@
* @param locales The locale list. If null, an empty LocaleList will be assigned.
*/
public void setLocales(@Nullable LocaleList locales) {
+ LocaleList oldList = mLocaleList;
mLocaleList = locales == null ? LocaleList.getEmptyLocaleList() : locales;
locale = mLocaleList.get(0);
+ if (!mLocaleList.equals(oldList)) {
+ Slog.v(TAG, "Updating configuration, locales updated from " + oldList
+ + " to " + mLocaleList);
+ }
setLayoutDirection(locale);
}
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 96c7176..eec30f3 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -491,6 +491,9 @@
}
defaultLocale =
adjustLanguageTag(lc.getDefaultLocale().toLanguageTag());
+ Slog.v(TAG, "Updating configuration, with default locale "
+ + defaultLocale + " and selected locales "
+ + Arrays.toString(selectedLocales));
} else {
String[] availableLocales;
// The LocaleList has changed. We must query the AssetManager's
@@ -526,6 +529,7 @@
for (int i = 0; i < locales.size(); i++) {
selectedLocales[i] = adjustLanguageTag(locales.get(i).toLanguageTag());
}
+ defaultLocale = adjustLanguageTag(lc.getDefaultLocale().toLanguageTag());
} else {
selectedLocales = new String[]{
adjustLanguageTag(locales.get(0).toLanguageTag())};
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index b44620f..4ba9738 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -105,6 +105,7 @@
ENABLE_RESIZING_METRICS(Flags::enableResizingMetrics, true),
ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE(
Flags::enableRestoreToPreviousSizeFromDesktopImmersive, true),
+ ENABLE_TASKBAR_OVERFLOW(Flags::enableTaskbarOverflow, false),
ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS(Flags::enableTaskResizingKeyboardShortcuts, true),
ENABLE_TASK_STACK_OBSERVER_IN_SHELL(Flags::enableTaskStackObserverInShell, true),
ENABLE_THEMED_APP_HEADERS(Flags::enableThemedAppHeaders, true),
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 3e3b8e1..8265d25 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -705,6 +705,13 @@
}
flag {
+ name: "enable_activity_embedding_support_for_connected_displays"
+ namespace: "lse_desktop_experience"
+ description: "Enables activity embedding support for connected displays, including enabling AE optimization for Settings."
+ bug: "369438353"
+}
+
+flag {
name: "enable_full_screen_window_on_removing_split_screen_stage_bugfix"
namespace: "lse_desktop_experience"
description: "Enables clearing the windowing mode of a freeform window when removing the task from the split screen stage."
@@ -764,3 +771,10 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "enable_taskbar_overflow"
+ namespace: "lse_desktop_experience"
+ description: "Show recent apps in the taskbar overflow."
+ bug: "368119679"
+}
diff --git a/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java b/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java
index b57acf3..b19967a 100644
--- a/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java
+++ b/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java
@@ -261,7 +261,12 @@
// Default value is false for when the flag is not found.
// Note: Unlike with the old storage, with AconfigPackage, we don't have a way to
// know if the flag is not found or if it's found but the value is false.
- value = aconfigPackage.getBooleanFlagValue(flagName, false);
+ try {
+ value = aconfigPackage.getBooleanFlagValue(flagName, false);
+ } catch (Exception e) {
+ Slog.e(LOG_TAG, "Failed to read Aconfig flag value for " + flagPackageAndName, e);
+ return null;
+ }
}
if (DEBUG) {
Slog.v(LOG_TAG, "Aconfig flag value for " + flagPackageAndName + " = " + value);
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index e9d920c..64b44df 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -121,22 +121,38 @@
setMessagingClippingDisabled(false);
}
- @RemotableViewMethod
+ @RemotableViewMethod(asyncImpl = "setAvatarReplacementAsync")
public void setAvatarReplacement(Icon icon) {
mAvatarReplacement = icon;
}
- @RemotableViewMethod
+ /**
+ * @hide
+ */
+ public Runnable setAvatarReplacementAsync(Icon icon) {
+ mAvatarReplacement = icon;
+ return () -> {};
+ }
+
+ @RemotableViewMethod(asyncImpl = "setNameReplacementAsync")
public void setNameReplacement(CharSequence nameReplacement) {
mNameReplacement = nameReplacement;
}
/**
+ * @hide
+ */
+ public Runnable setNameReplacementAsync(CharSequence nameReplacement) {
+ mNameReplacement = nameReplacement;
+ return () -> {};
+ }
+
+ /**
* Set this layout to show the collapsed representation.
*
* @param isCollapsed is it collapsed
*/
- @RemotableViewMethod
+ @RemotableViewMethod(asyncImpl = "setIsCollapsedAsync")
public void setIsCollapsed(boolean isCollapsed) {
mIsCollapsed = isCollapsed;
}
@@ -145,7 +161,6 @@
* setDataAsync needs to do different stuff for the collapsed vs expanded view, so store the
* collapsed state early.
*/
- @RemotableViewMethod(asyncImpl = "setIsCollapsedAsync")
public Runnable setIsCollapsedAsync(boolean isCollapsed) {
mIsCollapsed = isCollapsed;
return () -> {};
@@ -161,12 +176,20 @@
*
* @param conversationTitle the conversation title
*/
- @RemotableViewMethod
+ @RemotableViewMethod(asyncImpl = "setConversationTitleAsync")
public void setConversationTitle(CharSequence conversationTitle) {
mConversationTitle = conversationTitle;
}
/**
+ * @hide
+ */
+ public Runnable setConversationTitleAsync(CharSequence conversationTitle) {
+ mConversationTitle = conversationTitle;
+ return ()->{};
+ }
+
+ /**
* Set Messaging data
* @param extras Bundle contains messaging data
*/
@@ -417,22 +440,44 @@
return mPeopleHelper.createAvatarSymbol(senderName, symbol, layoutColor);
}
- @RemotableViewMethod
+ @RemotableViewMethod(asyncImpl = "setLayoutColorAsync")
public void setLayoutColor(int color) {
mLayoutColor = color;
}
- @RemotableViewMethod
+ /**
+ * @hide
+ */
+ public Runnable setLayoutColorAsync(int color) {
+ mLayoutColor = color;
+ return () -> {};
+ }
+
+ @RemotableViewMethod(asyncImpl = "setIsOneToOneAsync")
public void setIsOneToOne(boolean oneToOne) {
mIsOneToOne = oneToOne;
}
- @RemotableViewMethod
+ /**
+ * @hide
+ */
+ public Runnable setIsOneToOneAsync(boolean oneToOne) {
+ mIsOneToOne = oneToOne;
+ return () -> {};
+ }
+
+ @RemotableViewMethod(asyncImpl = "setSenderTextColorAsync")
public void setSenderTextColor(int color) {
mSenderTextColor = color;
}
-
+ /**
+ * @hide
+ */
+ public Runnable setSenderTextColorAsync(int color) {
+ mSenderTextColor = color;
+ return () -> {};
+ }
/**
* @param color the color of the notification background
*/
@@ -441,11 +486,19 @@
// Nothing to do with this
}
- @RemotableViewMethod
+ @RemotableViewMethod(asyncImpl = "setMessageTextColorAsync")
public void setMessageTextColor(int color) {
mMessageTextColor = color;
}
+ /**
+ * @hide
+ */
+ public Runnable setMessageTextColorAsync(int color) {
+ mMessageTextColor = color;
+ return () -> {};
+ }
+
public void setUser(Person user) {
mUser = user;
if (mUser.getIcon() == null) {
diff --git a/core/jni/android_app_PropertyInvalidatedCache.cpp b/core/jni/android_app_PropertyInvalidatedCache.cpp
index 12585d5..6267522 100644
--- a/core/jni/android_app_PropertyInvalidatedCache.cpp
+++ b/core/jni/android_app_PropertyInvalidatedCache.cpp
@@ -35,7 +35,7 @@
return kMaxNonce;
}
-size_t NonceStore::getMaxByte() const {
+int32_t NonceStore::getMaxByte() const {
return kMaxByte;
}
@@ -68,13 +68,13 @@
}
// Copy the byte block to the target and return the current hash.
-int32_t NonceStore::getByteBlock(block_t* block, size_t len) const {
+int32_t NonceStore::getByteBlock(block_t* block, int32_t len) const {
memcpy(block, (void*) byteBlock(), std::min(kMaxByte, len));
return mByteHash;
}
// Set the byte block and the hash.
-void NonceStore::setByteBlock(int hash, const block_t* block, size_t len) {
+void NonceStore::setByteBlock(int hash, const block_t* block, int32_t len) {
memcpy((void*) byteBlock(), block, len = std::min(kMaxByte, len));
mByteHash = hash;
}
diff --git a/core/jni/android_app_PropertyInvalidatedCache.h b/core/jni/android_app_PropertyInvalidatedCache.h
index 54a4ac6..1d75182 100644
--- a/core/jni/android_app_PropertyInvalidatedCache.h
+++ b/core/jni/android_app_PropertyInvalidatedCache.h
@@ -27,8 +27,12 @@
* location. Fields with a variable location are found via offsets. The offsets make this
* object position-independent, which is required because it is in shared memory and would be
* mapped into different virtual addresses for different processes.
+ *
+ * This structure is shared between 64-bit and 32-bit processes. Therefore it is imperative
+ * that the structure not use any datatypes that are architecture-dependent (like size_t).
+ * Additionally, care must be taken to avoid unexpected padding in the structure.
*/
-class NonceStore {
+class alignas(8) NonceStore {
protected:
// A convenient typedef. The jbyteArray element type is jbyte, which the compiler treats as
// signed char.
@@ -43,23 +47,27 @@
// The value of an unset field.
static constexpr int UNSET = 0;
- // The size of the nonce array.
+ // The size of the nonce array. This and the following sizes are int32_t to
+ // be ABI independent.
const int32_t kMaxNonce;
// The size of the byte array.
- const size_t kMaxByte;
+ const int32_t kMaxByte;
// The offset to the nonce array.
- const size_t mNonceOffset;
+ const int32_t mNonceOffset;
// The offset to the byte array.
- const size_t mByteOffset;
+ const int32_t mByteOffset;
// The byte block hash. This is fixed and at a known offset, so leave it in the base class.
volatile std::atomic<int32_t> mByteHash;
+ // A 4-byte padd that makes the size of this structure a multiple of 8 bytes.
+ const int32_t _pad = 0;
+
// The constructor is protected! It only makes sense when called from a subclass.
- NonceStore(int kMaxNonce, size_t kMaxByte, volatile nonce_t* nonce, volatile block_t* block) :
+ NonceStore(int kMaxNonce, int kMaxByte, volatile nonce_t* nonce, volatile block_t* block) :
kMaxNonce(kMaxNonce),
kMaxByte(kMaxByte),
mNonceOffset(offset(this, const_cast<nonce_t*>(nonce))),
@@ -70,7 +78,7 @@
// These provide run-time access to the sizing parameters.
int getMaxNonce() const;
- size_t getMaxByte() const;
+ int getMaxByte() const;
// 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
@@ -86,10 +94,10 @@
int32_t getHash() const;
// Copy the byte block to the target and return the current hash.
- int32_t getByteBlock(block_t* block, size_t len) const;
+ int32_t getByteBlock(block_t* block, int32_t len) const;
// Set the byte block and the hash.
- void setByteBlock(int hash, const block_t* block, size_t len);
+ void setByteBlock(int hash, const block_t* block, int32_t len);
private:
@@ -113,6 +121,12 @@
}
};
+// Assert that the size of the object is fixed, independent of the CPU architecture. There are
+// four int32_t fields and one atomic<int32_t>, which sums to 20 bytes total. This assertion
+// uses a constant instead of computing the size of the objects in the compiler, to avoid
+// different answers on different architectures.
+static_assert(sizeof(NonceStore) == 24);
+
/**
* 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
@@ -126,20 +140,22 @@
* 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 : public NonceStore {
+template<int MAX_NONCE, int MAX_BYTE> class CacheNonce : public NonceStore {
// The array of nonces
- volatile nonce_t mNonce[maxNonce];
+ volatile nonce_t mNonce[MAX_NONCE];
// The byte array. This is not atomic but it is guarded by the mByteHash.
- volatile block_t mByteBlock[maxByte];
+ volatile block_t mByteBlock[MAX_BYTE];
public:
+ // Export the parameters for use in compiler assertions.
+ static constexpr int kMaxNonceCount = MAX_NONCE;
+ static constexpr int kMaxByteCount = MAX_BYTE;
+
// Construct and initialize the memory.
- CacheNonce() :
- NonceStore(maxNonce, maxByte, &mNonce[0], &mByteBlock[0])
- {
- for (int i = 0; i < maxNonce; i++) {
+ CacheNonce() : NonceStore(MAX_NONCE, MAX_BYTE, &mNonce[0], &mByteBlock[0]) {
+ for (int i = 0; i < MAX_NONCE; i++) {
mNonce[i] = UNSET;
}
mByteHash = UNSET;
@@ -155,4 +171,10 @@
typedef CacheNonce</* max nonce */ 128, /* byte block size */ 8192> SystemCacheNonce;
// LINT.ThenChange(/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java:system_nonce_config)
+// Verify that there is no padding in the final class.
+static_assert(sizeof(SystemCacheNonce) ==
+ sizeof(NonceStore)
+ + SystemCacheNonce::kMaxNonceCount*8
+ + SystemCacheNonce::kMaxByteCount);
+
} // namespace android.app.PropertyInvalidatedCache
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index b99b0ef..a8e51a7 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -765,28 +765,28 @@
std::vector<int32_t> dimensions;
std::vector<int32_t> sizes;
std::vector<int32_t> samplingKeys;
- int32_t fd = -1;
+ base::unique_fd fd;
if (jdimensionArray) {
jsize numLuts = env->GetArrayLength(jdimensionArray);
- ScopedIntArrayRW joffsets(env, joffsetArray);
+ ScopedIntArrayRO joffsets(env, joffsetArray);
if (joffsets.get() == nullptr) {
- jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from joffsetArray");
+ jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRO from joffsetArray");
return;
}
- ScopedIntArrayRW jdimensions(env, jdimensionArray);
+ ScopedIntArrayRO jdimensions(env, jdimensionArray);
if (jdimensions.get() == nullptr) {
- jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jdimensionArray");
+ jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRO from jdimensionArray");
return;
}
- ScopedIntArrayRW jsizes(env, jsizeArray);
+ ScopedIntArrayRO jsizes(env, jsizeArray);
if (jsizes.get() == nullptr) {
- jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jsizeArray");
+ jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRO from jsizeArray");
return;
}
- ScopedIntArrayRW jsamplingKeys(env, jsamplingKeyArray);
+ ScopedIntArrayRO jsamplingKeys(env, jsamplingKeyArray);
if (jsamplingKeys.get() == nullptr) {
- jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jsamplingKeyArray");
+ jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRO from jsamplingKeyArray");
return;
}
@@ -796,15 +796,15 @@
sizes = std::vector<int32_t>(jsizes.get(), jsizes.get() + numLuts);
samplingKeys = std::vector<int32_t>(jsamplingKeys.get(), jsamplingKeys.get() + numLuts);
- ScopedFloatArrayRW jbuffers(env, jbufferArray);
+ ScopedFloatArrayRO jbuffers(env, jbufferArray);
if (jbuffers.get() == nullptr) {
- jniThrowRuntimeException(env, "Failed to get ScopedFloatArrayRW from jbufferArray");
+ jniThrowRuntimeException(env, "Failed to get ScopedFloatArrayRO from jbufferArray");
return;
}
// create the shared memory and copy jbuffers
size_t bufferSize = jbuffers.size() * sizeof(float);
- fd = ashmem_create_region("lut_shared_mem", bufferSize);
+ fd.reset(ashmem_create_region("lut_shared_mem", bufferSize));
if (fd < 0) {
jniThrowRuntimeException(env, "ashmem_create_region() failed");
return;
@@ -820,7 +820,7 @@
}
}
- transaction->setLuts(ctrl, base::unique_fd(fd), offsets, dimensions, sizes, samplingKeys);
+ transaction->setLuts(ctrl, std::move(fd), offsets, dimensions, sizes, samplingKeys);
}
static void nativeSetPictureProfileId(JNIEnv* env, jclass clazz, jlong transactionObj,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DisplayDeskState.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DisplayDeskState.aidl
index 59add47..5f45192 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DisplayDeskState.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DisplayDeskState.aidl
@@ -18,13 +18,11 @@
/**
* Defines the state of desks on a display whose ID is `displayId`, which is:
- * - `canCreateDesks`: whether it's possible to create new desks on this display.
* - `activeDeskId`: the currently active desk Id, or `-1` if none is active.
* - `deskId`: the list of desk Ids of the available desks on this display.
*/
parcelable DisplayDeskState {
int displayId;
- boolean canCreateDesk;
int activeDeskId;
int[] deskIds;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
index 7ed1581..cefbd89 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
@@ -28,7 +28,7 @@
* Called once when the listener first gets connected to initialize it with the current state of
* desks in Shell.
*/
- void onListenerConnected(in DisplayDeskState[] displayDeskStates);
+ void onListenerConnected(in DisplayDeskState[] displayDeskStates, boolean canCreateDesks);
/** Desktop tasks visibility has changed. Visible if at least 1 task is visible. */
void onTasksVisibilityChanged(int displayId, int visibleTasksCount);
@@ -49,10 +49,10 @@
void onExitDesktopModeTransitionStarted(int transitionDuration);
/**
- * Called when the conditions that allow the creation of a new desk on the display whose ID is
- * `displayId` changes to `canCreateDesks`. It's also called when a new display is added.
+ * Called when the conditions that allow the creation of a new desk changes. This is a global
+ * state for the entire device.
*/
- void onCanCreateDesksChanged(int displayId, boolean canCreateDesks);
+ void onCanCreateDesksChanged(boolean canCreateDesks);
/** Called when a desk whose ID is `deskId` is added to the display whose ID is `displayId`. */
void onDeskAdded(int displayId, int deskId);
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 4fe0b80..2759724 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -715,47 +715,45 @@
sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
- int fd = -1;
+ base::unique_fd fd;
std::vector<int32_t> offsets;
std::vector<int32_t> dimensions;
std::vector<int32_t> sizes;
std::vector<int32_t> samplingKeys;
if (luts) {
- std::vector<float> buffer(luts->totalBufferSize);
int32_t count = luts->offsets.size();
offsets = luts->offsets;
dimensions.reserve(count);
sizes.reserve(count);
samplingKeys.reserve(count);
- for (int32_t i = 0; i < count; i++) {
- dimensions.emplace_back(luts->entries[i]->properties.dimension);
- sizes.emplace_back(luts->entries[i]->properties.size);
- samplingKeys.emplace_back(luts->entries[i]->properties.samplingKey);
- std::copy(luts->entries[i]->buffer.data.begin(), luts->entries[i]->buffer.data.end(),
- buffer.begin() + offsets[i]);
- }
// mmap
- fd = ashmem_create_region("lut_shared_mem", luts->totalBufferSize * sizeof(float));
+ fd.reset(ashmem_create_region("lut_shared_mem", luts->totalBufferSize * sizeof(float)));
if (fd < 0) {
LOG_ALWAYS_FATAL("setLuts, ashmem_create_region() failed");
return;
}
- void* ptr = mmap(nullptr, luts->totalBufferSize * sizeof(float), PROT_READ | PROT_WRITE,
- MAP_SHARED, fd, 0);
+ float* ptr = (float*)mmap(nullptr, luts->totalBufferSize * sizeof(float),
+ PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED) {
LOG_ALWAYS_FATAL("setLuts, Failed to map the shared memory");
return;
}
- memcpy(ptr, buffer.data(), luts->totalBufferSize * sizeof(float));
+ for (int32_t i = 0; i < count; i++) {
+ dimensions.emplace_back(luts->entries[i]->properties.dimension);
+ sizes.emplace_back(luts->entries[i]->properties.size);
+ samplingKeys.emplace_back(luts->entries[i]->properties.samplingKey);
+ std::copy(luts->entries[i]->buffer.data.begin(), luts->entries[i]->buffer.data.end(),
+ ptr + offsets[i]);
+ }
+
munmap(ptr, luts->totalBufferSize * sizeof(float));
}
- transaction->setLuts(surfaceControl, base::unique_fd(fd), offsets, dimensions, sizes,
- samplingKeys);
+ transaction->setLuts(surfaceControl, std::move(fd), offsets, dimensions, sizes, samplingKeys);
}
void ASurfaceTransaction_setColor(ASurfaceTransaction* aSurfaceTransaction,
diff --git a/packages/CtsShim/build/Android.bp b/packages/CtsShim/build/Android.bp
index bd89263..853d1ad 100644
--- a/packages/CtsShim/build/Android.bp
+++ b/packages/CtsShim/build/Android.bp
@@ -152,34 +152,6 @@
}
//##########################################################
-// Variant: System app upgrade
-
-android_app {
- name: "CtsShimUpgrade",
-
- sdk_version: "current",
- optimize: {
- enabled: false,
- },
- dex_preopt: {
- enabled: false,
- },
-
- manifest: "shim/AndroidManifestUpgrade.xml",
- min_sdk_version: "24",
-}
-
-java_genrule {
- name: "generate_shim_manifest",
- srcs: [
- "shim/AndroidManifest.xml",
- ":CtsShimUpgrade",
- ],
- out: ["AndroidManifest.xml"],
- cmd: "sed -e s/__HASH__/`sha512sum -b $(location :CtsShimUpgrade) | cut -d' ' -f1`/ $(location shim/AndroidManifest.xml) > $(out)",
-}
-
-//##########################################################
// Variant: System app
android_app {
@@ -193,7 +165,7 @@
enabled: false,
},
- manifest: ":generate_shim_manifest",
+ manifest: "shim/AndroidManifest.xml",
apex_available: [
"//apex_available:platform",
"com.android.apex.cts.shim.v1",
diff --git a/packages/CtsShim/build/shim/AndroidManifest.xml b/packages/CtsShim/build/shim/AndroidManifest.xml
index 3b8276b6..1ffe56c 100644
--- a/packages/CtsShim/build/shim/AndroidManifest.xml
+++ b/packages/CtsShim/build/shim/AndroidManifest.xml
@@ -17,15 +17,13 @@
<!-- Manifest for the system CTS shim -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- package="com.android.cts.ctsshim"
- android:sharedUserId="com.android.cts.ctsshim" >
+ package="com.android.cts.ctsshim" >
- <uses-sdk
- android:minSdkVersion="24"
+ <uses-sdk android:minSdkVersion="24"
android:targetSdkVersion="28" />
<restrict-update
- android:hash="__HASH__" />
+ android:hash="__CAN_NOT_BE_UPDATED__" />
<application
android:hasCode="false"
diff --git a/packages/CtsShim/build/shim/AndroidManifestUpgrade.xml b/packages/CtsShim/build/shim/AndroidManifestUpgrade.xml
deleted file mode 100644
index 7f3644a..0000000
--- a/packages/CtsShim/build/shim/AndroidManifestUpgrade.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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 for the system CTS shim -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- package="com.android.cts.ctsshim" >
-
- <uses-sdk
- android:minSdkVersion="24"
- android:targetSdkVersion="28" />
-
- <application
- android:hasCode="false"
- tools:ignore="AllowBackup,MissingApplicationIcon" />
-</manifest>
-
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/themes.xml
index dc5c9b2..b6e80c7 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v35/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v35/themes.xml
@@ -18,7 +18,7 @@
<resources>
<style name="Theme.SettingsBase_v35" parent="Theme.SettingsBase_v33" >
<item name="android:colorAccent">@color/settingslib_materialColorPrimary</item>
- <item name="android:colorBackground">@color/settingslib_materialColorSurfaceContainerLowest</item>
+ <item name="android:colorBackground">@color/settingslib_materialColorSurfaceContainer</item>
<item name="android:textColorPrimary">@color/settingslib_materialColorOnSurface</item>
<item name="android:textColorSecondary">@color/settingslib_text_color_secondary</item>
<item name="android:textColorTertiary">@color/settingslib_materialColorOutline</item>
diff --git a/packages/SettingsLib/res/drawable/ic_mobile_0_4_bar_error.xml b/packages/SettingsLib/res/drawable/ic_mobile_0_4_bar_error.xml
index 6015be8..aa5e9d2 100644
--- a/packages/SettingsLib/res/drawable/ic_mobile_0_4_bar_error.xml
+++ b/packages/SettingsLib/res/drawable/ic_mobile_0_4_bar_error.xml
@@ -23,10 +23,10 @@
<clip-path
android:pathData="
M0,0
- V13.5,0
- H13.5,20
- V0,20
- H0,0
+ H16
+ V12
+ H0
+ Z
M14.999,13.5C17.761,13.5 19.999,11.261 19.999,8.5C19.999,5.739 17.761,3.5 14.999,3.5C12.238,3.5 9.999,5.739 9.999,8.5C9.999,11.261 12.238,13.5 14.999,13.5Z" />
<path
android:pathData="M1.249,7C1.939,7 2.499,7.56 2.499,8.25L2.499,10.75C2.499,11.44 1.939,12 1.249,12C0.559,12 -0.001,11.44 -0.001,10.75L-0.001,8.25C-0.001,7.56 0.559,7 1.249,7Z"
diff --git a/packages/SettingsLib/res/drawable/ic_mobile_1_4_bar_error.xml b/packages/SettingsLib/res/drawable/ic_mobile_1_4_bar_error.xml
index fb73b6b..f7bf851 100644
--- a/packages/SettingsLib/res/drawable/ic_mobile_1_4_bar_error.xml
+++ b/packages/SettingsLib/res/drawable/ic_mobile_1_4_bar_error.xml
@@ -8,10 +8,10 @@
<clip-path
android:pathData="
M0,0
- V13.5,0
- H13.5,20
- V0,20
- H0,0
+ H16
+ V12
+ H0
+ Z
M14.999,13.5C17.761,13.5 19.999,11.261 19.999,8.5C19.999,5.739 17.761,3.5 14.999,3.5C12.238,3.5 9.999,5.739 9.999,8.5C9.999,11.261 12.238,13.5 14.999,13.5Z" />
<path
android:pathData="M1.249,7C1.939,7 2.499,7.56 2.499,8.25L2.499,10.75C2.499,11.44 1.939,12 1.249,12C0.559,12 -0.001,11.44 -0.001,10.75L-0.001,8.25C-0.001,7.56 0.559,7 1.249,7Z"
diff --git a/packages/SettingsLib/res/drawable/ic_mobile_3_4_bar_error.xml b/packages/SettingsLib/res/drawable/ic_mobile_3_4_bar_error.xml
index 7c4c1c6..016c614 100644
--- a/packages/SettingsLib/res/drawable/ic_mobile_3_4_bar_error.xml
+++ b/packages/SettingsLib/res/drawable/ic_mobile_3_4_bar_error.xml
@@ -23,10 +23,10 @@
<clip-path
android:pathData="
M0,0
- V13.5,0
- H13.5,20
- V0,20
- H0,0
+ H16
+ V12
+ H0
+ Z
M14.999,13.5C17.761,13.5 19.999,11.261 19.999,8.5C19.999,5.739 17.761,3.5 14.999,3.5C12.238,3.5 9.999,5.739 9.999,8.5C9.999,11.261 12.238,13.5 14.999,13.5Z" />
<path
android:pathData="M1.249,7C1.939,7 2.499,7.56 2.499,8.25L2.499,10.75C2.499,11.44 1.939,12 1.249,12C0.559,12 -0.001,11.44 -0.001,10.75L-0.001,8.25C-0.001,7.56 0.559,7 1.249,7Z"
diff --git a/packages/SettingsLib/res/drawable/ic_mobile_4_4_bar_error.xml b/packages/SettingsLib/res/drawable/ic_mobile_4_4_bar_error.xml
index d23680d..b830415 100644
--- a/packages/SettingsLib/res/drawable/ic_mobile_4_4_bar_error.xml
+++ b/packages/SettingsLib/res/drawable/ic_mobile_4_4_bar_error.xml
@@ -23,10 +23,10 @@
<clip-path
android:pathData="
M0,0
- V13.5,0
- H13.5,20
- V0,20
- H0,0
+ H16
+ V12
+ H0
+ Z
M14.999,13.5C17.761,13.5 19.999,11.261 19.999,8.5C19.999,5.739 17.761,3.5 14.999,3.5C12.238,3.5 9.999,5.739 9.999,8.5C9.999,11.261 12.238,13.5 14.999,13.5Z" />
<path
android:pathData="M1.249,7C1.939,7 2.499,7.56 2.499,8.25L2.499,10.75C2.499,11.44 1.939,12 1.249,12C0.559,12 -0.001,11.44 -0.001,10.75L-0.001,8.25C-0.001,7.56 0.559,7 1.249,7Z"
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index 5e12ee1..db9035b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -65,6 +65,7 @@
import com.android.compose.animation.scene.animateElementFloatAsState
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.modifiers.thenIf
+import com.android.compose.theme.colorAttr
import com.android.settingslib.Utils
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
@@ -75,8 +76,6 @@
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.ui.composable.ShadeHeader.Colors.chipBackground
-import com.android.systemui.shade.ui.composable.ShadeHeader.Colors.chipHighlighted
import com.android.systemui.shade.ui.composable.ShadeHeader.Colors.onScrimDim
import com.android.systemui.shade.ui.composable.ShadeHeader.Dimensions.ChipPaddingHorizontal
import com.android.systemui.shade.ui.composable.ShadeHeader.Dimensions.ChipPaddingVertical
@@ -107,7 +106,7 @@
object Dimensions {
val CollapsedHeight = 48.dp
val ExpandedHeight = 120.dp
- val ChipPaddingHorizontal = 8.dp
+ val ChipPaddingHorizontal = 6.dp
val ChipPaddingVertical = 4.dp
}
@@ -117,12 +116,6 @@
val ColorScheme.onScrimDim: Color
get() = Color.DarkGray
-
- val ColorScheme.chipBackground: Color
- get() = Color.DarkGray
-
- val ColorScheme.chipHighlighted: Color
- get() = Color.LightGray
}
object TestTags {
@@ -165,7 +158,7 @@
VariableDayDate(
longerDateText = viewModel.longerDateText,
shorterDateText = viewModel.shorterDateText,
- chipHighlight = viewModel.notificationsChipHighlight,
+ textColor = colorAttr(android.R.attr.textColorPrimary),
modifier = Modifier.element(ShadeHeader.Elements.CollapsedContentStart),
)
}
@@ -265,7 +258,7 @@
VariableDayDate(
longerDateText = viewModel.longerDateText,
shorterDateText = viewModel.shorterDateText,
- chipHighlight = viewModel.notificationsChipHighlight,
+ textColor = colorAttr(android.R.attr.textColorPrimary),
modifier = Modifier.widthIn(max = 90.dp),
)
Spacer(modifier = Modifier.weight(1f))
@@ -310,6 +303,7 @@
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(horizontal = horizontalPadding),
) {
+ val chipHighlight = viewModel.notificationsChipHighlight
if (isShadeLayoutWide) {
Clock(
scale = 1f,
@@ -319,13 +313,13 @@
Spacer(modifier = Modifier.width(5.dp))
}
NotificationsChip(
- chipHighlight = viewModel.notificationsChipHighlight,
onClick = viewModel::onNotificationIconChipClicked,
+ backgroundColor = chipHighlight.backgroundColor(MaterialTheme.colorScheme),
) {
VariableDayDate(
longerDateText = viewModel.longerDateText,
shorterDateText = viewModel.shorterDateText,
- chipHighlight = viewModel.notificationsChipHighlight,
+ textColor = chipHighlight.foregroundColor(MaterialTheme.colorScheme),
)
}
}
@@ -338,14 +332,13 @@
) {
val chipHighlight = viewModel.quickSettingsChipHighlight
SystemIconChip(
- chipHighlight = chipHighlight,
+ backgroundColor = chipHighlight.backgroundColor(MaterialTheme.colorScheme),
onClick = viewModel::onSystemIconChipClicked,
) {
StatusIcons(
viewModel = viewModel,
useExpandedFormat = false,
modifier = Modifier.padding(end = 6.dp).weight(1f, fill = false),
- chipHighlight = chipHighlight,
)
BatteryIcon(
createBatteryMeterViewController =
@@ -515,6 +508,7 @@
batteryIcon.setPercentShowMode(
if (useExpandedFormat) BatteryMeterView.MODE_ESTIMATE else BatteryMeterView.MODE_ON
)
+ // TODO(b/397223606): Get the actual spec for this.
if (chipHighlight is HeaderChipHighlight.Strong) {
batteryIcon.updateColors(primaryColor, inverseColor, inverseColor)
} else if (chipHighlight is HeaderChipHighlight.Weak) {
@@ -553,7 +547,6 @@
viewModel: ShadeHeaderViewModel,
useExpandedFormat: Boolean,
modifier: Modifier = Modifier,
- chipHighlight: HeaderChipHighlight = HeaderChipHighlight.None,
) {
val localContext = LocalContext.current
val themedContext =
@@ -581,6 +574,8 @@
viewModel.createTintedIconManager(iconContainer, StatusBarLocation.QS)
}
+ val chipHighlight = viewModel.quickSettingsChipHighlight
+
AndroidView(
factory = { context ->
iconManager.setTint(primaryColor, inverseColor)
@@ -617,6 +612,7 @@
iconContainer.removeIgnoredSlot(locationSlot)
}
+ // TODO(b/397223606): Get the actual spec for this.
if (chipHighlight is HeaderChipHighlight.Strong) {
iconManager.setTint(inverseColor, primaryColor)
} else if (chipHighlight is HeaderChipHighlight.Weak) {
@@ -629,16 +625,12 @@
@Composable
private fun NotificationsChip(
- chipHighlight: HeaderChipHighlight,
onClick: () -> Unit,
modifier: Modifier = Modifier,
+ backgroundColor: Color = Color.Unspecified,
content: @Composable BoxScope.() -> Unit,
) {
val interactionSource = remember { MutableInteractionSource() }
- val chipBackground =
- with(MaterialTheme.colorScheme) {
- if (chipHighlight is HeaderChipHighlight.Strong) chipHighlighted else chipBackground
- }
Box(
modifier =
modifier
@@ -647,7 +639,7 @@
indication = null,
onClick = onClick,
)
- .background(chipBackground, RoundedCornerShape(25.dp))
+ .background(backgroundColor, RoundedCornerShape(25.dp))
.padding(horizontal = ChipPaddingHorizontal, vertical = ChipPaddingVertical)
) {
content()
@@ -657,7 +649,7 @@
@Composable
private fun SystemIconChip(
modifier: Modifier = Modifier,
- chipHighlight: HeaderChipHighlight = HeaderChipHighlight.None,
+ backgroundColor: Color = Color.Unspecified,
onClick: (() -> Unit)? = null,
content: @Composable RowScope.() -> Unit,
) {
@@ -667,16 +659,12 @@
with(MaterialTheme.colorScheme) {
Modifier.background(onScrimDim, RoundedCornerShape(CollapsedHeight / 4))
}
- val backgroundColor =
- with(MaterialTheme.colorScheme) {
- if (chipHighlight is HeaderChipHighlight.Strong) chipHighlighted else chipBackground
- }
Row(
verticalAlignment = Alignment.CenterVertically,
modifier =
modifier
- .thenIf(chipHighlight !is HeaderChipHighlight.None) {
+ .thenIf(backgroundColor != Color.Unspecified) {
Modifier.background(backgroundColor, RoundedCornerShape(25.dp))
.padding(horizontal = ChipPaddingHorizontal, vertical = ChipPaddingVertical)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt
index 64aada5..8fbd051 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt
@@ -4,22 +4,16 @@
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.Layout
-import com.android.compose.theme.colorAttr
-import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel.HeaderChipHighlight
@Composable
fun VariableDayDate(
longerDateText: String,
shorterDateText: String,
- chipHighlight: HeaderChipHighlight,
+ textColor: Color,
modifier: Modifier = Modifier,
) {
- val textColor =
- if (chipHighlight is HeaderChipHighlight.Strong)
- colorAttr(android.R.attr.textColorPrimaryInverse)
- else colorAttr(android.R.attr.textColorPrimary)
-
Layout(
contents =
listOf(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
index 4e14fec..943ada9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
@@ -18,6 +18,9 @@
import android.animation.Animator
import android.animation.ObjectAnimator
+import android.icu.text.MeasureFormat
+import android.icu.util.Measure
+import android.icu.util.MeasureUnit
import android.testing.TestableLooper
import android.view.View
import android.widget.SeekBar
@@ -30,6 +33,7 @@
import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
import com.android.systemui.res.R
import com.google.common.truth.Truth.assertThat
+import java.util.Locale
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -61,11 +65,11 @@
fun setUp() {
context.orCreateTestableResources.addOverride(
R.dimen.qs_media_enabled_seekbar_height,
- enabledHeight
+ enabledHeight,
)
context.orCreateTestableResources.addOverride(
R.dimen.qs_media_disabled_seekbar_height,
- disabledHeight
+ disabledHeight,
)
seekBarView = SeekBar(context)
@@ -110,14 +114,31 @@
@Test
fun seekBarProgress() {
+ val elapsedTime = 3000
+ val duration = (1.5 * 60 * 60 * 1000).toInt()
// WHEN part of the track has been played
- val data = SeekBarViewModel.Progress(true, true, true, false, 3000, 120000, true)
+ val data = SeekBarViewModel.Progress(true, true, true, false, elapsedTime, duration, true)
observer.onChanged(data)
// THEN seek bar shows the progress
- assertThat(seekBarView.progress).isEqualTo(3000)
- assertThat(seekBarView.max).isEqualTo(120000)
+ assertThat(seekBarView.progress).isEqualTo(elapsedTime)
+ assertThat(seekBarView.max).isEqualTo(duration)
- val desc = context.getString(R.string.controls_media_seekbar_description, "00:03", "02:00")
+ val expectedProgress =
+ MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
+ .formatMeasures(Measure(3, MeasureUnit.SECOND))
+ val expectedDuration =
+ MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
+ .formatMeasures(
+ Measure(1, MeasureUnit.HOUR),
+ Measure(30, MeasureUnit.MINUTE),
+ Measure(0, MeasureUnit.SECOND),
+ )
+ val desc =
+ context.getString(
+ R.string.controls_media_seekbar_description,
+ expectedProgress,
+ expectedDuration,
+ )
assertThat(seekBarView.contentDescription).isEqualTo(desc)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryTest.kt
index 426af26..83e26c4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryTest.kt
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.collection
+import android.app.Notification
+import android.graphics.Color
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.testing.TestableLooper.RunWithLooper
@@ -72,4 +74,35 @@
fun getKey_adapter() {
assertThat(underTest.entryAdapter.key).isEqualTo("key")
}
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun isClearable_adapter() {
+ assertThat(underTest.entryAdapter.isClearable).isTrue()
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun getSummarization_adapter() {
+ assertThat(underTest.entryAdapter.summarization).isNull()
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun getContrastedColor_adapter() {
+ assertThat(underTest.entryAdapter.getContrastedColor(context, false, Color.WHITE))
+ .isEqualTo(Notification.COLOR_DEFAULT)
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun canPeek_adapter() {
+ assertThat(underTest.entryAdapter.canPeek()).isFalse()
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun getWhen_adapter() {
+ assertThat(underTest.entryAdapter.`when`).isEqualTo(0)
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
similarity index 89%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
index 19d1224..1f5c672 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -163,9 +163,10 @@
@Test
public void testIsExemptFromDndVisualSuppression_media() {
+ MediaSession session = new MediaSession(mContext, "test");
Notification.Builder n = new Notification.Builder(mContext, "")
.setStyle(new Notification.MediaStyle()
- .setMediaSession(mock(MediaSession.Token.class)))
+ .setMediaSession(session.getSessionToken()))
.setSmallIcon(R.drawable.ic_person)
.setContentTitle("Title")
.setContentText("Text");
@@ -593,6 +594,76 @@
assertThat(entry.getEntryAdapter().getGroupRoot()).isEqualTo(parent.getEntryAdapter());
}
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ public void isClearable_adapter() {
+ ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ Notification notification = new Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .build();
+
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_NAME)
+ .setOpPkg(TEST_PACKAGE_NAME)
+ .setUid(TEST_UID)
+ .setChannel(mChannel)
+ .setId(mId++)
+ .setNotification(notification)
+ .setUser(new UserHandle(ActivityManager.getCurrentUser()))
+ .build();
+ entry.setRow(row);
+
+ assertThat(entry.getEntryAdapter().isClearable()).isEqualTo(entry.isClearable());
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ public void getSummarization_adapter() {
+ ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ Notification notification = new Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .build();
+
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_NAME)
+ .setOpPkg(TEST_PACKAGE_NAME)
+ .setUid(TEST_UID)
+ .setChannel(mChannel)
+ .setId(mId++)
+ .setNotification(notification)
+ .setUser(new UserHandle(ActivityManager.getCurrentUser()))
+ .build();
+ Ranking ranking = new RankingBuilder(entry.getRanking())
+ .setSummarization("hello")
+ .build();
+ entry.setRanking(ranking);
+ entry.setRow(row);
+
+ assertThat(entry.getEntryAdapter().getSummarization()).isEqualTo("hello");
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ public void getIcons_adapter() {
+ ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ Notification notification = new Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .build();
+
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_NAME)
+ .setOpPkg(TEST_PACKAGE_NAME)
+ .setUid(TEST_UID)
+ .setChannel(mChannel)
+ .setId(mId++)
+ .setNotification(notification)
+ .setUser(new UserHandle(ActivityManager.getCurrentUser()))
+ .build();
+ entry.setRow(row);
+
+ assertThat(entry.getEntryAdapter().getIcons()).isEqualTo(entry.getIcons());
+ }
+
private Notification.Action createContextualAction(String title) {
return new Notification.Action.Builder(
Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt
index 7781df1..43cb957 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt
@@ -16,8 +16,11 @@
package com.android.systemui.statusbar.notification.collection.coordinator
import android.app.Notification
+import android.app.Notification.MediaStyle
+import android.media.session.MediaSession
import android.platform.test.flag.junit.FlagsParameterization
import android.provider.Settings
+import android.service.notification.StatusBarNotification
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.dumpManager
@@ -36,6 +39,7 @@
import com.android.systemui.scene.data.repository.setTransition
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.statusbar.SbnBuilder
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
@@ -217,6 +221,16 @@
mock<ExpandableNotificationRow>().apply {
whenever(isMediaRow).thenReturn(true)
}
+ sbn = SbnBuilder().setNotification(
+ Notification.Builder(context, "channel").setStyle(
+ MediaStyle().setMediaSession(
+ MediaSession(
+ context,
+ "tag"
+ ).sessionToken
+ )
+ ).build()
+ ).build()
}
collectionListener.onEntryAdded(fakeEntry)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
index 4c1f4f1..1b8d64d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
@@ -80,9 +80,6 @@
import com.android.systemui.testKosmos
import com.android.systemui.util.kotlin.JavaAdapter
import com.android.systemui.wmshell.BubblesManager
-import java.util.Optional
-import kotlin.test.assertNotNull
-import kotlin.test.fail
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
@@ -110,6 +107,9 @@
import org.mockito.kotlin.whenever
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
+import java.util.Optional
+import kotlin.test.assertNotNull
+import kotlin.test.fail
/** Tests for [NotificationGutsManager]. */
@SmallTest
@@ -509,7 +509,6 @@
.setImportance(NotificationManager.IMPORTANCE_HIGH)
.build()
- whenever(row.isNonblockable).thenReturn(false)
whenever(highPriorityProvider.isHighPriority(entry)).thenReturn(true)
val statusBarNotification = entry.sbn
gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -546,7 +545,6 @@
NotificationEntryHelper.modifyRanking(row.entry)
.setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE)
.build()
- whenever(row.isNonblockable).thenReturn(false)
val statusBarNotification = row.entry.sbn
val entry = row.entry
@@ -586,7 +584,6 @@
NotificationEntryHelper.modifyRanking(row.entry)
.setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE)
.build()
- whenever(row.isNonblockable).thenReturn(false)
val statusBarNotification = row.entry.sbn
val entry = row.entry
@@ -641,7 +638,7 @@
): NotificationMenuRowPlugin.MenuItem {
val menuRow: NotificationMenuRowPlugin =
NotificationMenuRow(mContext, peopleNotificationIdentifier)
- menuRow.createMenu(row, row.entry.sbn)
+ menuRow.createMenu(row)
val menuItem = menuRow.getLongpressMenuItem(mContext)
assertNotNull(menuItem)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
index 027e899..9fdfca1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
@@ -72,7 +72,7 @@
public void testAttachDetach() {
NotificationMenuRowPlugin row =
new NotificationMenuRow(mContext, mPeopleNotificationIdentifier);
- row.createMenu(mRow, null);
+ row.createMenu(mRow);
ViewUtils.attachView(row.getMenuView());
TestableLooper.get(this).processAllMessages();
ViewUtils.detachView(row.getMenuView());
@@ -83,9 +83,9 @@
public void testRecreateMenu() {
NotificationMenuRowPlugin row =
new NotificationMenuRow(mContext, mPeopleNotificationIdentifier);
- row.createMenu(mRow, null);
+ row.createMenu(mRow);
assertTrue(row.getMenuView() != null);
- row.createMenu(mRow, null);
+ row.createMenu(mRow);
assertTrue(row.getMenuView() != null);
}
@@ -103,7 +103,7 @@
Settings.Global.putInt(mContext.getContentResolver(), SHOW_NEW_NOTIF_DISMISS, 0);
NotificationMenuRow row = new NotificationMenuRow(mContext, mPeopleNotificationIdentifier);
- row.createMenu(mRow, null);
+ row.createMenu(mRow);
ViewGroup container = (ViewGroup) row.getMenuView();
// noti blocking
@@ -116,7 +116,7 @@
Settings.Global.putInt(mContext.getContentResolver(), SHOW_NEW_NOTIF_DISMISS, 0);
NotificationMenuRow row = new NotificationMenuRow(mContext, mPeopleNotificationIdentifier);
- row.createMenu(mRow, null);
+ row.createMenu(mRow);
ViewGroup container = (ViewGroup) row.getMenuView();
// just for noti blocking
@@ -129,7 +129,7 @@
Settings.Global.putInt(mContext.getContentResolver(), SHOW_NEW_NOTIF_DISMISS, 0);
NotificationMenuRow row = new NotificationMenuRow(mContext, mPeopleNotificationIdentifier);
- row.createMenu(mRow, null);
+ row.createMenu(mRow);
ViewGroup container = (ViewGroup) row.getMenuView();
// one for snooze and one for noti blocking
@@ -142,7 +142,7 @@
Settings.Global.putInt(mContext.getContentResolver(), SHOW_NEW_NOTIF_DISMISS, 1);
NotificationMenuRow row = new NotificationMenuRow(mContext, mPeopleNotificationIdentifier);
- row.createMenu(mRow, null);
+ row.createMenu(mRow);
ViewGroup container = (ViewGroup) row.getMenuView();
// Clear menu
@@ -417,7 +417,7 @@
public void testOnTouchMove() {
NotificationMenuRow row = Mockito.spy(
new NotificationMenuRow(mContext, mPeopleNotificationIdentifier));
- row.createMenu(mRow, null);
+ row.createMenu(mRow);
doReturn(50f).when(row).getDismissThreshold();
doReturn(true).when(row).canBeDismissed();
doReturn(mView).when(row).getMenuView();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt
index e5cb0fb..885e71e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt
@@ -32,6 +32,7 @@
import com.google.android.msdl.data.model.MSDLToken
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
+import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -49,7 +50,7 @@
private val sectionsManager = mock<NotificationSectionsManager>()
private val msdlPlayer = kosmos.fakeMSDLPlayer
private var canRowBeDismissed = true
- private var magneticAnimationsCancelled = false
+ private var magneticAnimationsCancelled = MutableList(childrenNumber) { false }
private val underTest = kosmos.magneticNotificationRowManagerImpl
@@ -64,8 +65,10 @@
NotificationTestHelper(mContext, mDependency, kosmos.testableLooper, featureFlags)
children = notificationTestHelper.createGroup(childrenNumber).childrenContainer
swipedRow = children.attachedChildren[childrenNumber / 2]
- configureMagneticRowListener(swipedRow)
- magneticAnimationsCancelled = false
+ children.attachedChildren.forEachIndexed { index, row ->
+ row.magneticRowListener = row.magneticRowListener.asTestableListener(index)
+ }
+ magneticAnimationsCancelled.replaceAll { false }
}
@Test
@@ -259,14 +262,14 @@
underTest.onMagneticInteractionEnd(swipedRow, velocity = null)
// THEN magnetic animations are cancelled
- assertThat(magneticAnimationsCancelled).isTrue()
+ assertThat(magneticAnimationsCancelled[childrenNumber / 2]).isTrue()
}
@Test
fun onMagneticInteractionEnd_forMagneticNeighbor_cancelsMagneticAnimations() =
kosmos.testScope.runTest {
- val neighborRow = children.attachedChildren[childrenNumber / 2 - 1]
- configureMagneticRowListener(neighborRow)
+ val neighborIndex = childrenNumber / 2 - 1
+ val neighborRow = children.attachedChildren[neighborIndex]
// GIVEN that targets are set
setTargets()
@@ -275,9 +278,15 @@
underTest.onMagneticInteractionEnd(neighborRow, null)
// THEN magnetic animations are cancelled
- assertThat(magneticAnimationsCancelled).isTrue()
+ assertThat(magneticAnimationsCancelled[neighborIndex]).isTrue()
}
+ @After
+ fun tearDown() {
+ // We reset the manager so that all MagneticRowListener can cancel all animations
+ underTest.reset()
+ }
+
private fun setDetachedState() {
val threshold = 100f
underTest.setSwipeThresholdPx(threshold)
@@ -302,27 +311,33 @@
originalTranslation *
MagneticNotificationRowManagerImpl.MAGNETIC_REDUCTION
- private fun configureMagneticRowListener(row: ExpandableNotificationRow) {
- val listener =
- object : MagneticRowListener {
- override fun setMagneticTranslation(translation: Float) {
- row.translation = translation
- }
-
- override fun triggerMagneticForce(
- endTranslation: Float,
- springForce: SpringForce,
- startVelocity: Float,
- ) {}
-
- override fun cancelMagneticAnimations() {
- magneticAnimationsCancelled = true
- }
-
- override fun cancelTranslationAnimations() {}
-
- override fun canRowBeDismissed(): Boolean = canRowBeDismissed
+ private fun MagneticRowListener.asTestableListener(rowIndex: Int): MagneticRowListener {
+ val delegate = this
+ return object : MagneticRowListener {
+ override fun setMagneticTranslation(translation: Float) {
+ delegate.setMagneticTranslation(translation)
}
- row.magneticRowListener = listener
+
+ override fun triggerMagneticForce(
+ endTranslation: Float,
+ springForce: SpringForce,
+ startVelocity: Float,
+ ) {
+ delegate.triggerMagneticForce(endTranslation, springForce, startVelocity)
+ }
+
+ override fun cancelMagneticAnimations() {
+ magneticAnimationsCancelled[rowIndex] = true
+ delegate.cancelMagneticAnimations()
+ }
+
+ override fun cancelTranslationAnimations() {
+ delegate.cancelTranslationAnimations()
+ }
+
+ override fun canRowBeDismissed(): Boolean {
+ return canRowBeDismissed
+ }
+ }
}
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
index 94fdbae..9b961d2 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
@@ -122,7 +122,7 @@
public void setAppName(String appName);
- public void createMenu(ViewGroup parent, StatusBarNotification sbn);
+ public void createMenu(ViewGroup parent);
public void resetMenu();
@@ -215,9 +215,8 @@
/**
* Callback used to signal the menu that its parent notification has been updated.
- * @param sbn
*/
- public void onNotificationUpdated(StatusBarNotification sbn);
+ public void onNotificationUpdated();
/**
* Callback used to signal the menu that a user is moving the parent notification.
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3fc46ed..359bd2b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3170,8 +3170,8 @@
<string name="controls_media_settings_button">Settings</string>
<!-- Description for media control's playing media item, including information for the media's title, the artist, and source app [CHAR LIMIT=NONE]-->
<string name="controls_media_playing_item_description"><xliff:g id="song_name" example="Daily mix">%1$s</xliff:g> by <xliff:g id="artist_name" example="Various artists">%2$s</xliff:g> is playing from <xliff:g id="app_label" example="Spotify">%3$s</xliff:g></string>
- <!-- Content description for media cotnrols progress bar [CHAR_LIMIT=NONE] -->
- <string name="controls_media_seekbar_description"><xliff:g id="elapsed_time" example="1:30">%1$s</xliff:g> of <xliff:g id="total_time" example="3:00">%2$s</xliff:g></string>
+ <!-- Content description for media controls progress bar [CHAR_LIMIT=NONE] -->
+ <string name="controls_media_seekbar_description"><xliff:g id="elapsed_time" example="1 hour 2 minutes 30 seconds">%1$s</xliff:g> of <xliff:g id="total_time" example="4 hours 5 seconds">%2$s</xliff:g></string>
<!-- Placeholder title to inform user that an app has posted media controls [CHAR_LIMIT=NONE] -->
<string name="controls_media_empty_title"><xliff:g id="app_name" example="Foo Music App">%1$s</xliff:g> is running</string>
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 0f1da50..ae3a76e 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -71,6 +71,7 @@
"//frameworks/libs/systemui:com_android_systemui_shared_flags_lib",
"//frameworks/libs/systemui:msdl",
"//frameworks/libs/systemui:view_capture",
+ "am_flags_lib",
],
resource_dirs: [
"res",
diff --git a/packages/SystemUI/shared/res/values/bools.xml b/packages/SystemUI/shared/res/values/bools.xml
index f22dac4..98e5cea 100644
--- a/packages/SystemUI/shared/res/values/bools.xml
+++ b/packages/SystemUI/shared/res/values/bools.xml
@@ -22,4 +22,7 @@
<resources>
<!-- Whether to add padding at the bottom of the complication clock -->
<bool name="dream_overlay_complication_clock_bottom_padding">false</bool>
-</resources>
\ No newline at end of file
+
+ <!-- Whether to mark tasks that are present in the UI as perceptible tasks. -->
+ <bool name="config_usePerceptibleTasks">false</bool>
+</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index ed9ba7a..26d78bb 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -46,6 +46,8 @@
import android.window.TaskSnapshot;
import com.android.internal.app.IVoiceInteractionManagerService;
+import com.android.server.am.Flags;
+import com.android.systemui.shared.R;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -227,6 +229,17 @@
}
/**
+ * Sets whether or not the specified task is perceptible.
+ */
+ public boolean setTaskIsPerceptible(int taskId, boolean isPerceptible) {
+ try {
+ return getService().setTaskIsPerceptible(taskId, isPerceptible);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Removes a task by id.
*/
public void removeTask(final int taskId) {
@@ -311,6 +324,14 @@
}
/**
+ * Returns true if tasks with a presence in the UI should be marked as perceptible tasks.
+ */
+ public static boolean usePerceptibleTasks(Context context) {
+ return Flags.perceptibleTasks()
+ && context.getResources().getBoolean(R.bool.config_usePerceptibleTasks);
+ }
+
+ /**
* Returns true if the running task represents the home task
*/
public static boolean isHomeTask(RunningTaskInfo info) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java
index 7c141c1..5247acc 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java
@@ -58,11 +58,22 @@
private final BiMap<Integer, AmbientVolumeSlider> mSideToSliderMap = HashBiMap.create();
private int mVolumeLevel = AMBIENT_VOLUME_LEVEL_DEFAULT;
+ private HearingDevicesUiEventLogger mUiEventLogger;
+ private int mLaunchSourceId;
+
private final AmbientVolumeSlider.OnChangeListener mSliderOnChangeListener =
(slider, value) -> {
- if (mListener != null) {
- final int side = mSideToSliderMap.inverse().get(slider);
- mListener.onSliderValueChange(side, value);
+ final Integer side = mSideToSliderMap.inverse().get(slider);
+ if (side != null) {
+ if (mUiEventLogger != null) {
+ HearingDevicesUiEvent uiEvent = side == SIDE_UNIFIED
+ ? HearingDevicesUiEvent.HEARING_DEVICES_AMBIENT_CHANGE_UNIFIED
+ : HearingDevicesUiEvent.HEARING_DEVICES_AMBIENT_CHANGE_SEPARATED;
+ mUiEventLogger.log(uiEvent, mLaunchSourceId);
+ }
+ if (mListener != null) {
+ mListener.onSliderValueChange(side, value);
+ }
}
};
@@ -94,6 +105,12 @@
return;
}
setMuted(!mMuted);
+ if (mUiEventLogger != null) {
+ HearingDevicesUiEvent uiEvent = mMuted
+ ? HearingDevicesUiEvent.HEARING_DEVICES_AMBIENT_MUTE
+ : HearingDevicesUiEvent.HEARING_DEVICES_AMBIENT_UNMUTE;
+ mUiEventLogger.log(uiEvent, mLaunchSourceId);
+ }
if (mListener != null) {
mListener.onAmbientVolumeIconClick();
}
@@ -103,6 +120,12 @@
mExpandIcon = requireViewById(R.id.ambient_expand_icon);
mExpandIcon.setOnClickListener(v -> {
setExpanded(!mExpanded);
+ if (mUiEventLogger != null) {
+ HearingDevicesUiEvent uiEvent = mExpanded
+ ? HearingDevicesUiEvent.HEARING_DEVICES_AMBIENT_EXPAND_CONTROLS
+ : HearingDevicesUiEvent.HEARING_DEVICES_AMBIENT_COLLAPSE_CONTROLS;
+ mUiEventLogger.log(uiEvent, mLaunchSourceId);
+ }
if (mListener != null) {
mListener.onExpandIconClick();
}
@@ -243,6 +266,11 @@
updateVolumeLevel();
}
+ void setUiEventLogger(HearingDevicesUiEventLogger uiEventLogger, int launchSourceId) {
+ mUiEventLogger = uiEventLogger;
+ mLaunchSourceId = launchSourceId;
+ }
+
private void updateVolumeLevel() {
int leftLevel, rightLevel;
if (mExpanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
index 22ecb0a..786d27a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -382,6 +382,7 @@
private void setupAmbientControls(CachedBluetoothDevice activeHearingDevice) {
final AmbientVolumeLayout ambientLayout = mDialog.requireViewById(R.id.ambient_layout);
+ ambientLayout.setUiEventLogger(mUiEventLogger, mLaunchSourceId);
mAmbientController = new AmbientVolumeUiController(
mDialog.getContext(), mLocalBluetoothManager, ambientLayout);
mAmbientController.setShowUiWhenLocalDataExist(false);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt
index 9e77b02..fe1d504 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt
@@ -29,7 +29,17 @@
@UiEvent(doc = "Click on the device gear to enter device detail page")
HEARING_DEVICES_GEAR_CLICK(1853),
@UiEvent(doc = "Select a preset from preset spinner") HEARING_DEVICES_PRESET_SELECT(1854),
- @UiEvent(doc = "Click on related tool") HEARING_DEVICES_RELATED_TOOL_CLICK(1856);
+ @UiEvent(doc = "Click on related tool") HEARING_DEVICES_RELATED_TOOL_CLICK(1856),
+ @UiEvent(doc = "Change the ambient volume with unified control")
+ HEARING_DEVICES_AMBIENT_CHANGE_UNIFIED(2149),
+ @UiEvent(doc = "Change the ambient volume with separated control")
+ HEARING_DEVICES_AMBIENT_CHANGE_SEPARATED(2150),
+ @UiEvent(doc = "Mute the ambient volume") HEARING_DEVICES_AMBIENT_MUTE(2151),
+ @UiEvent(doc = "Unmute the ambient volume") HEARING_DEVICES_AMBIENT_UNMUTE(2152),
+ @UiEvent(doc = "Expand the ambient volume controls")
+ HEARING_DEVICES_AMBIENT_EXPAND_CONTROLS(2153),
+ @UiEvent(doc = "Collapse the ambient volume controls")
+ HEARING_DEVICES_AMBIENT_COLLAPSE_CONTROLS(2154);
override fun getId(): Int = this.id
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index cce1ae1..6473b1c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -237,7 +237,15 @@
with(mediaHost) {
expansion = MediaHostState.EXPANDED
expandedMatchesParentHeight = true
- showsOnlyActiveMedia = false
+ if (v2FlagEnabled()) {
+ // Only show active media to match lock screen, not resumable media, which can
+ // persist
+ // for up to 2 days.
+ showsOnlyActiveMedia = true
+ } else {
+ // Maintain old behavior on tablet until V2 flag rolls out.
+ showsOnlyActiveMedia = false
+ }
falsingProtectionNeeded = false
disablePagination = true
init(MediaHierarchyManager.LOCATION_COMMUNAL_HUB)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
index 6d796d9..3f53820 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
@@ -49,9 +49,10 @@
import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.media.controls.ui.viewmodel.MediaActionViewModel
import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel
-import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.Companion.MEDIA_PLAYER_SCRIM_CENTER_ALPHA
import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.Companion.MEDIA_PLAYER_SCRIM_END_ALPHA
+import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.Companion.MEDIA_PLAYER_SCRIM_END_ALPHA_LEGACY
import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.Companion.MEDIA_PLAYER_SCRIM_START_ALPHA
+import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.Companion.MEDIA_PLAYER_SCRIM_START_ALPHA_LEGACY
import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.Companion.SEMANTIC_ACTIONS_ALL
import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.Companion.SEMANTIC_ACTIONS_COMPACT
import com.android.systemui.media.controls.ui.viewmodel.MediaOutputSwitcherViewModel
@@ -537,18 +538,24 @@
height: Int,
): LayerDrawable {
val albumArt = MediaArtworkHelper.getScaledBackground(context, artworkIcon, width, height)
- val alpha =
+ val startAlpha =
if (Flags.mediaControlsA11yColors()) {
- MEDIA_PLAYER_SCRIM_CENTER_ALPHA
- } else {
MEDIA_PLAYER_SCRIM_START_ALPHA
+ } else {
+ MEDIA_PLAYER_SCRIM_START_ALPHA_LEGACY
+ }
+ val endAlpha =
+ if (Flags.mediaControlsA11yColors()) {
+ MEDIA_PLAYER_SCRIM_END_ALPHA
+ } else {
+ MEDIA_PLAYER_SCRIM_END_ALPHA_LEGACY
}
return MediaArtworkHelper.setUpGradientColorOnDrawable(
albumArt,
context.getDrawable(R.drawable.qs_media_scrim)?.mutate() as GradientDrawable,
mutableColorScheme,
- alpha,
- MEDIA_PLAYER_SCRIM_END_ALPHA,
+ startAlpha,
+ endAlpha,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
index 34f7c4d..c9716be 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
@@ -18,6 +18,9 @@
import android.animation.Animator
import android.animation.ObjectAnimator
+import android.icu.text.MeasureFormat
+import android.icu.util.Measure
+import android.icu.util.MeasureUnit
import android.text.format.DateUtils
import androidx.annotation.UiThread
import androidx.lifecycle.Observer
@@ -28,8 +31,11 @@
import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
import com.android.systemui.res.R
+import java.util.Locale
private const val TAG = "SeekBarObserver"
+private const val MIN_IN_SEC = 60
+private const val HOUR_IN_SEC = MIN_IN_SEC * 60
/**
* Observer for changes from SeekBarViewModel.
@@ -127,10 +133,9 @@
}
holder.seekBar.setMax(data.duration)
- val totalTimeString =
- DateUtils.formatElapsedTime(data.duration / DateUtils.SECOND_IN_MILLIS)
+ val totalTimeDescription = formatTimeContentDescription(data.duration)
if (data.scrubbing) {
- holder.scrubbingTotalTimeView.text = totalTimeString
+ holder.scrubbingTotalTimeView.text = formatTimeLabel(data.duration)
}
data.elapsedTime?.let {
@@ -148,20 +153,62 @@
}
}
- val elapsedTimeString = DateUtils.formatElapsedTime(it / DateUtils.SECOND_IN_MILLIS)
+ val elapsedTimeDescription = formatTimeContentDescription(it)
if (data.scrubbing) {
- holder.scrubbingElapsedTimeView.text = elapsedTimeString
+ holder.scrubbingElapsedTimeView.text = formatTimeLabel(it)
}
holder.seekBar.contentDescription =
holder.seekBar.context.getString(
R.string.controls_media_seekbar_description,
- elapsedTimeString,
- totalTimeString
+ elapsedTimeDescription,
+ totalTimeDescription,
)
}
}
+ /** Returns a time string suitable for display, e.g. "12:34" */
+ private fun formatTimeLabel(milliseconds: Int): CharSequence {
+ return DateUtils.formatElapsedTime(milliseconds / DateUtils.SECOND_IN_MILLIS)
+ }
+
+ /**
+ * Returns a time string suitable for content description, e.g. "12 minutes 34 seconds"
+ *
+ * Follows same logic as Chronometer#formatDuration
+ */
+ private fun formatTimeContentDescription(milliseconds: Int): CharSequence {
+ var seconds = milliseconds / DateUtils.SECOND_IN_MILLIS
+
+ val hours =
+ if (seconds >= HOUR_IN_SEC) {
+ seconds / HOUR_IN_SEC
+ } else {
+ 0
+ }
+ seconds -= hours * HOUR_IN_SEC
+
+ val minutes =
+ if (seconds >= MIN_IN_SEC) {
+ seconds / MIN_IN_SEC
+ } else {
+ 0
+ }
+ seconds -= minutes * MIN_IN_SEC
+
+ val measures = arrayListOf<Measure>()
+ if (hours > 0) {
+ measures.add(Measure(hours, MeasureUnit.HOUR))
+ }
+ if (minutes > 0) {
+ measures.add(Measure(minutes, MeasureUnit.MINUTE))
+ }
+ measures.add(Measure(seconds, MeasureUnit.SECOND))
+
+ return MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
+ .formatMeasures(*measures.toTypedArray())
+ }
+
@VisibleForTesting
open fun buildResetAnimator(targetTime: Int): Animator {
val animator =
@@ -169,7 +216,7 @@
holder.seekBar,
"progress",
holder.seekBar.progress,
- targetTime + RESET_ANIMATION_DURATION_MS
+ targetTime + RESET_ANIMATION_DURATION_MS,
)
animator.setAutoCancel(true)
animator.duration = RESET_ANIMATION_DURATION_MS.toLong()
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index 39c08da..694a4c7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -23,7 +23,10 @@
import static com.android.systemui.Flags.mediaLockscreenLaunchAnimation;
import static com.android.systemui.media.controls.domain.pipeline.MediaActionsKt.getNotificationActions;
import static com.android.systemui.media.controls.shared.model.SmartspaceMediaDataKt.NUM_REQUIRED_RECOMMENDATIONS;
-import static com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.MEDIA_PLAYER_SCRIM_CENTER_ALPHA;
+import static com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.MEDIA_PLAYER_SCRIM_END_ALPHA;
+import static com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.MEDIA_PLAYER_SCRIM_END_ALPHA_LEGACY;
+import static com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.MEDIA_PLAYER_SCRIM_START_ALPHA;
+import static com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.MEDIA_PLAYER_SCRIM_START_ALPHA_LEGACY;
import android.animation.Animator;
import android.animation.AnimatorInflater;
@@ -176,9 +179,7 @@
protected static final int SMARTSPACE_CARD_DISMISS_EVENT = 761;
private static final float REC_MEDIA_COVER_SCALE_FACTOR = 1.25f;
- private static final float MEDIA_SCRIM_START_ALPHA = 0.25f;
private static final float MEDIA_REC_SCRIM_START_ALPHA = 0.15f;
- private static final float MEDIA_PLAYER_SCRIM_END_ALPHA = 1.0f;
private static final float MEDIA_REC_SCRIM_END_ALPHA = 1.0f;
private static final Intent SETTINGS_INTENT = new Intent(ACTION_MEDIA_CONTROLS_SETTINGS);
@@ -1093,11 +1094,12 @@
Drawable albumArt = getScaledBackground(artworkIcon, width, height);
GradientDrawable gradient = (GradientDrawable) mContext.getDrawable(
R.drawable.qs_media_scrim).mutate();
- float startAlpha = (Flags.mediaControlsA11yColors())
- ? MEDIA_PLAYER_SCRIM_CENTER_ALPHA
- : MEDIA_SCRIM_START_ALPHA;
+ if (Flags.mediaControlsA11yColors()) {
+ return setupGradientColorOnDrawable(albumArt, gradient, mutableColorScheme,
+ MEDIA_PLAYER_SCRIM_START_ALPHA, MEDIA_PLAYER_SCRIM_END_ALPHA);
+ }
return setupGradientColorOnDrawable(albumArt, gradient, mutableColorScheme,
- startAlpha, MEDIA_PLAYER_SCRIM_END_ALPHA);
+ MEDIA_PLAYER_SCRIM_START_ALPHA_LEGACY, MEDIA_PLAYER_SCRIM_END_ALPHA_LEGACY);
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
index 198155b..b687dce 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
@@ -1137,6 +1137,7 @@
) {
gutsViewHolder.gutsText.setTypeface(menuTF)
gutsViewHolder.dismissText.setTypeface(menuTF)
+ gutsViewHolder.cancelText.setTypeface(menuTF)
titleText.setTypeface(titleTF)
artistText.setTypeface(artistTF)
seamlessText.setTypeface(menuTF)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
index 9153e17..bcda485 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
@@ -419,8 +419,10 @@
const val TURBULENCE_NOISE_PLAY_MS_DURATION = 7500L
@Deprecated("Remove with media_controls_a11y_colors flag")
- const val MEDIA_PLAYER_SCRIM_START_ALPHA = 0.25f
- const val MEDIA_PLAYER_SCRIM_CENTER_ALPHA = 0.75f
- const val MEDIA_PLAYER_SCRIM_END_ALPHA = 1.0f
+ const val MEDIA_PLAYER_SCRIM_START_ALPHA_LEGACY = 0.25f
+ @Deprecated("Remove with media_controls_a11y_colors flag")
+ const val MEDIA_PLAYER_SCRIM_END_ALPHA_LEGACY = 1.0f
+ const val MEDIA_PLAYER_SCRIM_START_ALPHA = 0.65f
+ const val MEDIA_PLAYER_SCRIM_END_ALPHA = 0.75f
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt b/packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt
index 24bb16a..3a81102 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt
@@ -27,9 +27,7 @@
private set
fun setLoggerForTesting(): UiEventLoggerFake {
- return UiEventLoggerFake().also {
- qsUiEventsLogger = it
- }
+ return UiEventLoggerFake().also { qsUiEventsLogger = it }
}
fun resetLogger() {
@@ -40,32 +38,28 @@
enum class QSEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
@UiEvent(doc = "Tile clicked. It has an instance id and a spec (or packageName)")
QS_ACTION_CLICK(387),
-
- @UiEvent(doc = "Tile secondary button clicked. " +
- "It has an instance id and a spec (or packageName)")
+ @UiEvent(
+ doc =
+ "Tile secondary button clicked. " + "It has an instance id and a spec (or packageName)"
+ )
QS_ACTION_SECONDARY_CLICK(388),
-
@UiEvent(doc = "Tile long clicked. It has an instance id and a spec (or packageName)")
QS_ACTION_LONG_PRESS(389),
-
- @UiEvent(doc = "Quick Settings panel expanded")
- QS_PANEL_EXPANDED(390),
-
- @UiEvent(doc = "Quick Settings panel collapsed")
- QS_PANEL_COLLAPSED(391),
-
- @UiEvent(doc = "Tile visible in Quick Settings panel. The tile may be in a different page. " +
- "It has an instance id and a spec (or packageName)")
+ @UiEvent(doc = "Quick Settings panel expanded") QS_PANEL_EXPANDED(390),
+ @UiEvent(doc = "Quick Settings panel collapsed") QS_PANEL_COLLAPSED(391),
+ @UiEvent(
+ doc =
+ "Tile visible in Quick Settings panel. The tile may be in a different page. " +
+ "It has an instance id and a spec (or packageName)"
+ )
QS_TILE_VISIBLE(392),
-
- @UiEvent(doc = "Quick Quick Settings panel expanded")
- QQS_PANEL_EXPANDED(393),
-
- @UiEvent(doc = "Quick Quick Settings panel collapsed")
- QQS_PANEL_COLLAPSED(394),
-
- @UiEvent(doc = "Tile visible in Quick Quick Settings panel. " +
- "It has an instance id and a spec (or packageName)")
+ @UiEvent(doc = "Quick Quick Settings panel expanded") QQS_PANEL_EXPANDED(393),
+ @UiEvent(doc = "Quick Quick Settings panel collapsed") QQS_PANEL_COLLAPSED(394),
+ @UiEvent(
+ doc =
+ "Tile visible in Quick Quick Settings panel. " +
+ "It has an instance id and a spec (or packageName)"
+ )
QQS_TILE_VISIBLE(395);
override fun getId() = _id
@@ -73,47 +67,32 @@
enum class QSEditEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
- @UiEvent(doc = "Tile removed from current tiles")
- QS_EDIT_REMOVE(210),
-
- @UiEvent(doc = "Tile added to current tiles")
- QS_EDIT_ADD(211),
-
- @UiEvent(doc = "Tile moved")
- QS_EDIT_MOVE(212),
-
- @UiEvent(doc = "QS customizer open")
- QS_EDIT_OPEN(213),
-
- @UiEvent(doc = "QS customizer closed")
- QS_EDIT_CLOSED(214),
-
- @UiEvent(doc = "QS tiles reset")
- QS_EDIT_RESET(215);
+ @UiEvent(doc = "Tile removed from current tiles") QS_EDIT_REMOVE(210),
+ @UiEvent(doc = "Tile added to current tiles") QS_EDIT_ADD(211),
+ @UiEvent(doc = "Tile moved") QS_EDIT_MOVE(212),
+ @UiEvent(doc = "QS customizer open") QS_EDIT_OPEN(213),
+ @UiEvent(doc = "QS customizer closed") QS_EDIT_CLOSED(214),
+ @UiEvent(doc = "QS tiles reset") QS_EDIT_RESET(215),
+ @UiEvent(doc = "QS edit mode resize tile to large") QS_EDIT_RESIZE_LARGE(2122),
+ @UiEvent(doc = "QS edit mode resize tile to small") QS_EDIT_RESIZE_SMALL(2123);
override fun getId() = _id
}
/**
- * Events from the QS DND tile dialog. {@see QSZenModeDialogMetricsLogger}
- * Other names for DND (Do Not Disturb) include "Zen" and "Priority mode".
+ * Events from the QS DND tile dialog. {@see QSZenModeDialogMetricsLogger} Other names for DND (Do
+ * Not Disturb) include "Zen" and "Priority mode".
*/
enum class QSDndEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
- @UiEvent(doc = "User selected an option on the DND dialog")
- QS_DND_CONDITION_SELECT(420),
-
+ @UiEvent(doc = "User selected an option on the DND dialog") QS_DND_CONDITION_SELECT(420),
@UiEvent(doc = "User increased countdown duration of DND from the DND dialog")
QS_DND_TIME_UP(422),
-
@UiEvent(doc = "User decreased countdown duration of DND from the DND dialog")
QS_DND_TIME_DOWN(423),
-
@UiEvent(doc = "User enabled DND from the QS DND dialog to last until manually turned off")
QS_DND_DIALOG_ENABLE_FOREVER(946),
-
@UiEvent(doc = "User enabled DND from the QS DND dialog to last until the next alarm goes off")
QS_DND_DIALOG_ENABLE_UNTIL_ALARM(947),
-
@UiEvent(doc = "User enabled DND from the QS DND dialog to last until countdown is done")
QS_DND_DIALOG_ENABLE_UNTIL_COUNTDOWN(948);
@@ -121,29 +100,17 @@
}
enum class QSUserSwitcherEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
- @UiEvent(doc = "The current user has been switched in the detail panel")
- QS_USER_SWITCH(424),
-
- @UiEvent(doc = "User switcher QS dialog open")
- QS_USER_DETAIL_OPEN(425),
-
- @UiEvent(doc = "User switcher QS dialog closed")
- QS_USER_DETAIL_CLOSE(426),
-
- @UiEvent(doc = "User switcher QS dialog more settings pressed")
- QS_USER_MORE_SETTINGS(427),
-
- @UiEvent(doc = "The user has added a guest in the detail panel")
- QS_USER_GUEST_ADD(754),
-
+ @UiEvent(doc = "The current user has been switched in the detail panel") QS_USER_SWITCH(424),
+ @UiEvent(doc = "User switcher QS dialog open") QS_USER_DETAIL_OPEN(425),
+ @UiEvent(doc = "User switcher QS dialog closed") QS_USER_DETAIL_CLOSE(426),
+ @UiEvent(doc = "User switcher QS dialog more settings pressed") QS_USER_MORE_SETTINGS(427),
+ @UiEvent(doc = "The user has added a guest in the detail panel") QS_USER_GUEST_ADD(754),
@UiEvent(doc = "The user selected 'Start over' after switching to the existing Guest user")
QS_USER_GUEST_WIPE(755),
-
@UiEvent(doc = "The user selected 'Yes, continue' after switching to the existing Guest user")
QS_USER_GUEST_CONTINUE(756),
-
@UiEvent(doc = "The user has pressed 'Remove guest' in the detail panel")
QS_USER_GUEST_REMOVE(757);
override fun getId() = _id
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
index 482cd40..3f279b0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
@@ -16,15 +16,18 @@
package com.android.systemui.qs.panels.domain.interactor
+import com.android.internal.logging.UiEventLogger
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
+import com.android.systemui.qs.QSEditEvent
import com.android.systemui.qs.panels.data.repository.DefaultLargeTilesRepository
import com.android.systemui.qs.panels.data.repository.LargeTileSpanRepository
import com.android.systemui.qs.panels.shared.model.PanelsLog
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.pipeline.shared.metricSpec
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
@@ -40,6 +43,7 @@
private val repo: DefaultLargeTilesRepository,
private val currentTilesInteractor: CurrentTilesInteractor,
private val preferencesInteractor: QSPreferencesInteractor,
+ private val uiEventLogger: UiEventLogger,
largeTilesSpanRepo: LargeTileSpanRepository,
@PanelsLog private val logBuffer: LogBuffer,
@Application private val applicationScope: CoroutineScope,
@@ -70,8 +74,18 @@
val isIcon = !largeTilesSpecs.value.contains(spec)
if (toIcon && !isIcon) {
preferencesInteractor.setLargeTilesSpecs(largeTilesSpecs.value - spec)
+ uiEventLogger.log(
+ /* event= */ QSEditEvent.QS_EDIT_RESIZE_SMALL,
+ /* uid= */ 0,
+ /* packageName= */ spec.metricSpec,
+ )
} else if (!toIcon && isIcon) {
preferencesInteractor.setLargeTilesSpecs(largeTilesSpecs.value + spec)
+ uiEventLogger.log(
+ /* event= */ QSEditEvent.QS_EDIT_RESIZE_LARGE,
+ /* uid= */ 0,
+ /* packageName= */ spec.metricSpec,
+ )
}
}
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 4adc1a5..20b44d7 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
@@ -22,7 +22,9 @@
import android.icu.text.DisplayContext
import android.provider.Settings
import android.view.ViewGroup
+import androidx.compose.material3.ColorScheme
import androidx.compose.runtime.getValue
+import androidx.compose.ui.graphics.Color
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.lifecycle.ExclusiveActivatable
@@ -244,11 +246,29 @@
/** Represents the background highlight of a header icons chip. */
sealed interface HeaderChipHighlight {
- data object None : HeaderChipHighlight
- data object Weak : HeaderChipHighlight
+ fun backgroundColor(colorScheme: ColorScheme): Color
- data object Strong : HeaderChipHighlight
+ fun foregroundColor(colorScheme: ColorScheme): Color
+
+ data object None : HeaderChipHighlight {
+ override fun backgroundColor(colorScheme: ColorScheme): Color = Color.Unspecified
+
+ override fun foregroundColor(colorScheme: ColorScheme): Color = colorScheme.primary
+ }
+
+ data object Weak : HeaderChipHighlight {
+ override fun backgroundColor(colorScheme: ColorScheme): Color =
+ colorScheme.primary.copy(alpha = 0.1f)
+
+ override fun foregroundColor(colorScheme: ColorScheme): Color = colorScheme.primary
+ }
+
+ data object Strong : HeaderChipHighlight {
+ override fun backgroundColor(colorScheme: ColorScheme): Color = colorScheme.secondary
+
+ override fun foregroundColor(colorScheme: ColorScheme): Color = colorScheme.onSecondary
+ }
}
private fun getFormatFromPattern(pattern: String?): DateFormat {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
index 0e3f103..24ab695 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
@@ -21,9 +21,14 @@
import static android.app.NotificationChannel.RECS_ID;
import static android.app.NotificationChannel.SOCIAL_MEDIA_ID;
+import android.app.Notification;
+import android.content.Context;
+import android.os.Build;
+
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.systemui.statusbar.notification.icon.IconPack;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import java.util.List;
@@ -79,6 +84,43 @@
public EntryAdapter getGroupRoot() {
return this;
}
+
+ @Override
+ public boolean isClearable() {
+ // TODO(b/394483200): check whether all of the children are clearable, when implemented
+ return true;
+ }
+
+ @Override
+ public int getTargetSdk() {
+ return Build.VERSION_CODES.CUR_DEVELOPMENT;
+ }
+
+ @Override
+ public String getSummarization() {
+ return null;
+ }
+
+ @Override
+ public int getContrastedColor(Context context, boolean isLowPriority, int backgroundColor) {
+ return Notification.COLOR_DEFAULT;
+ }
+
+ @Override
+ public boolean canPeek() {
+ return false;
+ }
+
+ @Override
+ public long getWhen() {
+ return 0;
+ }
+
+ @Override
+ public IconPack getIcons() {
+ // TODO(b/396446620): implement bundle icons
+ return null;
+ }
}
public static final List<BundleEntry> ROOT_BUNDLES = List.of(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
index 4df81c9..6431cac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
@@ -16,9 +16,12 @@
package com.android.systemui.statusbar.notification.collection;
+import android.content.Context;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.systemui.statusbar.notification.icon.IconPack;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
/**
@@ -59,9 +62,49 @@
EntryAdapter getGroupRoot();
/**
+ * @return whether the row can be removed with the 'Clear All' action
+ */
+ boolean isClearable();
+
+ /**
* Returns whether the entry is attached to the current shade list
*/
default boolean isAttached() {
return getParent() != null;
}
+
+ /**
+ * Returns the target sdk of the package that owns this entry.
+ */
+ int getTargetSdk();
+
+ /**
+ * Returns the summarization for this entry, if there is one
+ */
+ @Nullable String getSummarization();
+
+ /**
+ * Performs any steps needed to set or reset data before an inflation or reinflation.
+ */
+ default void prepareForInflation() {}
+
+ /**
+ * Gets a color that would have sufficient contrast on the given background color.
+ */
+ int getContrastedColor(Context context, boolean isLowPriority, int backgroundColor);
+
+ /**
+ * Whether this entry can peek on screen as a heads up view
+ */
+ boolean canPeek();
+
+ /**
+ * Returns the visible 'time', in milliseconds, of the entry
+ */
+ long getWhen();
+
+ /**
+ * Retrieves the pack of icons associated with this entry
+ */
+ IconPack getIcons();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 90f9525..698a563 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -78,6 +78,7 @@
import com.android.systemui.statusbar.notification.row.shared.HeadsUpStatusBarModel;
import com.android.systemui.statusbar.notification.row.shared.NotificationContentModel;
import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.notification.stack.PriorityBucket;
import com.android.systemui.util.ListenerSet;
@@ -309,6 +310,47 @@
}
return null;
}
+
+ @Override
+ public boolean isClearable() {
+ return NotificationEntry.this.isClearable();
+ }
+
+ @Override
+ public int getTargetSdk() {
+ return NotificationEntry.this.targetSdk;
+ }
+
+ @Override
+ public String getSummarization() {
+ return getRanking().getSummarization();
+ }
+
+ @Override
+ public void prepareForInflation() {
+ getSbn().clearPackageContext();
+ }
+
+ @Override
+ public int getContrastedColor(Context context, boolean isLowPriority, int backgroundColor) {
+ return NotificationEntry.this.getContrastedColor(
+ context, isLowPriority, backgroundColor);
+ }
+
+ @Override
+ public boolean canPeek() {
+ return isStickyAndNotDemoted();
+ }
+
+ @Override
+ public long getWhen() {
+ return getSbn().getNotification().getWhen();
+ }
+
+ @Override
+ public IconPack getIcons() {
+ return NotificationEntry.this.getIcons();
+ }
}
public EntryAdapter getEntryAdapter() {
@@ -580,6 +622,7 @@
}
public boolean hasFinishedInitialization() {
+ NotificationBundleUi.assertInLegacyMode();
return initializationTime != -1
&& SystemClock.elapsedRealtime() > initializationTime + INITIALIZATION_DELAY;
}
@@ -663,10 +706,12 @@
}
public void resetInitializationTime() {
+ NotificationBundleUi.assertInLegacyMode();
initializationTime = -1;
}
public void setInitializationTime(long time) {
+ NotificationBundleUi.assertInLegacyMode();
if (initializationTime == -1) {
initializationTime = time;
}
@@ -683,9 +728,13 @@
* @return {@code true} if we are a media notification
*/
public boolean isMediaNotification() {
- if (row == null) return false;
+ if (NotificationBundleUi.isEnabled()) {
+ return getSbn().getNotification().isMediaNotification();
+ } else {
+ if (row == null) return false;
- return row.isMediaRow();
+ return row.isMediaRow();
+ }
}
public boolean containsCustomViews() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 9c1d073..ac11c15 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -67,6 +67,7 @@
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt;
import com.android.systemui.util.Assert;
import com.android.systemui.util.NamedListenerSet;
@@ -1282,7 +1283,13 @@
entry.getAttachState().setExcludingFilter(filter);
if (filter != null) {
// notification is removed from the list, so we reset its initialization time
- entry.resetInitializationTime();
+ if (NotificationBundleUi.isEnabled()) {
+ if (entry.getRow() != null) {
+ entry.getRow().resetInitializationTime();
+ }
+ } else {
+ entry.resetInitializationTime();
+ }
}
return filter != null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 64cd617..473460b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -46,6 +46,7 @@
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
@@ -117,7 +118,6 @@
import com.android.systemui.statusbar.notification.logging.NotificationCounters;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded;
-import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationCompactMessagingTemplateViewWrapper;
@@ -169,6 +169,7 @@
private static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
private static final SourceType BASE_VALUE = SourceType.from("BaseValue");
private static final SourceType FROM_PARENT = SourceType.from("FromParent(ENR)");
+ private static final long INITIALIZATION_DELAY = 400;
// We don't correctly track dark mode until the content views are inflated, so always update
// the background on first content update just in case it happens to be during a theme change.
@@ -267,7 +268,6 @@
private NotificationContentView mPublicLayout;
private NotificationContentView mPrivateLayout;
private NotificationContentView[] mLayouts;
- private int mNotificationColor;
private ExpandableNotificationRowLogger mLogger;
private String mLoggingKey;
private NotificationGuts mGuts;
@@ -352,6 +352,10 @@
*/
private boolean mSaveSpaceOnLockscreen;
+ // indicates when this view was first attached to a window
+ // this value will reset when the view is completely removed from the shade (ie: filtered out)
+ private long initializationTime = -1;
+
/**
* It is added for unit testing purpose.
* Please do not use it for other purposes.
@@ -625,26 +629,11 @@
}
/**
- * Marks a content view as freeable, setting it so that future inflations do not reinflate
- * and ensuring that the view is freed when it is safe to remove.
- *
- * @param inflationFlag flag corresponding to the content view to be freed
- * @deprecated By default, {@link NotificationRowContentBinder#unbindContent} will tell the
- * view hierarchy to only free when the view is safe to remove so this method is no longer
- * needed. Will remove when all uses are gone.
- */
- @Deprecated
- public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) {
- RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
- params.markContentViewsFreeable(inflationFlag);
- mRowContentBindStage.requestRebind(mEntry, null /* callback */);
- }
-
- /**
* Returns whether this row is considered non-blockable (i.e. it's a non-blockable system notif
* or is in an allowList).
*/
public boolean getIsNonblockable() {
+ NotificationBundleUi.assertInLegacyMode();
if (mEntry == null) {
return true;
}
@@ -666,9 +655,8 @@
l.onNotificationUpdated(mEntry);
}
mShowingPublicInitialized = false;
- updateNotificationColor();
if (mMenuRow != null) {
- mMenuRow.onNotificationUpdated(mEntry.getSbn());
+ mMenuRow.onNotificationUpdated();
mMenuRow.setAppName(mAppName);
}
if (mIsSummaryWithChildren) {
@@ -727,15 +715,6 @@
}
/**
- * Called when the notification's ranking was changed (but nothing else changed).
- */
- public void onNotificationRankingUpdated() {
- if (mMenuRow != null) {
- mMenuRow.onNotificationUpdated(mEntry.getSbn());
- }
- }
-
- /**
* Call when bubble state has changed and the button on the notification should be updated.
*/
public void updateBubbleButton() {
@@ -746,7 +725,7 @@
@VisibleForTesting
void updateShelfIconColor() {
- StatusBarIconView expandedIcon = mEntry.getIcons().getShelfIcon();
+ StatusBarIconView expandedIcon = getShelfIcon();
boolean isPreL = Boolean.TRUE.equals(expandedIcon.getTag(R.id.icon_is_pre_L));
boolean colorize = !isPreL || NotificationUtils.isGrayscale(expandedIcon,
ContrastColorUtil.getInstance(mContext));
@@ -767,8 +746,13 @@
if (color != Notification.COLOR_INVALID) {
return color;
} else {
- return mEntry.getContrastedColor(mContext, mIsMinimized && !isExpanded(),
- getBackgroundColorWithoutTint());
+ if (NotificationBundleUi.isEnabled()) {
+ return mEntryAdapter.getContrastedColor(mContext, mIsMinimized && !isExpanded(),
+ getBackgroundColorWithoutTint());
+ } else {
+ return mEntry.getContrastedColor(mContext, mIsMinimized && !isExpanded(),
+ getBackgroundColorWithoutTint());
+ }
}
}
@@ -870,15 +854,29 @@
boolean customView = contractedView != null
&& contractedView.getId()
!= com.android.internal.R.id.status_bar_latest_event_content;
- boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N;
- boolean beforeP = mEntry.targetSdk < Build.VERSION_CODES.P;
- boolean beforeS = mEntry.targetSdk < Build.VERSION_CODES.S;
+ int targetSdk = Build.VERSION_CODES.CUR_DEVELOPMENT;
+ if (NotificationBundleUi.isEnabled()) {
+ targetSdk = mEntryAdapter.getTargetSdk();
+ } else {
+ targetSdk = mEntry.targetSdk;
+ }
+
+ boolean beforeN = targetSdk < Build.VERSION_CODES.N;
+ boolean beforeP = targetSdk < Build.VERSION_CODES.P;
+ boolean beforeS = targetSdk < Build.VERSION_CODES.S;
int smallHeight;
boolean isCallLayout = contractedView instanceof CallLayout;
boolean isMessagingLayout = contractedView instanceof MessagingLayout
|| contractedView instanceof ConversationLayout;
+ String summarization = null;
+ if (NotificationBundleUi.isEnabled()) {
+ summarization = mEntryAdapter.getSummarization();
+ } else {
+ summarization = mEntry.getRanking().getSummarization();
+ }
+
if (customView && beforeS && !mIsSummaryWithChildren) {
if (beforeN) {
smallHeight = mMaxSmallHeightBeforeN;
@@ -891,7 +889,7 @@
smallHeight = maxExpandedHeight;
} else if (NmSummarizationUiFlag.isEnabled()
&& isMessagingLayout
- && !TextUtils.isEmpty(mEntry.getRanking().getSummarization())) {
+ && !TextUtils.isEmpty(summarization)) {
smallHeight = mMaxSmallHeightWithSummarization;
} else {
smallHeight = mMaxSmallHeight;
@@ -1524,7 +1522,22 @@
@Override
public boolean hasFinishedInitialization() {
- return getEntry().hasFinishedInitialization();
+ if (NotificationBundleUi.isEnabled()) {
+ return initializationTime != -1
+ && SystemClock.elapsedRealtime() > initializationTime + INITIALIZATION_DELAY;
+ } else {
+ return getEntry().hasFinishedInitialization();
+ }
+ }
+
+ public void resetInitializationTime() {
+ initializationTime = -1;
+ }
+
+ public void setInitializationTime(long time) {
+ if (initializationTime == -1) {
+ initializationTime = time;
+ }
}
/**
@@ -1539,7 +1552,7 @@
return null;
}
if (mMenuRow.getMenuView() == null) {
- mMenuRow.createMenu(this, mEntry.getSbn());
+ mMenuRow.createMenu(this);
mMenuRow.setAppName(mAppName);
FrameLayout.LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
@@ -1581,7 +1594,7 @@
if (oldMenu != null) {
int menuIndex = indexOfChild(oldMenu);
removeView(oldMenu);
- mMenuRow.createMenu(ExpandableNotificationRow.this, mEntry.getSbn());
+ mMenuRow.createMenu(ExpandableNotificationRow.this);
mMenuRow.setAppName(mAppName);
addView(mMenuRow.getMenuView(), menuIndex);
}
@@ -1589,12 +1602,17 @@
l.reinflate();
l.reInflateViews();
}
- mEntry.getSbn().clearPackageContext();
+ if (NotificationBundleUi.isEnabled()) {
+ mEntryAdapter.prepareForInflation();
+ } else {
+ mEntry.getSbn().clearPackageContext();
+ }
// TODO: Move content inflation logic out of this call
RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
params.setNeedsReinflation(true);
- var rebindEndCallback = mRebindingTracker.trackRebinding(mEntry.getKey());
+ var rebindEndCallback = mRebindingTracker.trackRebinding(NotificationBundleUi.isEnabled()
+ ? mEntryAdapter.getKey() : mEntry.getKey());
mRowContentBindStage.requestRebind(mEntry, (e) -> rebindEndCallback.onFinished());
Trace.endSection();
}
@@ -1658,20 +1676,6 @@
mPrivateLayout.setSingleLineWidthIndention(indention);
}
- public int getNotificationColor() {
- return mNotificationColor;
- }
-
- public void updateNotificationColor() {
- Configuration currentConfig = getResources().getConfiguration();
- boolean nightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
- == Configuration.UI_MODE_NIGHT_YES;
-
- mNotificationColor = ContrastColorUtil.resolveContrastColor(mContext,
- mEntry.getSbn().getNotification().color,
- getBackgroundColorWithoutTint(), nightMode);
- }
-
public HybridNotificationView getSingleLineView() {
return mPrivateLayout.getSingleLineView();
}
@@ -2260,6 +2264,11 @@
mEntry = entry;
}
+ @VisibleForTesting
+ protected void setEntryAdapter(EntryAdapter entry) {
+ mEntryAdapter = entry;
+ }
+
private final Runnable mExpireRecentlyAlertedFlag = () -> applyAudiblyAlertedRecently(false);
private void applyAudiblyAlertedRecently(boolean audiblyAlertedRecently) {
@@ -2497,7 +2506,7 @@
mTranslateableViews.get(i).setTranslationX(0);
}
invalidateOutline();
- getEntry().getIcons().getShelfIcon().setScrollX(0);
+ getShelfIcon().setScrollX(0);
}
if (mMenuRow != null) {
@@ -2616,7 +2625,7 @@
// In order to keep the shelf in sync with this swiping, we're simply translating
// it's icon by the same amount. The translation is already being used for the normal
// positioning, so we can use the scrollX instead.
- getEntry().getIcons().getShelfIcon().setScrollX((int) -translationX);
+ getShelfIcon().setScrollX((int) -translationX);
}
if (mMenuRow != null && mMenuRow.getMenuView() != null) {
@@ -2843,7 +2852,11 @@
@Override
public @NonNull StatusBarIconView getShelfIcon() {
- return getEntry().getIcons().getShelfIcon();
+ if (NotificationBundleUi.isEnabled()) {
+ return getEntryAdapter().getIcons().getShelfIcon();
+ } else {
+ return mEntry.getIcons().getShelfIcon();
+ }
}
@Override
@@ -3072,8 +3085,11 @@
* except for legacy use cases.
*/
public boolean canShowHeadsUp() {
+ boolean canEntryHun = NotificationBundleUi.isEnabled()
+ ? mEntryAdapter.canPeek()
+ : mEntry.isStickyAndNotDemoted();
if (mOnKeyguard && !isDozing() && !isBypassEnabled() &&
- (!mEntry.isStickyAndNotDemoted()
+ (!canEntryHun
|| (!mIgnoreLockscreenConstraints && mSaveSpaceOnLockscreen))) {
return false;
}
@@ -3112,7 +3128,11 @@
}
if (!mIsSummaryWithChildren && wasSummary) {
// Reset the 'when' once the row stops being a summary
- mPublicLayout.setNotificationWhen(mEntry.getSbn().getNotification().getWhen());
+ if (NotificationBundleUi.isEnabled()) {
+ mPublicLayout.setNotificationWhen(mEntryAdapter.getWhen());
+ } else {
+ mPublicLayout.setNotificationWhen(mEntry.getSbn().getNotification().getWhen());
+ }
}
getShowingLayout().updateBackgroundColor(false /* animate */);
mPrivateLayout.updateExpandButtons(isExpandable());
@@ -3381,7 +3401,7 @@
*/
@Override
public boolean canExpandableViewBeDismissed() {
- if (areGutsExposed() || !mEntry.hasFinishedInitialization()) {
+ if (areGutsExposed() || !hasFinishedInitialization()) {
return false;
}
return canViewBeDismissed();
@@ -3405,7 +3425,12 @@
* clearability see {@link NotificationEntry#isClearable()}.
*/
public boolean canViewBeCleared() {
- return mEntry.isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
+ if (NotificationBundleUi.isEnabled()) {
+ return mEntryAdapter.isClearable()
+ && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
+ } else {
+ return mEntry.isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
+ }
}
private boolean shouldShowPublic() {
@@ -4082,6 +4107,7 @@
}
public boolean isMediaRow() {
+ NotificationBundleUi.assertInLegacyMode();
return mEntry.getSbn().getNotification().isMediaNotification();
}
@@ -4204,7 +4230,11 @@
public void dump(PrintWriter pwOriginal, String[] args) {
IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
// Skip super call; dump viewState ourselves
- pw.println("Notification: " + mEntry.getKey());
+ if (NotificationBundleUi.isEnabled()) {
+ pw.println("Notification: " + mEntryAdapter.getKey());
+ } else {
+ pw.println("Notification: " + mEntry.getKey());
+ }
DumpUtilsKt.withIncreasedIndent(pw, () -> {
pw.println(this);
pw.print("visibility: " + getVisibility());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index b43a387..07711b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -374,7 +374,11 @@
mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
- mView.getEntry().setInitializationTime(mClock.elapsedRealtime());
+ if (NotificationBundleUi.isEnabled()) {
+ mView.setInitializationTime(mClock.elapsedRealtime());
+ } else {
+ mView.getEntry().setInitializationTime(mClock.elapsedRealtime());
+ }
mPluginManager.addPluginListener(mView,
NotificationMenuRowPlugin.class, false /* Allow multiple */);
if (!SceneContainerFlag.isEnabled()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 6e638f5..9a75253 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -73,6 +73,7 @@
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -436,7 +437,9 @@
onNasFeedbackClick,
mUiEventLogger,
mDeviceProvisionedController.isDeviceProvisioned(),
- row.getIsNonblockable(),
+ NotificationBundleUi.isEnabled()
+ ? !row.getEntry().isBlockable()
+ : row.getIsNonblockable(),
mHighPriorityProvider.isHighPriority(row.getEntry()),
mAssistantFeedbackController,
mMetricsLogger,
@@ -480,7 +483,9 @@
row.getEntry(),
onSettingsClick,
mDeviceProvisionedController.isDeviceProvisioned(),
- row.getIsNonblockable());
+ NotificationBundleUi.isEnabled()
+ ? !row.getEntry().isBlockable()
+ : row.getIsNonblockable());
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index b96b224a..ab382df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -186,7 +186,7 @@
}
@Override
- public void createMenu(ViewGroup parent, StatusBarNotification sbn) {
+ public void createMenu(ViewGroup parent) {
mParent = (ExpandableNotificationRow) parent;
createMenuViews(true /* resetState */);
}
@@ -227,7 +227,7 @@
}
@Override
- public void onNotificationUpdated(StatusBarNotification sbn) {
+ public void onNotificationUpdated() {
if (mMenuContainer == null) {
// Menu hasn't been created yet, no need to do anything.
return;
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 3d60e03..3ff18ef 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
@@ -6686,7 +6686,7 @@
static boolean canChildBeCleared(View v) {
if (v instanceof ExpandableNotificationRow row) {
- if (row.areGutsExposed() || !row.getEntry().hasFinishedInitialization()) {
+ if (row.areGutsExposed() || !row.hasFinishedInitialization()) {
return false;
}
return row.canViewBeCleared();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index 4ff09d3..e8b50d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -45,6 +45,9 @@
import static java.util.Collections.singletonList;
import android.os.SystemClock;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArrayMap;
@@ -74,10 +77,14 @@
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Assert;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -127,6 +134,9 @@
private TestableStabilityManager mStabilityManager;
private TestableNotifFilter mFinalizeFilter;
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private Map<String, Integer> mNextIdMap = new ArrayMap<>();
private int mNextRank = 0;
@@ -561,6 +571,7 @@
}
@Test
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
public void testFilter_resetsInitalizationTime() {
// GIVEN a NotifFilter that filters out a specific package
NotifFilter filter1 = spy(new PackageFilter(PACKAGE_1));
@@ -584,6 +595,31 @@
}
@Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ public void testFilter_resetsInitializationTime_onRow() throws Exception {
+ // GIVEN a NotifFilter that filters out a specific package
+ NotifFilter filter1 = spy(new PackageFilter(PACKAGE_1));
+ mListBuilder.addFinalizeFilter(filter1);
+
+ // GIVEN a notification that was initialized 1 second ago that will be filtered out
+ final NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg(PACKAGE_1)
+ .setId(nextId(PACKAGE_1))
+ .setRank(nextRank())
+ .build();
+ entry.setRow(new NotificationTestHelper(mContext, mDependency).createRow());
+ entry.getRow().setInitializationTime(SystemClock.elapsedRealtime() - 1000);
+ assertTrue(entry.getRow().hasFinishedInitialization());
+
+ // WHEN the pipeline is kicked off
+ mReadyForBuildListener.onBuildList(singletonList(entry), "test");
+ mPipelineChoreographer.runIfScheduled();
+
+ // THEN the entry's initialization time is reset
+ assertFalse(entry.getRow().hasFinishedInitialization());
+ }
+
+ @Test
public void testNotifFiltersCanBePreempted() {
// GIVEN two notif filters
NotifFilter filter1 = spy(new PackageFilter(PACKAGE_2));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 1b53531..24d8d1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -75,6 +75,7 @@
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded;
import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -589,6 +590,7 @@
}
@Test
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
public void testGetIsNonblockable() throws Exception {
ExpandableNotificationRow row =
mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
index 47238fe..c874bc6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
@@ -36,6 +36,7 @@
import com.android.internal.widget.NotificationExpandButton
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.FeedbackIcon
+import com.android.systemui.statusbar.notification.collection.EntryAdapter
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
@@ -82,6 +83,7 @@
fakeParent =
spy(FrameLayout(mContext, /* attrs= */ null).also { it.visibility = View.GONE })
val mockEntry = createMockNotificationEntry()
+ val mockEntryAdapter = createMockNotificationEntryAdapter()
row =
spy(
when (NotificationBundleUi.isEnabled) {
@@ -92,6 +94,7 @@
UserHandle.CURRENT
).apply {
entry = mockEntry
+ entryAdapter = mockEntryAdapter
}
}
false -> {
@@ -611,6 +614,7 @@
whenever(this.entry).thenReturn(notificationEntry)
whenever(this.context).thenReturn(mContext)
whenever(this.bubbleClickListener).thenReturn(View.OnClickListener {})
+ whenever(this.entryAdapter).thenReturn(createMockNotificationEntryAdapter())
}
private fun createMockNotificationEntry() =
@@ -624,6 +628,9 @@
whenever(sbnMock.user).thenReturn(userMock)
}
+ private fun createMockNotificationEntryAdapter() =
+ mock<EntryAdapter>()
+
private fun createLinearLayoutWithBottomMargin(bottomMargin: Int): LinearLayout {
val outerLayout = LinearLayout(mContext)
val innerLayout = LinearLayout(mContext)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
index 3d4c901..99b99ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
@@ -427,7 +427,6 @@
.setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
.setImportance(NotificationManager.IMPORTANCE_HIGH)
.build()
- whenever(row.getIsNonblockable()).thenReturn(false)
whenever(highPriorityProvider.isHighPriority(entry)).thenReturn(true)
val statusBarNotification = entry.sbn
gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -463,7 +462,6 @@
NotificationEntryHelper.modifyRanking(row.entry)
.setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
.build()
- whenever(row.getIsNonblockable()).thenReturn(false)
val statusBarNotification = row.entry.sbn
val entry = row.entry
gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -499,7 +497,6 @@
NotificationEntryHelper.modifyRanking(row.entry)
.setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
.build()
- whenever(row.getIsNonblockable()).thenReturn(false)
val statusBarNotification = row.entry.sbn
val entry = row.entry
gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -566,7 +563,7 @@
): NotificationMenuRowPlugin.MenuItem {
val menuRow: NotificationMenuRowPlugin =
NotificationMenuRow(mContext, peopleNotificationIdentifier)
- menuRow.createMenu(row, row!!.entry.sbn)
+ menuRow.createMenu(row)
val menuItem = menuRow.getLongpressMenuItem(mContext)
Assert.assertNotNull(menuItem)
return menuItem
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 6ac20d4..955de273 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -675,7 +675,7 @@
@Test
public void testClearNotifications_clearAllInProgress() {
ExpandableNotificationRow row = createClearableRow();
- when(row.getEntry().hasFinishedInitialization()).thenReturn(true);
+ when(row.hasFinishedInitialization()).thenReturn(true);
doReturn(true).when(mStackScroller).isVisible(row);
mStackScroller.addContainerView(row);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorKosmos.kt
index 8d4db8b..8a6f68c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.panels.domain.interactor
+import com.android.internal.logging.uiEventLoggerFake
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.log.core.FakeLogBuffer
@@ -29,6 +30,7 @@
defaultLargeTilesRepository,
currentTilesInteractor,
qsPreferencesInteractor,
+ uiEventLoggerFake,
largeTileSpanRepository,
FakeLogBuffer.Factory.create(),
applicationCoroutineScope,
diff --git a/packages/Vcn/framework-b/Android.bp b/packages/Vcn/framework-b/Android.bp
index edb22c0..c531233 100644
--- a/packages/Vcn/framework-b/Android.bp
+++ b/packages/Vcn/framework-b/Android.bp
@@ -77,8 +77,7 @@
],
soong_config_variables: {
is_vcn_in_mainline: {
- //TODO: b/380155299 Make it Baklava when aidl tool can understand it
- min_sdk_version: "current",
+ min_sdk_version: "36",
static_libs: ["android.net.vcn.flags-aconfig-java"],
apex_available: ["com.android.tethering"],
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 05301fd..4f56483 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -31,6 +31,8 @@
import static com.android.internal.util.CollectionUtils.any;
import static com.android.internal.util.Preconditions.checkState;
+import static com.android.server.companion.association.DisassociationProcessor.REASON_API;
+import static com.android.server.companion.association.DisassociationProcessor.REASON_PKG_DATA_CLEARED;
import static com.android.server.companion.utils.PackageUtils.enforceUsesCompanionDeviceFeature;
import static com.android.server.companion.utils.PackageUtils.isRestrictedSettingsAllowed;
import static com.android.server.companion.utils.PermissionsUtils.enforceCallerCanManageAssociationsForPackage;
@@ -250,7 +252,7 @@
+ packageName + "]. Cleaning up CDM data...");
for (AssociationInfo association : associationsForPackage) {
- mDisassociationProcessor.disassociate(association.getId());
+ mDisassociationProcessor.disassociate(association.getId(), REASON_PKG_DATA_CLEARED);
}
mCompanionAppBinder.onPackageChanged(userId);
@@ -426,7 +428,7 @@
@Override
public void disassociate(int associationId) {
- mDisassociationProcessor.disassociate(associationId);
+ mDisassociationProcessor.disassociate(associationId, REASON_API);
}
@Override
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index e7d1460..c5ac7c3 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -18,6 +18,8 @@
import static android.companion.CompanionDeviceManager.MESSAGE_REQUEST_CONTEXT_SYNC;
+import static com.android.server.companion.association.DisassociationProcessor.REASON_SHELL;
+
import android.companion.AssociationInfo;
import android.companion.ContextSyncMessage;
import android.companion.Flags;
@@ -122,7 +124,7 @@
if (association == null) {
out.println("Association doesn't exist.");
} else {
- mDisassociationProcessor.disassociate(association.getId());
+ mDisassociationProcessor.disassociate(association.getId(), REASON_SHELL);
}
}
break;
@@ -132,7 +134,7 @@
final List<AssociationInfo> userAssociations =
mAssociationStore.getAssociationsByUser(userId);
for (AssociationInfo association : userAssociations) {
- mDisassociationProcessor.disassociate(association.getId());
+ mDisassociationProcessor.disassociate(association.getId(), REASON_SHELL);
}
}
break;
diff --git a/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java b/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java
index f2d019b..ce7dcd0 100644
--- a/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java
@@ -58,6 +58,7 @@
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
@@ -164,6 +165,7 @@
private static final String FILE_NAME_LEGACY = "companion_device_manager_associations.xml";
private static final String FILE_NAME = "companion_device_manager.xml";
+ private static final String FILE_NAME_LAST_REMOVED_ASSOCIATION = "last_removed_association.txt";
private static final String XML_TAG_STATE = "state";
private static final String XML_TAG_ASSOCIATIONS = "associations";
@@ -268,6 +270,46 @@
}
}
+ /**
+ * Read the last removed association from disk.
+ */
+ public String readLastRemovedAssociation(@UserIdInt int userId) {
+ final AtomicFile file = createStorageFileForUser(
+ userId, FILE_NAME_LAST_REMOVED_ASSOCIATION);
+ StringBuilder sb = new StringBuilder();
+ int c;
+ try (FileInputStream fis = file.openRead()) {
+ while ((c = fis.read()) != -1) {
+ sb.append((char) c);
+ }
+ fis.close();
+ return sb.toString();
+ } catch (FileNotFoundException e) {
+ Slog.e(TAG, "File " + file + " for user=" + userId + " doesn't exist.");
+ return null;
+ } catch (IOException e) {
+ Slog.e(TAG, "Can't read file " + file + " for user=" + userId);
+ return null;
+ }
+ }
+
+ /**
+ * Write the last removed association to disk.
+ */
+ public void writeLastRemovedAssociation(AssociationInfo association, String reason) {
+ Slog.i(TAG, "Writing last removed association=" + association.getId() + " to disk...");
+
+ final AtomicFile file = createStorageFileForUser(
+ association.getUserId(), FILE_NAME_LAST_REMOVED_ASSOCIATION);
+ writeToFileSafely(file, out -> {
+ out.write(String.valueOf(System.currentTimeMillis()).getBytes());
+ out.write(' ');
+ out.write(reason.getBytes());
+ out.write(' ');
+ out.write(association.toString().getBytes());
+ });
+ }
+
@NonNull
private static Associations readAssociationsFromFile(@UserIdInt int userId,
@NonNull AtomicFile file, @NonNull String rootTag) {
diff --git a/services/companion/java/com/android/server/companion/association/AssociationStore.java b/services/companion/java/com/android/server/companion/association/AssociationStore.java
index 757abd9..f70c434 100644
--- a/services/companion/java/com/android/server/companion/association/AssociationStore.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationStore.java
@@ -276,7 +276,7 @@
/**
* Remove an association.
*/
- public void removeAssociation(int id) {
+ public void removeAssociation(int id, String reason) {
Slog.i(TAG, "Removing association id=[" + id + "]...");
final AssociationInfo association;
@@ -291,6 +291,8 @@
writeCacheToDisk(association.getUserId());
+ mDiskStore.writeLastRemovedAssociation(association, reason);
+
Slog.i(TAG, "Done removing association.");
}
@@ -525,6 +527,14 @@
out.append(" ").append(a.toString()).append('\n');
}
}
+
+ out.append("Last Removed Association:\n");
+ for (UserInfo user : mUserManager.getAliveUsers()) {
+ String lastRemovedAssociation = mDiskStore.readLastRemovedAssociation(user.id);
+ if (lastRemovedAssociation != null) {
+ out.append(" ").append(lastRemovedAssociation).append('\n');
+ }
+ }
}
private void broadcastChange(@ChangeType int changeType, AssociationInfo association) {
diff --git a/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java b/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java
index 150e8da..248056f3 100644
--- a/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java
+++ b/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java
@@ -47,6 +47,13 @@
@SuppressLint("LongLogTag")
public class DisassociationProcessor {
+ public static final String REASON_REVOKED = "revoked";
+ public static final String REASON_SELF_IDLE = "self-idle";
+ public static final String REASON_SHELL = "shell";
+ public static final String REASON_LEGACY = "legacy";
+ public static final String REASON_API = "api";
+ public static final String REASON_PKG_DATA_CLEARED = "pkg-data-cleared";
+
private static final String TAG = "CDM_DisassociationProcessor";
private static final String SYS_PROP_DEBUG_REMOVAL_TIME_WINDOW =
@@ -94,7 +101,7 @@
* Disassociate an association by id.
*/
// TODO: also revoke notification access
- public void disassociate(int id) {
+ public void disassociate(int id, String reason) {
Slog.i(TAG, "Disassociating id=[" + id + "]...");
final AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(id);
@@ -126,7 +133,7 @@
// Association cleanup.
mSystemDataTransferRequestStore.removeRequestsByAssociationId(userId, id);
- mAssociationStore.removeAssociation(association.getId());
+ mAssociationStore.removeAssociation(association.getId(), reason);
// If role is not in use by other associations, revoke the role.
// Do not need to remove the system role since it was pre-granted by the system.
@@ -151,7 +158,7 @@
}
/**
- * @deprecated Use {@link #disassociate(int)} instead.
+ * @deprecated Use {@link #disassociate(int, String)} instead.
*/
@Deprecated
public void disassociate(int userId, String packageName, String macAddress) {
@@ -165,7 +172,7 @@
mAssociationStore.getAssociationWithCallerChecks(association.getId());
- disassociate(association.getId());
+ disassociate(association.getId(), REASON_LEGACY);
}
@SuppressLint("MissingPermission")
@@ -223,7 +230,7 @@
Slog.i(TAG, "Removing inactive self-managed association=[" + association.toShortString()
+ "].");
- disassociate(id);
+ disassociate(id, REASON_SELF_IDLE);
}
}
@@ -234,7 +241,7 @@
*
* Lastly remove the role holder for the revoked associations for the same packages.
*
- * @see #disassociate(int)
+ * @see #disassociate(int, String)
*/
private class OnPackageVisibilityChangeListener implements
ActivityManager.OnUidImportanceListener {
@@ -260,7 +267,7 @@
int userId = UserHandle.getUserId(uid);
for (AssociationInfo association : mAssociationStore.getRevokedAssociations(userId,
packageName)) {
- disassociate(association.getId());
+ disassociate(association.getId(), REASON_REVOKED);
}
if (mAssociationStore.getRevokedAssociations().isEmpty()) {
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
index d8e10f8..7eb7072 100644
--- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
@@ -365,7 +365,7 @@
}
}
final ScreenshotHardwareBuffer shb = mWmInternal.takeContextualSearchScreenshot(
- (Flags.contextualSearchWindowLayer() ? csUid : -1));
+ (Flags.contextualSearchPreventSelfCapture() ? csUid : -1));
final Bitmap bm = shb != null ? shb.asBitmap() : null;
// Now that everything is fetched, putting it in the launchIntent.
if (bm != null) {
@@ -549,7 +549,7 @@
Binder.withCleanCallingIdentity(() -> {
final ScreenshotHardwareBuffer shb =
mWmInternal.takeContextualSearchScreenshot(
- (Flags.contextualSearchWindowLayer() ? callingUid : -1));
+ (Flags.contextualSearchPreventSelfCapture() ? callingUid : -1));
final Bitmap bm = shb != null ? shb.asBitmap() : null;
if (bm != null) {
bundle.putParcelable(ContextualSearchManager.EXTRA_SCREENSHOT,
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 61c5501..13d367a 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -446,6 +446,8 @@
private static final int CACHING_UI_SERVICE_CLIENT_ADJ_THRESHOLD =
Flags.raiseBoundUiServiceThreshold() ? SERVICE_ADJ : PERCEPTIBLE_APP_ADJ;
+ static final long PERCEPTIBLE_TASK_TIMEOUT_MILLIS = 5 * 60 * 1000;
+
@VisibleForTesting
public static class Injector {
boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId,
@@ -1847,7 +1849,7 @@
mHasVisibleActivities = false;
}
- void onOtherActivity() {
+ void onOtherActivity(long perceptibleTaskStoppedTimeMillis) {
if (procState > PROCESS_STATE_CACHED_ACTIVITY) {
procState = PROCESS_STATE_CACHED_ACTIVITY;
mAdjType = "cch-act";
@@ -1856,6 +1858,28 @@
"Raise procstate to cached activity: " + app);
}
}
+ if (Flags.perceptibleTasks() && adj > PERCEPTIBLE_MEDIUM_APP_ADJ) {
+ if (perceptibleTaskStoppedTimeMillis >= 0) {
+ final long now = mInjector.getUptimeMillis();
+ if (now - perceptibleTaskStoppedTimeMillis < PERCEPTIBLE_TASK_TIMEOUT_MILLIS) {
+ adj = PERCEPTIBLE_MEDIUM_APP_ADJ;
+ mAdjType = "perceptible-act";
+ if (procState > PROCESS_STATE_IMPORTANT_BACKGROUND) {
+ procState = PROCESS_STATE_IMPORTANT_BACKGROUND;
+ }
+
+ maybeSetProcessFollowUpUpdateLocked(app,
+ perceptibleTaskStoppedTimeMillis + PERCEPTIBLE_TASK_TIMEOUT_MILLIS,
+ now);
+ } else if (adj > PREVIOUS_APP_ADJ) {
+ adj = PREVIOUS_APP_ADJ;
+ mAdjType = "stale-perceptible-act";
+ if (procState > PROCESS_STATE_LAST_ACTIVITY) {
+ procState = PROCESS_STATE_LAST_ACTIVITY;
+ }
+ }
+ }
+ }
mHasVisibleActivities = false;
}
}
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index b0f808b..25175e6 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -1120,7 +1120,8 @@
} else if ((flags & ACTIVITY_STATE_FLAG_IS_STOPPING) != 0) {
callback.onStoppingActivity((flags & ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING) != 0);
} else {
- callback.onOtherActivity();
+ final long ts = mApp.getWindowProcessController().getPerceptibleTaskStoppedTimeMillis();
+ callback.onOtherActivity(ts);
}
mCachedAdj = callback.adj;
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index 27c384a..c8fedf3 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -293,6 +293,13 @@
}
flag {
+ name: "perceptible_tasks"
+ namespace: "system_performance"
+ description: "Boost the oom_score_adj of activities in perceptible tasks"
+ bug: "370890207"
+}
+
+flag {
name: "expedite_activity_launch_on_cold_start"
namespace: "system_performance"
description: "Notify ActivityTaskManager of cold starts early to fix app launch behavior."
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 508bc2f..dfdd9e5 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3485,7 +3485,7 @@
|| (windowPerceptible != null && windowPerceptible == perceptible)) {
return;
}
- mFocusedWindowPerceptible.put(windowToken, windowPerceptible);
+ mFocusedWindowPerceptible.put(windowToken, perceptible);
updateSystemUiLocked(userId);
}
});
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 0fc182f..fff812c 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -42,6 +42,7 @@
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED;
+import static com.android.server.notification.PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE;
import static com.android.server.notification.PreferencesHelper.LockableAppFields.USER_LOCKED_PROMOTABLE;
import android.annotation.FlaggedApi;
@@ -286,7 +287,7 @@
if (!TAG_RANKING.equals(tag)) return;
final int xmlVersion = parser.getAttributeInt(null, ATT_VERSION, -1);
- boolean upgradeForBubbles = xmlVersion == XML_VERSION_BUBBLES_UPGRADE;
+ boolean upgradeForBubbles = xmlVersion >= XML_VERSION_BUBBLES_UPGRADE;
boolean migrateToPermission = (xmlVersion < XML_VERSION_NOTIF_PERMISSION);
if (mShowReviewPermissionsNotification
&& (xmlVersion < XML_VERSION_REVIEW_PERMISSIONS_NOTIFICATION)) {
@@ -337,15 +338,19 @@
}
boolean skipWarningLogged = false;
boolean skipGroupWarningLogged = false;
- boolean hasSAWPermission = false;
- if (upgradeForBubbles && uid != UNKNOWN_UID) {
- hasSAWPermission = mAppOps.noteOpNoThrow(
- OP_SYSTEM_ALERT_WINDOW, uid, name, null,
- "check-notif-bubble") == AppOpsManager.MODE_ALLOWED;
+ int bubblePref = parser.getAttributeInt(null, ATT_ALLOW_BUBBLE,
+ DEFAULT_BUBBLE_PREFERENCE);
+ boolean bubbleLocked = (parser.getAttributeInt(null,
+ ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS) & USER_LOCKED_BUBBLE)
+ != 0;
+ if (!bubbleLocked
+ && upgradeForBubbles
+ && uid != UNKNOWN_UID
+ && mAppOps.noteOpNoThrow(OP_SYSTEM_ALERT_WINDOW, uid, name, null,
+ "check-notif-bubble") == AppOpsManager.MODE_ALLOWED) {
+ // User hasn't changed bubble pref & the app has SAW, so allow all bubbles.
+ bubblePref = BUBBLE_PREFERENCE_ALL;
}
- int bubblePref = hasSAWPermission
- ? BUBBLE_PREFERENCE_ALL
- : parser.getAttributeInt(null, ATT_ALLOW_BUBBLE, DEFAULT_BUBBLE_PREFERENCE);
int appImportance = parser.getAttributeInt(null, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
// when data is loaded from disk it's loaded as USER_ALL, but restored data that
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 9692b69..aa3b500 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -493,6 +493,7 @@
private long createTime = System.currentTimeMillis();
long lastVisibleTime; // last time this activity became visible
long pauseTime; // last time we started pausing the activity
+ long mStoppedTime; // last time we completely stopped the activity
long launchTickTime; // base time for launch tick messages
long topResumedStateLossTime; // last time we reported top resumed state loss to an activity
// Last configuration reported to the activity in the client process.
@@ -6447,6 +6448,7 @@
Slog.w(TAG, "Exception thrown during pause", e);
// Just in case, assume it to be stopped.
mAppStopped = true;
+ mStoppedTime = SystemClock.uptimeMillis();
ProtoLog.v(WM_DEBUG_STATES, "Stop failed; moving to STOPPED: %s", this);
setState(STOPPED, "stopIfPossible");
}
@@ -6480,6 +6482,7 @@
if (isStopping) {
ProtoLog.v(WM_DEBUG_STATES, "Moving to STOPPED: %s (stop complete)", this);
+ mStoppedTime = SystemClock.uptimeMillis();
setState(STOPPED, "activityStopped");
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 3778378..6f83822 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2130,6 +2130,26 @@
}
@Override
+ public boolean setTaskIsPerceptible(int taskId, boolean isPerceptible) {
+ enforceTaskPermission("setTaskIsPerceptible()");
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final Task task = mRootWindowContainer.anyTaskForId(taskId,
+ MATCH_ATTACHED_TASK_ONLY);
+ if (task == null) {
+ Slog.w(TAG, "setTaskIsPerceptible: No task to set with id=" + taskId);
+ return false;
+ }
+ task.mIsPerceptible = isPerceptible;
+ }
+ return true;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
public boolean removeTask(int taskId) {
mAmInternal.enforceCallingPermission(REMOVE_TASKS, "removeTask()");
synchronized (mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/PresentationController.java b/services/core/java/com/android/server/wm/PresentationController.java
index 6946343..b3cff9c 100644
--- a/services/core/java/com/android/server/wm/PresentationController.java
+++ b/services/core/java/com/android/server/wm/PresentationController.java
@@ -56,11 +56,6 @@
ProtoLog.v(WmProtoLogGroups.WM_DEBUG_PRESENTATION, "Presentation added to display %d: %s",
win.getDisplayId(), win);
mPresentingDisplayIds.add(win.getDisplayId());
- if (enablePresentationForConnectedDisplays()) {
- // A presentation hides all activities behind on the same display.
- win.mDisplayContent.ensureActivitiesVisible(/*starting=*/ null,
- /*notifyClients=*/ true);
- }
win.mWmService.mDisplayManagerInternal.onPresentation(displayId, /*isShown=*/ true);
}
@@ -76,11 +71,6 @@
if (displayIdIndex != -1) {
mPresentingDisplayIds.remove(displayIdIndex);
}
- if (enablePresentationForConnectedDisplays()) {
- // A presentation hides all activities behind on the same display.
- win.mDisplayContent.ensureActivitiesVisible(/*starting=*/ null,
- /*notifyClients=*/ true);
- }
win.mWmService.mDisplayManagerInternal.onPresentation(displayId, /*isShown=*/ false);
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 6b3499a..988af44 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -504,6 +504,17 @@
int mOffsetYForInsets;
/**
+ * When set to true, the task will be kept at a PERCEPTIBLE_APP_ADJ, and downgraded
+ * to PREVIOUS_APP_ADJ if not in foreground for a period of time.
+ * One example use case is for desktop form factors, where it is important keep tasks in the
+ * perceptible state (rather than cached where it may be frozen) when a user moves it to the
+ * foreground.
+ * On startup, restored Tasks will not be perceptible, until user actually interacts with it
+ * (i.e. brings it to the foreground)
+ */
+ boolean mIsPerceptible = false;
+
+ /**
* Whether the compatibility overrides that change the resizability of the app should be allowed
* for the specific app.
*/
@@ -3854,6 +3865,7 @@
pw.print(ActivityInfo.resizeModeToString(mResizeMode));
pw.print(" mSupportsPictureInPicture="); pw.print(mSupportsPictureInPicture);
pw.print(" isResizeable="); pw.println(isResizeable());
+ pw.print(" isPerceptible="); pw.println(mIsPerceptible);
pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime);
pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)");
pw.print(prefix); pw.println(" isTrimmable=" + mIsTrimmableFromRecents);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0b20911..8aed91b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -157,6 +157,7 @@
import static com.android.server.wm.WindowManagerServiceDumpProto.ROOT_WINDOW_CONTAINER;
import static com.android.server.wm.WindowManagerServiceDumpProto.WINDOW_FRAMES_VALID;
import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
+import static com.android.window.flags.Flags.enablePresentationForConnectedDisplays;
import static com.android.window.flags.Flags.multiCrop;
import static com.android.window.flags.Flags.setScPropertiesInClient;
@@ -1820,8 +1821,28 @@
final boolean hideSystemAlertWindows = shouldHideNonSystemOverlayWindow(win);
win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);
- res |= addWindowInner(win, displayPolicy, activity, displayContent, outInsetsState,
- outAttachedFrame, outActiveControls, client, outSizeCompatScale, attrs);
+ // Only a presentation window needs a transition because its visibility affets the
+ // lifecycle of apps below (b/390481865).
+ if (enablePresentationForConnectedDisplays() && win.isPresentation()) {
+ Transition transition = null;
+ if (!win.mTransitionController.isCollecting()) {
+ transition = win.mTransitionController.createAndStartCollecting(TRANSIT_OPEN);
+ }
+ win.mTransitionController.collect(win.mToken);
+ res |= addWindowInner(win, displayPolicy, activity, displayContent, outInsetsState,
+ outAttachedFrame, outActiveControls, client, outSizeCompatScale, attrs);
+ // A presentation hides all activities behind on the same display.
+ win.mDisplayContent.ensureActivitiesVisible(/*starting=*/ null,
+ /*notifyClients=*/ true);
+ win.mTransitionController.getCollectingTransition().setReady(win.mToken, true);
+ if (transition != null) {
+ win.mTransitionController.requestStartTransition(transition, null,
+ null /* remoteTransition */, null /* displayChange */);
+ }
+ } else {
+ res |= addWindowInner(win, displayPolicy, activity, displayContent, outInsetsState,
+ outAttachedFrame, outActiveControls, client, outSizeCompatScale, attrs);
+ }
}
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 30dde54..b2d28a3 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -37,6 +37,7 @@
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityRecord.State.STARTED;
import static com.android.server.wm.ActivityRecord.State.STOPPING;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
@@ -344,6 +345,12 @@
*/
private volatile int mActivityStateFlags = ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER;
+ /**
+ * The most recent timestamp of when one of this process's stopped activities in a
+ * perceptible task became stopped. Written by window manager and read by activity manager.
+ */
+ private volatile long mPerceptibleTaskStoppedTimeMillis = Long.MIN_VALUE;
+
public WindowProcessController(@NonNull ActivityTaskManagerService atm,
@NonNull ApplicationInfo info, String name, int uid, int userId, Object owner,
@NonNull WindowProcessListener listener) {
@@ -1228,6 +1235,17 @@
return mActivityStateFlags;
}
+ /**
+ * Returns the most recent timestamp when one of this process's stopped activities in a
+ * perceptible task became stopped. It should only be called if {@link #hasActivities}
+ * returns {@code true} and {@link #getActivityStateFlags} does not have any of
+ * the ACTIVITY_STATE_FLAG_IS_(VISIBLE|PAUSING_OR_PAUSED|STOPPING) bit set.
+ */
+ @HotPath(caller = HotPath.OOM_ADJUSTMENT)
+ public long getPerceptibleTaskStoppedTimeMillis() {
+ return mPerceptibleTaskStoppedTimeMillis;
+ }
+
void computeProcessActivityState() {
// Since there could be more than one activities in a process record, we don't need to
// compute the OomAdj with each of them, just need to find out the activity with the
@@ -1239,6 +1257,7 @@
int minTaskLayer = Integer.MAX_VALUE;
int stateFlags = 0;
int nonOccludedRatio = 0;
+ long perceptibleTaskStoppedTimeMillis = Long.MIN_VALUE;
final boolean wasResumed = hasResumedActivity();
final boolean wasAnyVisible = (mActivityStateFlags
& (ACTIVITY_STATE_FLAG_IS_VISIBLE | ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE)) != 0;
@@ -1287,6 +1306,11 @@
bestInvisibleState = STOPPING;
// Not "finishing" if any of activity isn't finishing.
allStoppingFinishing &= r.finishing;
+ } else if (bestInvisibleState == DESTROYED && r.isState(STOPPED)) {
+ if (task.mIsPerceptible) {
+ perceptibleTaskStoppedTimeMillis =
+ Long.max(r.mStoppedTime, perceptibleTaskStoppedTimeMillis);
+ }
}
}
}
@@ -1324,6 +1348,7 @@
}
}
mActivityStateFlags = stateFlags;
+ mPerceptibleTaskStoppedTimeMillis = perceptibleTaskStoppedTimeMillis;
final boolean anyVisible = (stateFlags
& (ACTIVITY_STATE_FLAG_IS_VISIBLE | ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE)) != 0;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 9f1289b2..92ad2ce 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -95,6 +95,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
+import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
@@ -182,6 +183,7 @@
import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY;
import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
+import static com.android.window.flags.Flags.enablePresentationForConnectedDisplays;
import static com.android.window.flags.Flags.surfaceTrustedOverlay;
import android.annotation.CallSuper;
@@ -2297,11 +2299,6 @@
dc.updateImeInputAndControlTarget(null);
}
- final int type = mAttrs.type;
-
- if (isPresentation()) {
- mWmService.mPresentationController.onPresentationRemoved(this);
- }
// Check if window provides non decor insets before clearing its provided insets.
final boolean windowProvidesDisplayDecorInsets = providesDisplayDecorInsets();
@@ -2442,11 +2439,33 @@
}
}
- removeImmediately();
- mWmService.updateFocusedWindowLocked(isFocused()
- ? UPDATE_FOCUS_REMOVING_FOCUS
- : UPDATE_FOCUS_NORMAL,
- true /*updateInputWindows*/);
+ // Only a presentation window needs a transition because its visibility affets the
+ // lifecycle of apps below (b/390481865).
+ if (enablePresentationForConnectedDisplays() && isPresentation()) {
+ Transition transition = null;
+ if (!mTransitionController.isCollecting()) {
+ transition = mTransitionController.createAndStartCollecting(TRANSIT_CLOSE);
+ }
+ mTransitionController.collect(mToken);
+ mAnimatingExit = true;
+ mRemoveOnExit = true;
+ mToken.setVisibleRequested(false);
+ mWmService.mPresentationController.onPresentationRemoved(this);
+ // A presentation hides all activities behind on the same display.
+ mDisplayContent.ensureActivitiesVisible(/*starting=*/ null,
+ /*notifyClients=*/ true);
+ mTransitionController.getCollectingTransition().setReady(mToken, true);
+ if (transition != null) {
+ mTransitionController.requestStartTransition(transition, null,
+ null /* remoteTransition */, null /* displayChange */);
+ }
+ } else {
+ removeImmediately();
+ mWmService.updateFocusedWindowLocked(isFocused()
+ ? UPDATE_FOCUS_REMOVING_FOCUS
+ : UPDATE_FOCUS_NORMAL,
+ true /*updateInputWindows*/);
+ }
} finally {
Binder.restoreCallingIdentity(origId);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 191c21e..aee32a0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -423,6 +423,7 @@
import android.database.Cursor;
import android.graphics.Bitmap;
import android.hardware.usb.UsbManager;
+import android.health.connect.HealthConnectManager;
import android.location.Location;
import android.location.LocationManager;
import android.media.AudioManager;
@@ -2149,6 +2150,14 @@
.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
mBackgroundHandler = BackgroundThread.getHandler();
+ // Add the health permission to the list of restricted permissions.
+ if (android.permission.flags.Flags.replaceBodySensorPermissionEnabled()) {
+ Set<String> healthPermissions = HealthConnectManager.getHealthPermissions(mContext);
+ for (String permission : healthPermissions) {
+ SENSOR_PERMISSIONS.add(permission);
+ }
+ }
+
// Needed when mHasFeature == false, because it controls the certificate warning text.
mCertificateMonitor = new CertificateMonitor(this, mInjector, mBackgroundHandler);
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 1ef758c..340115a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -3337,6 +3337,108 @@
followUpTimeCaptor.capture());
}
+ /**
+ * For Perceptible Tasks adjustment, this solely unit-tests OomAdjuster -> onOtherActivity()
+ */
+ @SuppressWarnings("GuardedBy")
+ @Test
+ @EnableFlags(Flags.FLAG_PERCEPTIBLE_TASKS)
+ public void testPerceptibleAdjustment() {
+ ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
+
+ long now = mInjector.getUptimeMillis();
+
+ // GIVEN: perceptible adjustment is NOT enabled (perceptible stop time is not set)
+ // EXPECT: zero adjustment
+ // TLDR: App is not set as a perceptible task and hence no oom_adj boosting.
+ mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.initialize(app, CACHED_APP_MIN_ADJ,
+ false, false, PROCESS_STATE_CACHED_ACTIVITY,
+ SCHED_GROUP_DEFAULT, 0, 0, PROCESS_STATE_IMPORTANT_FOREGROUND);
+ mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.onOtherActivity(-1);
+ assertEquals(CACHED_APP_MIN_ADJ, mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.adj);
+
+ // GIVEN: perceptible adjustment is enabled (perceptible stop time is set) and
+ // elapsed time < PERCEPTIBLE_TASK_TIMEOUT
+ // EXPECT: adjustment to PERCEPTIBLE_MEDIUM_APP_ADJ
+ // TLDR: App is a perceptible task (e.g. opened from launcher) and has oom_adj boosting.
+ mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.initialize(app, CACHED_APP_MIN_ADJ,
+ false, false, PROCESS_STATE_CACHED_ACTIVITY,
+ SCHED_GROUP_DEFAULT, 0, 0, PROCESS_STATE_IMPORTANT_FOREGROUND);
+ mInjector.reset();
+ mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.onOtherActivity(now);
+ assertEquals(PERCEPTIBLE_MEDIUM_APP_ADJ,
+ mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.adj);
+
+ // GIVEN: perceptible adjustment is enabled (perceptible stop time is set) and
+ // elapsed time > PERCEPTIBLE_TASK_TIMEOUT
+ // EXPECT: adjustment to PREVIOUS_APP_ADJ
+ // TLDR: App is a perceptible task (e.g. opened from launcher) and has oom_adj boosting, but
+ // time has elapsed and has dropped to a lower boosting of PREVIOUS_APP_ADJ
+ mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.initialize(app, CACHED_APP_MIN_ADJ,
+ false, false, PROCESS_STATE_CACHED_ACTIVITY,
+ SCHED_GROUP_DEFAULT, 0, 0, PROCESS_STATE_IMPORTANT_FOREGROUND);
+ mInjector.jumpUptimeAheadTo(OomAdjuster.PERCEPTIBLE_TASK_TIMEOUT_MILLIS + 1000);
+ mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.onOtherActivity(0);
+ assertEquals(PREVIOUS_APP_ADJ, mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.adj);
+ }
+
+ /**
+ * For Perceptible Tasks adjustment, this tests overall adjustment flow.
+ */
+ @SuppressWarnings("GuardedBy")
+ @Test
+ @EnableFlags(Flags.FLAG_PERCEPTIBLE_TASKS)
+ public void testUpdateOomAdjPerceptible() {
+ ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
+ WindowProcessController wpc = app.getWindowProcessController();
+
+ // Set uptime to be at least the timeout time + buffer, so that we don't end up with
+ // negative stopTime in our test input
+ mInjector.jumpUptimeAheadTo(OomAdjuster.PERCEPTIBLE_TASK_TIMEOUT_MILLIS + 60L * 1000L);
+ long now = mInjector.getUptimeMillis();
+ doReturn(true).when(wpc).hasActivities();
+
+ // GIVEN: perceptible adjustment is is enabled
+ // EXPECT: perceptible-act adjustment
+ doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING)
+ .when(wpc).getActivityStateFlags();
+ doReturn(now).when(wpc).getPerceptibleTaskStoppedTimeMillis();
+ updateOomAdj(app);
+ assertProcStates(app, PROCESS_STATE_IMPORTANT_BACKGROUND, PERCEPTIBLE_MEDIUM_APP_ADJ,
+ SCHED_GROUP_BACKGROUND, "perceptible-act");
+
+ // GIVEN: perceptible adjustment is is enabled and timeout has been reached
+ // EXPECT: stale-perceptible-act adjustment
+ doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING)
+ .when(wpc).getActivityStateFlags();
+
+ doReturn(now - OomAdjuster.PERCEPTIBLE_TASK_TIMEOUT_MILLIS).when(
+ wpc).getPerceptibleTaskStoppedTimeMillis();
+ updateOomAdj(app);
+ assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
+ SCHED_GROUP_BACKGROUND, "stale-perceptible-act");
+
+ // GIVEN: perceptible adjustment is is disabled
+ // EXPECT: no perceptible adjustment
+ doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING)
+ .when(wpc).getActivityStateFlags();
+ doReturn(Long.MIN_VALUE).when(wpc).getPerceptibleTaskStoppedTimeMillis();
+ updateOomAdj(app);
+ assertProcStates(app, PROCESS_STATE_CACHED_ACTIVITY, CACHED_APP_MIN_ADJ,
+ SCHED_GROUP_BACKGROUND, "cch-act");
+
+ // GIVEN: perceptible app is in foreground
+ // EXPECT: no perceptible adjustment
+ doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE)
+ .when(wpc).getActivityStateFlags();
+ doReturn(now).when(wpc).getPerceptibleTaskStoppedTimeMillis();
+ updateOomAdj(app);
+ assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ,
+ SCHED_GROUP_DEFAULT, "vis-activity");
+ }
+
@SuppressWarnings("GuardedBy")
@Test
public void testUpdateOomAdj_DoAll_Multiple_Provider_Retention() {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index e5fac7a..00b0c55 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -263,6 +263,11 @@
}
@Override
+ public Context getApplicationContext() {
+ return this;
+ }
+
+ @Override
public PackageManager getPackageManager() {
return mMockSystemServices.packageManager;
}
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 5dea44d..67e85ff 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -4495,6 +4495,27 @@
}
@Test
+ public void testBubblePreference_sameVersionWithSAWPermission() throws Exception {
+ when(mAppOpsManager.noteOpNoThrow(eq(OP_SYSTEM_ALERT_WINDOW), anyInt(),
+ anyString(), eq(null), anyString())).thenReturn(MODE_ALLOWED);
+
+ final String xml = "<ranking version=\"4\">\n"
+ + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\">\n"
+ + "<channel id=\"someId\" name=\"hi\""
+ + " importance=\"3\"/>"
+ + "</package>"
+ + "</ranking>";
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
+ null);
+ parser.nextTag();
+ mHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+ assertEquals(BUBBLE_PREFERENCE_ALL, mHelper.getBubblePreference(PKG_O, UID_O));
+ assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O));
+ }
+
+ @Test
public void testBubblePreference_upgradeWithSAWThenUserOverride() throws Exception {
when(mAppOpsManager.noteOpNoThrow(eq(OP_SYSTEM_ALERT_WINDOW), anyInt(),
anyString(), eq(null), anyString())).thenReturn(MODE_ALLOWED);
diff --git a/services/tests/wmtests/src/com/android/server/wm/PresentationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/PresentationControllerTests.java
index db90c28..7c8a883 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PresentationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PresentationControllerTests.java
@@ -17,14 +17,18 @@
package com.android.server.wm;
import static android.view.Display.FLAG_PRESENTATION;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OPEN;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.window.flags.Flags.FLAG_ENABLE_PRESENTATION_FOR_CONNECTED_DISPLAYS;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
+import android.annotation.NonNull;
import android.graphics.Rect;
import android.os.UserHandle;
import android.os.UserManager;
@@ -41,6 +45,7 @@
import androidx.test.filters.SmallTest;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,9 +58,16 @@
@RunWith(WindowTestRunner.class)
public class PresentationControllerTests extends WindowTestsBase {
+ TestTransitionPlayer mPlayer;
+
+ @Before
+ public void setUp() {
+ mPlayer = registerTestTransitionPlayer();
+ }
+
@EnableFlags(FLAG_ENABLE_PRESENTATION_FOR_CONNECTED_DISPLAYS)
@Test
- public void testPresentationHidesActivitiesBehind() {
+ public void testPresentationShowAndHide() {
final DisplayInfo displayInfo = new DisplayInfo();
displayInfo.copyFrom(mDisplayInfo);
displayInfo.flags = FLAG_PRESENTATION;
@@ -64,7 +76,6 @@
doReturn(dc).when(mWm.mRoot).getDisplayContentOrCreate(displayId);
final ActivityRecord activity = createActivityRecord(createTask(dc));
assertTrue(activity.isVisible());
-
doReturn(true).when(() -> UserManager.isVisibleBackgroundUsersEnabled());
final int uid = 100000; // uid for non-system user
final Session session = createTestSession(mAtm, 1234 /* pid */, uid);
@@ -72,16 +83,48 @@
doReturn(false).when(mWm.mUmInternal).isUserVisible(eq(userId), eq(displayId));
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_PRESENTATION);
-
final IWindow clientWindow = new TestIWindow();
+
+ // Show a Presentation window, which requests the activity to be stopped.
final int result = mWm.addWindow(session, clientWindow, params, View.VISIBLE, displayId,
userId, WindowInsets.Type.defaultVisible(), null, new InsetsState(),
new InsetsSourceControl.Array(), new Rect(), new float[1]);
assertTrue(result >= WindowManagerGlobal.ADD_OKAY);
+ assertFalse(activity.isVisibleRequested());
+ assertTrue(activity.isVisible());
+ final WindowState window = mWm.windowForClientLocked(session, clientWindow, false);
+ window.mHasSurface = true;
+ final Transition addTransition = window.mTransitionController.getCollectingTransition();
+ assertEquals(TRANSIT_OPEN, addTransition.mType);
+ assertTrue(addTransition.isInTransition(window));
+ assertTrue(addTransition.isInTransition(activity));
+
+ // Completing the transition makes the activity invisible.
+ completeTransition(addTransition, /*abortSync=*/ true);
assertFalse(activity.isVisible());
- final WindowState window = mWm.windowForClientLocked(session, clientWindow, false);
- window.removeImmediately();
+ // Remove a Presentation window, which requests the activity to be resumed back.
+ window.removeIfPossible();
+ final Transition removeTransition = window.mTransitionController.getCollectingTransition();
+ assertEquals(TRANSIT_CLOSE, removeTransition.mType);
+ assertTrue(removeTransition.isInTransition(window));
+ assertTrue(removeTransition.isInTransition(activity));
+ assertTrue(activity.isVisibleRequested());
+ assertFalse(activity.isVisible());
+
+ // Completing the transition makes the activity visible.
+ completeTransition(removeTransition, /*abortSync=*/ false);
assertTrue(activity.isVisible());
}
+
+ private void completeTransition(@NonNull Transition transition, boolean abortSync) {
+ final ActionChain chain = ActionChain.testFinish(transition);
+ if (abortSync) {
+ // Forcefully finishing the active sync for testing purpose.
+ mWm.mSyncEngine.abort(transition.getSyncId());
+ } else {
+ transition.onTransactionReady(transition.getSyncId(), mTransaction);
+ }
+ transition.finishTransition(chain);
+ }
}
diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java
index 7a19add..8cd89ce 100644
--- a/tests/testables/src/android/testing/TestableLooper.java
+++ b/tests/testables/src/android/testing/TestableLooper.java
@@ -34,6 +34,7 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
+import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.Map;
import java.util.Objects;
@@ -73,15 +74,21 @@
/**
* Baklava introduces new {@link TestLooperManager} APIs that we can use instead of reflection.
*/
- private static boolean newTestabilityApisSupported() {
- return android.os.Flags.messageQueueTestability();
+ private static boolean isAtLeastBaklava() {
+ Method[] methods = TestLooperManager.class.getMethods();
+ for (Method method : methods) {
+ if (method.getName().equals("peekWhen")) {
+ return true;
+ }
+ }
+ return false;
// TODO(shayba): delete the above, uncomment the below.
// SDK_INT has not yet ramped to Baklava in all 25Q2 builds.
// return Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA;
}
static {
- if (newTestabilityApisSupported()) {
+ if (isAtLeastBaklava()) {
MESSAGE_QUEUE_MESSAGES_FIELD = null;
MESSAGE_NEXT_FIELD = null;
MESSAGE_WHEN_FIELD = null;
@@ -241,14 +248,14 @@
}
public void moveTimeForward(long milliSeconds) {
- if (newTestabilityApisSupported()) {
- moveTimeForwardModern(milliSeconds);
+ if (isAtLeastBaklava()) {
+ moveTimeForwardBaklava(milliSeconds);
} else {
moveTimeForwardLegacy(milliSeconds);
}
}
- private void moveTimeForwardModern(long milliSeconds) {
+ private void moveTimeForwardBaklava(long milliSeconds) {
// Drain all Messages from the queue.
Queue<Message> messages = new ArrayDeque<>();
while (true) {
diff --git a/tests/utils/testutils/java/android/os/test/TestLooper.java b/tests/utils/testutils/java/android/os/test/TestLooper.java
index c7a36dd..4d379e4 100644
--- a/tests/utils/testutils/java/android/os/test/TestLooper.java
+++ b/tests/utils/testutils/java/android/os/test/TestLooper.java
@@ -65,13 +65,19 @@
private AutoDispatchThread mAutoDispatchThread;
/**
- * Modern introduces new {@link TestLooperManager} APIs that we can use instead of reflection.
+ * Baklava introduces new {@link TestLooperManager} APIs that we can use instead of reflection.
*/
- private static boolean newTestabilityApisSupported() {
- return android.os.Flags.messageQueueTestability();
+ private static boolean isAtLeastBaklava() {
+ Method[] methods = TestLooperManager.class.getMethods();
+ for (Method method : methods) {
+ if (method.getName().equals("peekWhen")) {
+ return true;
+ }
+ }
+ return false;
// TODO(shayba): delete the above, uncomment the below.
- // SDK_INT has not yet ramped to Modern in all 25Q2 builds.
- // return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Modern;
+ // SDK_INT has not yet ramped to Baklava in all 25Q2 builds.
+ // return Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA;
}
static {
@@ -81,7 +87,7 @@
THREAD_LOCAL_LOOPER_FIELD = Looper.class.getDeclaredField("sThreadLocal");
THREAD_LOCAL_LOOPER_FIELD.setAccessible(true);
- if (newTestabilityApisSupported()) {
+ if (isAtLeastBaklava()) {
MESSAGE_QUEUE_MESSAGES_FIELD = null;
MESSAGE_NEXT_FIELD = null;
MESSAGE_WHEN_FIELD = null;
@@ -130,7 +136,7 @@
throw new RuntimeException("Reflection error constructing or accessing looper", e);
}
- if (newTestabilityApisSupported()) {
+ if (isAtLeastBaklava()) {
mTestLooperManager =
InstrumentationRegistry.getInstrumentation().acquireLooperManager(mLooper);
} else {
@@ -159,14 +165,14 @@
}
public void moveTimeForward(long milliSeconds) {
- if (newTestabilityApisSupported()) {
- moveTimeForwardModern(milliSeconds);
+ if (isAtLeastBaklava()) {
+ moveTimeForwardBaklava(milliSeconds);
} else {
moveTimeForwardLegacy(milliSeconds);
}
}
- private void moveTimeForwardModern(long milliSeconds) {
+ private void moveTimeForwardBaklava(long milliSeconds) {
// Drain all Messages from the queue.
Queue<Message> messages = new ArrayDeque<>();
while (true) {
@@ -259,14 +265,14 @@
* @return true if there are pending messages in the message queue
*/
public boolean isIdle() {
- if (newTestabilityApisSupported()) {
- return isIdleModern();
+ if (isAtLeastBaklava()) {
+ return isIdleBaklava();
} else {
return isIdleLegacy();
}
}
- private boolean isIdleModern() {
+ private boolean isIdleBaklava() {
Long when = mTestLooperManager.peekWhen();
return when != null && currentTime() >= when;
}
@@ -280,14 +286,14 @@
* @return the next message in the Looper's message queue or null if there is none
*/
public Message nextMessage() {
- if (newTestabilityApisSupported()) {
- return nextMessageModern();
+ if (isAtLeastBaklava()) {
+ return nextMessageBaklava();
} else {
return nextMessageLegacy();
}
}
- private Message nextMessageModern() {
+ private Message nextMessageBaklava() {
if (isIdle()) {
return mTestLooperManager.poll();
} else {
@@ -308,14 +314,14 @@
* Asserts that there is a message in the queue
*/
public void dispatchNext() {
- if (newTestabilityApisSupported()) {
- dispatchNextModern();
+ if (isAtLeastBaklava()) {
+ dispatchNextBaklava();
} else {
dispatchNextLegacy();
}
}
- private void dispatchNextModern() {
+ private void dispatchNextBaklava() {
assertTrue(isIdle());
Message msg = mTestLooperManager.poll();
if (msg == null) {
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index ff4d8ef..0a5cb1f 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -2649,6 +2649,10 @@
".mpg", ".mpeg", ".mp4", ".m4a", ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", ".wma", ".wmv",
".webm", ".mkv"});
+ if (options_.no_compress_fonts) {
+ options_.extensions_to_not_compress.insert({".ttf", ".otf", ".ttc"});
+ }
+
// Turn off auto versioning for static-libs.
if (context.GetPackageType() == PackageType::kStaticLib) {
options_.no_auto_version = true;
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index 2f17853..9779788 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -78,6 +78,7 @@
bool use_sparse_encoding = false;
std::unordered_set<std::string> extensions_to_not_compress;
std::optional<std::regex> regex_to_not_compress;
+ bool no_compress_fonts = false;
FeatureFlagValues feature_flag_values;
// Static lib options.
@@ -300,6 +301,14 @@
"use the '$' symbol for end of line. Uses a case-sensitive ECMAScript"
"regular expression grammar.",
&no_compress_regex);
+ AddOptionalSwitch("--no-compress-fonts",
+ "Do not compress files with common extensions for fonts.\n"
+ "This allows loading fonts directly from the APK, without needing to\n"
+ "decompress them first. Loading fonts will be faster and use less memory.\n"
+ "The downside is that the APK will be larger.\n"
+ "Passing this flag is functionally equivalent to passing the following flags:\n"
+ "-0 .ttf -0 .otf -0 .ttc",
+ &options_.no_compress_fonts);
AddOptionalSwitch("--warn-manifest-validation",
"Treat manifest validation errors as warnings.",
&options_.manifest_fixer_options.warn_validation);
diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp
index a2dc8f8..41f8e250 100644
--- a/tools/aapt2/cmd/Link_test.cpp
+++ b/tools/aapt2/cmd/Link_test.cpp
@@ -98,6 +98,7 @@
WriteFile(GetTestPath("assets/test.txt"), content);
WriteFile(GetTestPath("assets/test.hello.txt"), content);
WriteFile(GetTestPath("assets/test.hello.xml"), content);
+ WriteFile(GetTestPath("assets/fonts/myfont.ttf"), content);
const std::string out_apk = GetTestPath("out.apk");
std::vector<std::string> link_args = {
@@ -136,6 +137,10 @@
file = zip->FindFile("assets/test.hello.xml");
ASSERT_THAT(file, Ne(nullptr));
EXPECT_FALSE(file->WasCompressed());
+
+ file = zip->FindFile("assets/fonts/myfont.ttf");
+ ASSERT_THAT(file, Ne(nullptr));
+ EXPECT_TRUE(file->WasCompressed());
}
TEST_F(LinkTest, NoCompressResources) {
@@ -182,6 +187,42 @@
EXPECT_FALSE(file->WasCompressed());
}
+TEST_F(LinkTest, NoCompressFonts) {
+ StdErrDiagnostics diag;
+ std::string content(500, 'a');
+ const std::string compiled_files_dir = GetTestPath("compiled");
+ ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test.txt"), content, compiled_files_dir, &diag));
+ WriteFile(GetTestPath("assets/fonts/myfont1.ttf"), content);
+ WriteFile(GetTestPath("assets/fonts/myfont2.ttf"), content);
+
+ const std::string out_apk = GetTestPath("out.apk");
+ std::vector<std::string> link_args = {
+ "--manifest", GetDefaultManifest(),
+ "-o", out_apk,
+ "--no-compress-fonts",
+ "-A", GetTestPath("assets")
+ };
+
+ ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
+
+ std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
+ ASSERT_THAT(apk, Ne(nullptr));
+ io::IFileCollection* zip = apk->GetFileCollection();
+ ASSERT_THAT(zip, Ne(nullptr));
+
+ auto file = zip->FindFile("res/raw/test.txt");
+ ASSERT_THAT(file, Ne(nullptr));
+ EXPECT_TRUE(file->WasCompressed());
+
+ file = zip->FindFile("assets/fonts/myfont1.ttf");
+ ASSERT_THAT(file, Ne(nullptr));
+ EXPECT_FALSE(file->WasCompressed());
+
+ file = zip->FindFile("assets/fonts/myfont2.ttf");
+ ASSERT_THAT(file, Ne(nullptr));
+ EXPECT_FALSE(file->WasCompressed());
+}
+
TEST_F(LinkTest, OverlayStyles) {
StdErrDiagnostics diag;
const std::string compiled_files_dir = GetTestPath("compiled");
diff --git a/tools/aapt2/readme.md b/tools/aapt2/readme.md
index 5c3dfdc..6bdbaae 100644
--- a/tools/aapt2/readme.md
+++ b/tools/aapt2/readme.md
@@ -3,6 +3,8 @@
## Version 2.20
- Too many features, bug fixes, and improvements to list since the last minor version update in
2017. This README will be updated more frequently in the future.
+- Added a new flag `--no-compress-fonts`. This can significantly speed up loading fonts from APK
+ assets, at the cost of increasing the storage size of the APK.
## Version 2.19
- Added navigation resource type.