Merge "Fix isInSection NPE from null representative entry" into main
diff --git a/apct-tests/perftests/core/src/android/os/TracePerfTest.java b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
index 0b941c9..d905124 100644
--- a/apct-tests/perftests/core/src/android/os/TracePerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
@@ -127,14 +127,14 @@
public void testInstantPerfettoWithArgs() {
PerfettoTrace.instant(FOO_CATEGORY, "testInstantP")
.addArg("foo", "bar")
- .addFlow(1)
+ .setFlow(1)
.emit();
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
PerfettoTrace.instant(FOO_CATEGORY, "testInstantP")
.addArg("foo", "bar")
- .addFlow(1)
+ .setFlow(1)
.emit();
}
}
diff --git a/boot/boot-image-profile-extra.txt b/boot/boot-image-profile-extra.txt
index ced0d17..ce99bfe 100644
--- a/boot/boot-image-profile-extra.txt
+++ b/boot/boot-image-profile-extra.txt
@@ -45,11 +45,15 @@
HSPLandroid/os/MessageQueue$StackNodeType;->*
HSPLandroid/os/MessageQueue$StateNode;->*
HSPLandroid/os/MessageQueue$TimedParkStateNode;->*
+
+# For now, compile all methods in PerfettoTrace and PerfettoTrackEventExtra.
+# Similar to the existing Trace APIs, these new APIs can impact the performance
+# of many subsystems including MessageQueue. This also keeps benchmark
+# comparisons between both APIs fair.
HSPLandroid/os/PerfettoTrace$Category;->*
HSPLandroid/os/PerfettoTrace;->*
HSPLandroid/os/PerfettoTrackEventExtra;->*
-HSPLandroid/os/PerfettoTrackEventExtra$BuilderImpl;->*
-HSPLandroid/os/PerfettoTrackEventExtra$NoOpBuilder;->*
+HSPLandroid/os/PerfettoTrackEventExtra$Builder;->*
HSPLandroid/os/PerfettoTrackEventExtra$ArgBool;->*
HSPLandroid/os/PerfettoTrackEventExtra$ArgInt64;->*
HSPLandroid/os/PerfettoTrackEventExtra$ArgDouble;->*
diff --git a/core/api/current.txt b/core/api/current.txt
index 9ebb506..4862236 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -53802,8 +53802,8 @@
method @FlaggedApi("com.android.graphics.hwui.flags.limited_hdr") public void setDesiredHdrHeadroom(@FloatRange(from=0.0f, to=10000.0) float);
method public void setSecure(boolean);
method public void setSurfaceLifecycle(int);
- method public void setZOrderMediaOverlay(boolean);
- method public void setZOrderOnTop(boolean);
+ method @Deprecated @FlaggedApi("android.view.flags.deprecate_surface_view_z_order_apis") public void setZOrderMediaOverlay(boolean);
+ method @Deprecated @FlaggedApi("android.view.flags.deprecate_surface_view_z_order_apis") public void setZOrderOnTop(boolean);
field public static final int SURFACE_LIFECYCLE_DEFAULT = 0; // 0x0
field public static final int SURFACE_LIFECYCLE_FOLLOWS_ATTACHMENT = 2; // 0x2
field public static final int SURFACE_LIFECYCLE_FOLLOWS_VISIBILITY = 1; // 0x1
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 40069aa..526a213 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -165,6 +165,16 @@
package android.media {
+ public class AudioDeviceVolumeManager {
+ method @FlaggedApi("android.media.audio.unify_absolute_volume_management") @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.BLUETOOTH_STACK}) public void setDeviceAbsoluteMultiVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, @NonNull java.util.List<android.media.VolumeInfo>, @NonNull java.util.concurrent.Executor, @NonNull android.media.AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener);
+ method @FlaggedApi("android.media.audio.unify_absolute_volume_management") @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.BLUETOOTH_STACK}) public void setDeviceAbsoluteVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, @NonNull android.media.VolumeInfo, @NonNull java.util.concurrent.Executor, @NonNull android.media.AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener);
+ }
+
+ @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static interface AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener {
+ method public void onAudioDeviceVolumeAdjusted(@NonNull android.media.AudioDeviceAttributes, @NonNull android.media.VolumeInfo, int, int);
+ method public void onAudioDeviceVolumeChanged(@NonNull android.media.AudioDeviceAttributes, @NonNull android.media.VolumeInfo);
+ }
+
public class AudioManager {
method public void adjustStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
method public void adjustSuggestedStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 89b3773..514a582 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1737,6 +1737,7 @@
method @NonNull public int[] getUserDisabledHdrTypes();
method public boolean isMinimalPostProcessingRequested(int);
method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public void overrideHdrTypes(int, @NonNull int[]);
+ method @FlaggedApi("com.android.server.display.feature.flags.delay_implicit_rr_registration_until_rr_accessed") public void resetImplicitRefreshRateCallbackStatus();
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setAreUserDisabledHdrTypesAllowed(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void setGlobalUserPreferredDisplayMode(@NonNull android.view.Display.Mode);
method @RequiresPermission(android.Manifest.permission.MODIFY_HDR_CONVERSION_MODE) public void setHdrConversionMode(@NonNull android.hardware.display.HdrConversionMode);
@@ -2585,7 +2586,7 @@
}
public class UserManager {
- method @FlaggedApi("android.os.allow_private_profile") @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public boolean canAddPrivateProfile();
+ method @FlaggedApi("android.os.allow_private_profile") @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean canAddPrivateProfile();
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createProfileForUser(@Nullable String, @NonNull String, int, int, @Nullable String[]);
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createRestrictedProfile(@Nullable String);
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createUser(@Nullable String, @NonNull String, int);
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 6151b8e..a12c067 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -23,6 +23,7 @@
import android.annotation.Nullable;
import android.annotation.PermissionMethod;
import android.annotation.PermissionName;
+import android.annotation.SpecialUsers.CanBeALL;
import android.annotation.UserIdInt;
import android.app.ActivityManager.ProcessCapability;
import android.app.ActivityManager.RestrictionLevel;
@@ -1365,8 +1366,8 @@
* watchdog reset.
* @hide
*/
- public abstract void killApplicationSync(String pkgName, int appId, int userId,
- String reason, int exitInfoReason);
+ public abstract void killApplicationSync(String pkgName, int appId,
+ @CanBeALL @UserIdInt int userId, String reason, int exitInfoReason);
/**
* Queries the offset data for a given method on a process.
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/Instrumentation.java b/core/java/android/app/Instrumentation.java
index b611acf..eb9feb9 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -50,6 +50,7 @@
import android.os.UserManager;
import android.ravenwood.annotation.RavenwoodKeep;
import android.ravenwood.annotation.RavenwoodKeepPartialClass;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.ravenwood.annotation.RavenwoodReplace;
import android.util.AndroidRuntimeException;
import android.util.Log;
@@ -460,6 +461,7 @@
*
* @param runner The code to run on the main thread.
*/
+ @RavenwoodReplace(blockedBy = ActivityThread.class)
public void runOnMainSync(Runnable runner) {
validateNotAppThread();
SyncRunnable sr = new SyncRunnable(runner);
@@ -467,6 +469,13 @@
sr.waitForComplete();
}
+ private void runOnMainSync$ravenwood(Runnable runner) {
+ validateNotAppThread();
+ SyncRunnable sr = new SyncRunnable(runner);
+ mInstrContext.getMainExecutor().execute(sr);
+ sr.waitForComplete();
+ }
+
boolean isSdkSandboxAllowedToStartActivities() {
return Process.isSdkSandbox()
&& mThread != null
@@ -2442,7 +2451,8 @@
}
}
- private final void validateNotAppThread() {
+ @RavenwoodKeep
+ private void validateNotAppThread() {
if (Looper.myLooper() == Looper.getMainLooper()) {
throw new RuntimeException(
"This method can not be called from the main application thread");
@@ -2586,6 +2596,7 @@
}
}
+ @RavenwoodKeepWholeClass
private static final class SyncRunnable implements Runnable {
private final Runnable mTarget;
private boolean mComplete;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 5dca1c7..719e438 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -18,6 +18,7 @@
import static android.annotation.Dimension.DP;
import static android.app.Flags.evenlyDividedCallStyleActionLayout;
+import static android.app.Flags.notificationsRedesignTemplates;
import static android.app.admin.DevicePolicyResources.Drawables.Source.NOTIFICATION;
import static android.app.admin.DevicePolicyResources.Drawables.Style.SOLID_COLORED;
import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
@@ -818,7 +819,8 @@
R.layout.notification_2025_template_expanded_base,
R.layout.notification_2025_template_heads_up_base,
R.layout.notification_2025_template_header,
- R.layout.notification_2025_template_conversation,
+ R.layout.notification_2025_template_collapsed_conversation,
+ R.layout.notification_2025_template_expanded_conversation,
R.layout.notification_2025_template_collapsed_call,
R.layout.notification_2025_template_expanded_call,
R.layout.notification_2025_template_collapsed_messaging,
@@ -5963,7 +5965,10 @@
|| resId == getCompactHeadsUpBaseLayoutResource()
|| resId == getMessagingCompactHeadsUpLayoutResource()
|| resId == getCollapsedMessagingLayoutResource()
- || resId == getCollapsedMediaLayoutResource());
+ || resId == getCollapsedMediaLayoutResource()
+ || resId == getCollapsedConversationLayoutResource()
+ || (notificationsRedesignTemplates()
+ && resId == getCollapsedCallLayoutResource()));
RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
resetStandardTemplate(contentView);
@@ -6001,8 +6006,7 @@
// Update margins to leave space for the top line (but not for headerless views like
// HUNS, which use a different layout that already accounts for that). Templates that
// have content that will be displayed under the small icon also use a different margin.
- if (Flags.notificationsRedesignTemplates()
- && !p.mHeaderless && !p.mSkipTopLineAlignment) {
+ if (Flags.notificationsRedesignTemplates() && !p.mHeaderless) {
int margin = getContentMarginTop(mContext,
R.dimen.notification_2025_content_margin_top);
contentView.setViewLayoutMargin(R.id.notification_main_column,
@@ -7673,12 +7677,18 @@
}
}
+ // Note: In the 2025 redesign, we use two separate layouts for the collapsed and expanded
+ // version of conversations. See below.
private int getConversationLayoutResource() {
- if (Flags.notificationsRedesignTemplates()) {
- return R.layout.notification_2025_template_conversation;
- } else {
- return R.layout.notification_template_material_conversation;
- }
+ return R.layout.notification_template_material_conversation;
+ }
+
+ private int getCollapsedConversationLayoutResource() {
+ return R.layout.notification_2025_template_collapsed_conversation;
+ }
+
+ private int getExpandedConversationLayoutResource() {
+ return R.layout.notification_2025_template_expanded_conversation;
}
private int getCollapsedCallLayoutResource() {
@@ -9462,7 +9472,7 @@
boolean hideRightIcons = viewType != StandardTemplateParams.VIEW_TYPE_NORMAL;
boolean isConversationLayout = mConversationType != CONVERSATION_TYPE_LEGACY;
boolean isImportantConversation = mConversationType == CONVERSATION_TYPE_IMPORTANT;
- boolean isHeaderless = !isConversationLayout && isCollapsed;
+ boolean isLegacyHeaderless = !isConversationLayout && isCollapsed;
//TODO (b/217799515): ensure mConversationTitle always returns the correct
// conversationTitle, probably set mConversationTitle = conversationTitle after this
@@ -9483,7 +9493,8 @@
} else {
isOneToOne = !isGroupConversation();
}
- if (isHeaderless && isOneToOne && TextUtils.isEmpty(conversationTitle)) {
+ if ((isLegacyHeaderless || notificationsRedesignTemplates())
+ && isOneToOne && TextUtils.isEmpty(conversationTitle)) {
conversationTitle = getOtherPersonName();
}
@@ -9493,22 +9504,24 @@
.viewType(viewType)
.highlightExpander(isConversationLayout)
.hideProgress(true)
- .title(isHeaderless ? conversationTitle : null)
.text(null)
.hideLeftIcon(isOneToOne)
- .hideRightIcon(hideRightIcons || isOneToOne)
- .headerTextSecondary(isHeaderless ? null : conversationTitle)
- .skipTopLineAlignment(true);
+ .hideRightIcon(hideRightIcons || isOneToOne);
+ if (notificationsRedesignTemplates()) {
+ p.title(conversationTitle)
+ .hideAppName(isCollapsed);
+ } else {
+ p.title(isLegacyHeaderless ? conversationTitle : null)
+ .headerTextSecondary(isLegacyHeaderless ? null : conversationTitle);
+ }
RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
- isConversationLayout
- ? mBuilder.getConversationLayoutResource()
- : isCollapsed
- ? mBuilder.getCollapsedMessagingLayoutResource()
- : mBuilder.getExpandedMessagingLayoutResource(),
+ getMessagingLayoutResource(isConversationLayout, isCollapsed),
p,
bindResult);
- if (isConversationLayout) {
+ if (isConversationLayout && !notificationsRedesignTemplates()) {
+ // Redesign note: This view is replaced by the `title`, which is handled normally.
mBuilder.setTextViewColorPrimary(contentView, R.id.conversation_text, p);
+ // Redesign note: This special divider is no longer needed.
mBuilder.setTextViewColorSecondary(contentView, R.id.app_name_divider, p);
}
@@ -9538,7 +9551,18 @@
contentView.setBoolean(R.id.status_bar_latest_event_content,
"setIsImportantConversation", isImportantConversation);
}
- if (isHeaderless) {
+ if (notificationsRedesignTemplates() && !isCollapsed) {
+ // Align the title to the app/small icon in the expanded form. In other layouts,
+ // this margin is added directly to the notification_main_column parent, but for
+ // messages we don't want the margin to be applied to the actual messaging
+ // content since it can contain icons that are displayed below the app icon.
+ Resources res = mBuilder.mContext.getResources();
+ int marginStart = res.getDimensionPixelSize(
+ R.dimen.notification_2025_content_margin_start);
+ contentView.setViewLayoutMargin(R.id.title,
+ RemoteViews.MARGIN_START, marginStart, TypedValue.COMPLEX_UNIT_PX);
+ }
+ if (isLegacyHeaderless) {
// Collapsed legacy messaging style has a 1-line limit.
contentView.setInt(R.id.notification_messaging, "setMaxDisplayedLines", 1);
}
@@ -9549,6 +9573,33 @@
return contentView;
}
+ private int getMessagingLayoutResource(boolean isConversationLayout, boolean isCollapsed) {
+ if (notificationsRedesignTemplates()) {
+ // Note: We eventually would like to use the same layouts for both conversations and
+ // regular messaging notifications.
+ if (isConversationLayout) {
+ if (isCollapsed) {
+ return mBuilder.getCollapsedConversationLayoutResource();
+ } else {
+ return mBuilder.getExpandedConversationLayoutResource();
+ }
+ } else {
+ if (isCollapsed) {
+ return mBuilder.getCollapsedMessagingLayoutResource();
+ } else {
+ return mBuilder.getExpandedMessagingLayoutResource();
+ }
+ }
+
+ } else {
+ return isConversationLayout
+ ? mBuilder.getConversationLayoutResource()
+ : isCollapsed
+ ? mBuilder.getCollapsedMessagingLayoutResource()
+ : mBuilder.getExpandedMessagingLayoutResource();
+ }
+ }
+
private CharSequence getKey(Person person) {
return person == null ? null
: person.getKey() == null ? person.getName() : person.getKey();
@@ -10986,6 +11037,7 @@
private RemoteViews makeCallLayout(int viewType) {
final boolean isCollapsed = viewType == StandardTemplateParams.VIEW_TYPE_NORMAL;
+ final boolean isHeadsUp = viewType == StandardTemplateParams.VIEW_TYPE_HEADS_UP;
Bundle extras = mBuilder.mN.extras;
CharSequence title = mPerson != null ? mPerson.getName() : null;
CharSequence text = mBuilder.processLegacyText(extras.getCharSequence(EXTRA_TEXT));
@@ -11001,22 +11053,31 @@
.hideLeftIcon(true)
.hideRightIcon(true)
.hideAppName(isCollapsed)
- .titleViewId(R.id.conversation_text)
.title(title)
- .text(text)
- .summaryText(mBuilder.processLegacyText(mVerificationText));
+ .text(text);
+ if (!notificationsRedesignTemplates()) {
+ // We're using the normal title in the redesign, not a special text.
+ p.titleViewId(R.id.conversation_text)
+ // The verification text is now part of the top line views, so this is no
+ // longer necessary.
+ .summaryText(mBuilder.processLegacyText(mVerificationText));
+ }
mBuilder.mActions = getActionsListWithSystemActions();
final RemoteViews contentView;
if (isCollapsed) {
contentView = mBuilder.applyStandardTemplate(
mBuilder.getCollapsedCallLayoutResource(), p, null /* result */);
+ } else if (notificationsRedesignTemplates() && isHeadsUp) {
+ contentView = mBuilder.applyStandardTemplateWithActions(
+ mBuilder.getCollapsedCallLayoutResource(), p, null /* result */);
} else {
contentView = mBuilder.applyStandardTemplateWithActions(
mBuilder.getExpandedCallLayoutResource(), p, null /* result */);
}
// Bind some extra conversation-specific header fields.
- if (!p.mHideAppName) {
+ if (!notificationsRedesignTemplates() && !p.mHideAppName) {
+ // Redesign note: This special divider is no longer needed.
mBuilder.setTextViewColorSecondary(contentView, R.id.app_name_divider, p);
contentView.setViewVisibility(R.id.app_name_divider, View.VISIBLE);
}
@@ -14676,7 +14737,6 @@
Icon mPromotedPicture;
boolean mCallStyleActions;
boolean mAllowTextWithProgress;
- boolean mSkipTopLineAlignment;
int mTitleViewId;
int mTextViewId;
@Nullable CharSequence mTitle;
@@ -14702,7 +14762,6 @@
mPromotedPicture = null;
mCallStyleActions = false;
mAllowTextWithProgress = false;
- mSkipTopLineAlignment = false;
mTitleViewId = R.id.title;
mTextViewId = R.id.text;
mTitle = null;
@@ -14769,11 +14828,6 @@
return this;
}
- public StandardTemplateParams skipTopLineAlignment(boolean skipTopLineAlignment) {
- mSkipTopLineAlignment = skipTopLineAlignment;
- return this;
- }
-
final StandardTemplateParams hideSnoozeButton(boolean hideSnoozeButton) {
this.mHideSnoozeButton = hideSnoozeButton;
return this;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 08719fc..5359ba4 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}),
@@ -17644,9 +17647,17 @@
android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS
})
public boolean isFinancedDevice() {
- return isDeviceManaged()
- && getDeviceOwnerType(getDeviceOwnerComponentOnAnyUser())
- == DEVICE_OWNER_TYPE_FINANCED;
+ try {
+ return isDeviceManaged()
+ && getDeviceOwnerType(getDeviceOwnerComponentOnAnyUser())
+ == DEVICE_OWNER_TYPE_FINANCED;
+ } catch (IllegalStateException e) {
+ // getDeviceOwnerType() will throw IllegalStateException if the device does not have a
+ // DO. This can happen under a race condition when the DO is removed after
+ // isDeviceManaged() (so it still returns true) but before getDeviceOwnerType().
+ // In this case, the device should not be considered a financed device.
+ return false;
+ }
}
// TODO(b/315298076): revert ag/25574027 and update the doc
diff --git a/core/java/android/app/supervision/flags.aconfig b/core/java/android/app/supervision/flags.aconfig
index 94de038..7094183 100644
--- a/core/java/android/app/supervision/flags.aconfig
+++ b/core/java/android/app/supervision/flags.aconfig
@@ -72,3 +72,11 @@
description: "Flag that enables system APIs in Supervision Manager"
bug: "382034839"
}
+
+flag {
+ name: "enable_web_content_filters_screen"
+ is_exported: true
+ namespace: "supervision"
+ description: "Flag that enables the web content filters screen with Supervision settings entry point"
+ bug: "395134536"
+}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index efcaa0e..a753cbf 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -23,6 +23,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SpecialUsers.CanBeALL;
+import android.annotation.SpecialUsers.CanBeCURRENT;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -2709,7 +2711,7 @@
public final void registerContentObserverAsUser(@NonNull Uri uri,
boolean notifyForDescendants,
@NonNull ContentObserver observer,
- @NonNull UserHandle userHandle) {
+ @NonNull @CanBeALL @CanBeCURRENT UserHandle userHandle) {
Objects.requireNonNull(uri, "uri");
Objects.requireNonNull(observer, "observer");
Objects.requireNonNull(userHandle, "userHandle");
@@ -2723,7 +2725,7 @@
/** @hide - designated user version */
@UnsupportedAppUsage
public final void registerContentObserver(Uri uri, boolean notifyForDescendants,
- ContentObserver observer, @UserIdInt int userHandle) {
+ ContentObserver observer, @CanBeALL @CanBeCURRENT @UserIdInt int userHandle) {
try {
getContentService().registerContentObserver(uri, notifyForDescendants,
observer.getContentObserver(), userHandle, mTargetSdkVersion);
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 0559631..2658efa 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -482,7 +482,6 @@
*
* @return Typeface The Typeface data associated with the resource.
*/
- @RavenwoodThrow(blockedBy = Typeface.class)
@NonNull public Typeface getFont(@FontRes int id) throws NotFoundException {
final TypedValue value = obtainTempTypedValue();
try {
@@ -507,7 +506,6 @@
/**
* @hide
*/
- @RavenwoodThrow(blockedBy = Typeface.class)
public void preloadFonts(@ArrayRes int id) {
final TypedArray array = obtainTypedArray(id);
try {
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index eec30f3..8c76fd7 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -1068,7 +1068,6 @@
* Loads a font from XML or resources stream.
*/
@Nullable
- @RavenwoodThrow(blockedBy = Typeface.class)
public Typeface loadFont(Resources wrapper, TypedValue value, int id) {
if (value.string == null) {
throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index 79185a1..ee7d008 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -1043,7 +1043,6 @@
* not a font resource.
*/
@Nullable
- @RavenwoodThrow(blockedBy = Typeface.class)
public Typeface getFont(@StyleableRes int index) {
if (mRecycled) {
throw new RuntimeException("Cannot make calls to a recycled instance!");
diff --git a/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java b/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
index bfbcfd8..1bf01f4 100644
--- a/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
+++ b/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
@@ -68,6 +68,7 @@
* @hide
*/
@SystemApi
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class FontFamilyUpdateRequest {
/**
diff --git a/core/java/android/graphics/fonts/FontFileUpdateRequest.java b/core/java/android/graphics/fonts/FontFileUpdateRequest.java
index cf1dca9..1f2be6f 100644
--- a/core/java/android/graphics/fonts/FontFileUpdateRequest.java
+++ b/core/java/android/graphics/fonts/FontFileUpdateRequest.java
@@ -28,6 +28,7 @@
* @hide
*/
@SystemApi
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class FontFileUpdateRequest {
private final ParcelFileDescriptor mParcelFileDescriptor;
diff --git a/core/java/android/hardware/contexthub/HubEndpoint.java b/core/java/android/hardware/contexthub/HubEndpoint.java
index b7edef6..47ba51d 100644
--- a/core/java/android/hardware/contexthub/HubEndpoint.java
+++ b/core/java/android/hardware/contexthub/HubEndpoint.java
@@ -126,7 +126,16 @@
if (sessionExists) {
Log.w(
TAG,
- "onSessionOpenComplete: session already exists, id=" + sessionId);
+ "onSessionOpenRequest: session already exists, id=" + sessionId);
+ }
+
+ if (mLifecycleCallback == null) {
+ Log.w(
+ TAG,
+ "onSessionOpenRequest: "
+ + "failed to open session, no lifecycle callback attached",
+ new Exception());
+ rejectSession(sessionId);
}
if (!sessionExists && mLifecycleCallback != null) {
diff --git a/core/java/android/hardware/contexthub/HubEndpointSession.java b/core/java/android/hardware/contexthub/HubEndpointSession.java
index ca59be8..c5e2d7a 100644
--- a/core/java/android/hardware/contexthub/HubEndpointSession.java
+++ b/core/java/android/hardware/contexthub/HubEndpointSession.java
@@ -88,7 +88,6 @@
: ContextHubTransaction.TYPE_HUB_MESSAGE_DEFAULT);
if (!isResponseRequired) {
// If the message doesn't require acknowledgement, respond with success immediately
- // TODO(b/379162322): Improve handling of synchronous failures.
mHubEndpoint.sendMessage(this, message, null);
ret.setResponse(
new ContextHubTransaction.Response<>(
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 7850e37..92a56fc 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -851,6 +851,12 @@
* Registers a display listener to receive notifications about when
* displays are added, removed or changed.
*
+ * Because of the high frequency at which the refresh rate can change, clients will be
+ * registered for refresh rate change callbacks only when they request for refresh rate
+ * data({@link Display#getRefreshRate()}. Or alternately, you can consider using
+ * {@link #registerDisplayListener(Executor, long, DisplayListener)} and explicitly subscribe to
+ * {@link #EVENT_TYPE_DISPLAY_REFRESH_RATE} event
+ *
* We encourage to use {@link #registerDisplayListener(Executor, long, DisplayListener)}
* instead to subscribe for explicit events of interest
*
@@ -863,8 +869,8 @@
public void registerDisplayListener(DisplayListener listener, Handler handler) {
registerDisplayListener(listener, handler, EVENT_TYPE_DISPLAY_ADDED
| EVENT_TYPE_DISPLAY_CHANGED
- | EVENT_TYPE_DISPLAY_REFRESH_RATE
- | EVENT_TYPE_DISPLAY_REMOVED);
+ | EVENT_TYPE_DISPLAY_REMOVED, 0,
+ ActivityThread.currentPackageName(), /* isEventFilterExplicit */ false);
}
/**
@@ -882,9 +888,8 @@
*/
public void registerDisplayListener(@NonNull DisplayListener listener,
@Nullable Handler handler, @EventType long eventFilter) {
- mGlobal.registerDisplayListener(listener, handler,
- mGlobal.mapFiltersToInternalEventFlag(eventFilter, 0),
- ActivityThread.currentPackageName());
+ registerDisplayListener(listener, handler, eventFilter, 0,
+ ActivityThread.currentPackageName(), /* isEventFilterExplicit */ true);
}
/**
@@ -901,9 +906,8 @@
@FlaggedApi(FLAG_DISPLAY_LISTENER_PERFORMANCE_IMPROVEMENTS)
public void registerDisplayListener(@NonNull Executor executor, @EventType long eventFilter,
@NonNull DisplayListener listener) {
- mGlobal.registerDisplayListener(listener, executor,
- mGlobal.mapFiltersToInternalEventFlag(eventFilter, 0),
- ActivityThread.currentPackageName());
+ registerDisplayListener(listener, executor, eventFilter, 0,
+ ActivityThread.currentPackageName(), /* isEventFilterExplicit */ true);
}
/**
@@ -924,9 +928,39 @@
public void registerDisplayListener(@NonNull DisplayListener listener,
@Nullable Handler handler, @EventType long eventFilter,
@PrivateEventType long privateEventFilter) {
+ registerDisplayListener(listener, handler, eventFilter, privateEventFilter,
+ ActivityThread.currentPackageName(), /* isEventFilterExplicit */ true);
+ }
+
+ /**
+ * Registers a display listener to receive notifications about given display event types.
+ *
+ * @param listener The listener to register.
+ * @param handler The handler on which the listener should be invoked, or null
+ * if the listener should be invoked on the calling thread's looper.
+ * @param eventFilter A bitmask of the event types for which this listener is subscribed.
+ * @param privateEventFilter A bitmask of the private event types for which this listener
+ * is subscribed.
+ * @param isEventFilterExplicit Indicates if the client explicitly supplied the display events
+ * to be subscribed to.
+ *
+ */
+ private void registerDisplayListener(@NonNull DisplayListener listener,
+ @Nullable Handler handler, @EventType long eventFilter,
+ @PrivateEventType long privateEventFilter, String packageName,
+ boolean isEventFilterExplicit) {
mGlobal.registerDisplayListener(listener, handler,
mGlobal.mapFiltersToInternalEventFlag(eventFilter, privateEventFilter),
- ActivityThread.currentPackageName());
+ packageName, /* isEventFilterExplicit */ isEventFilterExplicit);
+ }
+
+ private void registerDisplayListener(@NonNull DisplayListener listener,
+ Executor executor, @EventType long eventFilter,
+ @PrivateEventType long privateEventFilter, String packageName,
+ boolean isEventFilterExplicit) {
+ mGlobal.registerDisplayListener(listener, executor,
+ mGlobal.mapFiltersToInternalEventFlag(eventFilter, privateEventFilter),
+ packageName, /* isEventFilterExplicit */ isEventFilterExplicit);
}
/**
@@ -1146,6 +1180,28 @@
}
/**
+ * Resets the behavior that automatically registers clients for refresh rate change callbacks
+ * when they register via {@link #registerDisplayListener(DisplayListener, Handler)}
+ *
+ * <p>By default, clients are not registered for refresh rate change callbacks via
+ * {@link #registerDisplayListener(DisplayListener, Handler)}. However, calling
+ * {@link Display#getRefreshRate()} triggers automatic registration for existing and future
+ * {@link DisplayListener} instances. This method reverts this behavior, preventing new
+ * clients from being automatically registered for refresh rate change callbacks. Note that the
+ * existing ones will continue to stay registered
+ *
+ * <p>In essence, this method returns the system to its initial state, where explicit calls to
+ * {{@link Display#getRefreshRate()} are required to receive refresh rate change notifications.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_DELAY_IMPLICIT_RR_REGISTRATION_UNTIL_RR_ACCESSED)
+ @TestApi
+ public void resetImplicitRefreshRateCallbackStatus() {
+ mGlobal.resetImplicitRefreshRateCallbackStatus();
+ }
+
+ /**
* Overrides HDR modes for a display device.
*
* @hide
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index a7d610e..c4af871 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -187,6 +187,9 @@
private final Binder mToken = new Binder();
+ // Guarded by mLock
+ private boolean mShouldImplicitlyRegisterRrChanges = false;
+
@VisibleForTesting
public DisplayManagerGlobal(IDisplayManager dm) {
mDm = dm;
@@ -390,16 +393,36 @@
* the handler for the main thread.
* If that is still null, a runtime exception will be thrown.
* @param packageName of the calling package.
+ * @param isEventFilterExplicit Indicates if the client explicitly supplied the display events
+ * to be subscribed to.
+ */
+ public void registerDisplayListener(@NonNull DisplayListener listener,
+ @Nullable Handler handler, @InternalEventFlag long internalEventFlagsMask,
+ String packageName, boolean isEventFilterExplicit) {
+ Looper looper = getLooperForHandler(handler);
+ Handler springBoard = new Handler(looper);
+ registerDisplayListener(listener, new HandlerExecutor(springBoard), internalEventFlagsMask,
+ packageName, isEventFilterExplicit);
+ }
+
+ /**
+ * Register a listener for display-related changes.
+ *
+ * @param listener The listener that will be called when display changes occur.
+ * @param handler Handler for the thread that will be receiving the callbacks. May be null.
+ * If null, listener will use the handler for the current thread, and if still null,
+ * the handler for the main thread.
+ * If that is still null, a runtime exception will be thrown.
+ * @param internalEventFlagsMask Mask of events to be listened to.
+ * @param packageName of the calling package.
*/
public void registerDisplayListener(@NonNull DisplayListener listener,
@Nullable Handler handler, @InternalEventFlag long internalEventFlagsMask,
String packageName) {
- Looper looper = getLooperForHandler(handler);
- Handler springBoard = new Handler(looper);
- registerDisplayListener(listener, new HandlerExecutor(springBoard), internalEventFlagsMask,
- packageName);
+ registerDisplayListener(listener, handler, internalEventFlagsMask, packageName, true);
}
+
/**
* Register a listener for display-related changes.
*
@@ -407,10 +430,12 @@
* @param executor Executor for the thread that will be receiving the callbacks. Cannot be null.
* @param internalEventFlagsMask Mask of events to be listened to.
* @param packageName of the calling package.
+ * @param isEventFilterExplicit Indicates if the explicit events to be subscribed to
+ * were supplied or not
*/
public void registerDisplayListener(@NonNull DisplayListener listener,
@NonNull Executor executor, @InternalEventFlag long internalEventFlagsMask,
- String packageName) {
+ String packageName, boolean isEventFilterExplicit) {
if (listener == null) {
throw new IllegalArgumentException("listener must not be null");
}
@@ -429,7 +454,7 @@
int index = findDisplayListenerLocked(listener);
if (index < 0) {
mDisplayListeners.add(new DisplayListenerDelegate(listener, executor,
- internalEventFlagsMask, packageName));
+ internalEventFlagsMask, packageName, isEventFilterExplicit));
registerCallbackIfNeededLocked();
} else {
mDisplayListeners.get(index).setEventsMask(internalEventFlagsMask);
@@ -439,6 +464,22 @@
}
}
+
+ /**
+ * Registers all the clients to INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE events if qualified
+ */
+ public void registerForRefreshRateChanges() {
+ if (!Flags.delayImplicitRrRegistrationUntilRrAccessed()) {
+ return;
+ }
+ synchronized (mLock) {
+ if (!mShouldImplicitlyRegisterRrChanges) {
+ mShouldImplicitlyRegisterRrChanges = true;
+ updateCallbackIfNeededLocked();
+ }
+ }
+ }
+
public void unregisterDisplayListener(DisplayListener listener) {
if (listener == null) {
throw new IllegalArgumentException("listener must not be null");
@@ -521,8 +562,14 @@
long mask = 0;
final int numListeners = mDisplayListeners.size();
for (int i = 0; i < numListeners; i++) {
- mask |= mDisplayListeners.get(i).mInternalEventFlagsMask;
+ DisplayListenerDelegate displayListenerDelegate = mDisplayListeners.get(i);
+ if (!Flags.delayImplicitRrRegistrationUntilRrAccessed()
+ || mShouldImplicitlyRegisterRrChanges) {
+ displayListenerDelegate.implicitlyRegisterForRRChanges();
+ }
+ mask |= displayListenerDelegate.mInternalEventFlagsMask;
}
+
if (mDispatchNativeCallbacks) {
mask |= INTERNAL_EVENT_FLAG_DISPLAY_ADDED
| INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
@@ -802,6 +849,18 @@
}
/**
+ * Resets the implicit registration of refresh rate change callbacks
+ *
+ */
+ public void resetImplicitRefreshRateCallbackStatus() {
+ if (Flags.delayImplicitRrRegistrationUntilRrAccessed()) {
+ synchronized (mLock) {
+ mShouldImplicitlyRegisterRrChanges = false;
+ }
+ }
+ }
+
+ /**
* Overrides HDR modes for a display device.
*
*/
@@ -1439,21 +1498,27 @@
}
}
- private static final class DisplayListenerDelegate {
+ @VisibleForTesting
+ static final class DisplayListenerDelegate {
public final DisplayListener mListener;
public volatile long mInternalEventFlagsMask;
+ // Indicates if the client explicitly supplied the display events to be subscribed to.
+ private final boolean mIsEventFilterExplicit;
+
private final DisplayInfo mDisplayInfo = new DisplayInfo();
private final Executor mExecutor;
private AtomicLong mGenerationId = new AtomicLong(1);
private final String mPackageName;
DisplayListenerDelegate(DisplayListener listener, @NonNull Executor executor,
- @InternalEventFlag long internalEventFlag, String packageName) {
+ @InternalEventFlag long internalEventFlag, String packageName,
+ boolean isEventFilterExplicit) {
mExecutor = executor;
mListener = listener;
mInternalEventFlagsMask = internalEventFlag;
mPackageName = packageName;
+ mIsEventFilterExplicit = isEventFilterExplicit;
}
void sendDisplayEvent(int displayId, @DisplayEvent int event, @Nullable DisplayInfo info,
@@ -1470,6 +1535,11 @@
});
}
+ @VisibleForTesting
+ boolean isEventFilterExplicit() {
+ return mIsEventFilterExplicit;
+ }
+
void clearEvents() {
mGenerationId.incrementAndGet();
}
@@ -1478,6 +1548,17 @@
mInternalEventFlagsMask = newInternalEventFlagsMask;
}
+ private void implicitlyRegisterForRRChanges() {
+ // For backward compatibility, if the user didn't supply the explicit events while
+ // subscribing, register them to refresh rate change events if they subscribed to
+ // display changed events
+ if ((mInternalEventFlagsMask & INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED) != 0
+ && !mIsEventFilterExplicit) {
+ setEventsMask(mInternalEventFlagsMask
+ | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE);
+ }
+ }
+
private void handleDisplayEventInner(int displayId, @DisplayEvent int event,
@Nullable DisplayInfo info, boolean forceUpdate) {
if (extraLogging()) {
@@ -1677,6 +1758,9 @@
public void registerNativeChoreographerForRefreshRateCallbacks() {
synchronized (mLock) {
mDispatchNativeCallbacks = true;
+ if (Flags.delayImplicitRrRegistrationUntilRrAccessed()) {
+ mShouldImplicitlyRegisterRrChanges = true;
+ }
registerCallbackIfNeededLocked();
updateCallbackIfNeededLocked();
DisplayInfo display = getDisplayInfoLocked(Display.DEFAULT_DISPLAY);
@@ -1806,4 +1890,9 @@
return baseEventMask;
}
+
+ @VisibleForTesting
+ CopyOnWriteArrayList<DisplayListenerDelegate> getDisplayListeners() {
+ return mDisplayListeners;
+ }
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index a528ba4..7b47efd4 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -990,6 +990,8 @@
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (android.view.inputmethod.Flags.refactorInsetsController()) {
+ // After the IME window was hidden, we can remove its surface
+ scheduleImeSurfaceRemoval();
// The hide request first finishes the animation and then proceeds to the server
// side, finally reaching here, marking this the end state.
ImeTracker.forLogging().onHidden(statsToken);
diff --git a/core/java/android/os/BundleMerger.java b/core/java/android/os/BundleMerger.java
index dc243a5..fd5a18c 100644
--- a/core/java/android/os/BundleMerger.java
+++ b/core/java/android/os/BundleMerger.java
@@ -119,11 +119,23 @@
public static final int STRATEGY_ARRAY_APPEND = 50;
/**
+ * Merge strategy that combines two conflicting array values by creating a new array
+ * containing all unique elements from both arrays.
+ */
+ public static final int STRATEGY_ARRAY_UNION = 55;
+
+ /**
* Merge strategy that combines two conflicting {@link ArrayList} values by
* appending the last {@link ArrayList} after the first {@link ArrayList}.
*/
public static final int STRATEGY_ARRAY_LIST_APPEND = 60;
+ /**
+ * Merge strategy that combines two conflicting {@link String} values by
+ * appending the last {@link String} after the first {@link String}.
+ */
+ public static final int STRATEGY_STRING_APPEND = 70;
+
@IntDef(flag = false, prefix = { "STRATEGY_" }, value = {
STRATEGY_REJECT,
STRATEGY_FIRST,
@@ -136,7 +148,9 @@
STRATEGY_BOOLEAN_AND,
STRATEGY_BOOLEAN_OR,
STRATEGY_ARRAY_APPEND,
+ STRATEGY_ARRAY_UNION,
STRATEGY_ARRAY_LIST_APPEND,
+ STRATEGY_STRING_APPEND,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Strategy {}
@@ -298,8 +312,12 @@
return booleanOr(first, last);
case STRATEGY_ARRAY_APPEND:
return arrayAppend(first, last);
+ case STRATEGY_ARRAY_UNION:
+ return arrayUnion(first, last);
case STRATEGY_ARRAY_LIST_APPEND:
return arrayListAppend(first, last);
+ case STRATEGY_STRING_APPEND:
+ return stringAppend(first, last);
default:
throw new UnsupportedOperationException();
}
@@ -361,6 +379,29 @@
return res;
}
+ private static @NonNull Object arrayUnion(@NonNull Object first, @NonNull Object last) {
+ if (!first.getClass().isArray()) {
+ throw new IllegalArgumentException("Unable to union " + first.getClass());
+ }
+ final int firstLength = Array.getLength(first);
+ final int lastLength = Array.getLength(last);
+ final ArrayList<Object> list = new ArrayList<>(firstLength + lastLength);
+ final ArraySet<Object> set = new ArraySet<>();
+ for (int i = 0; i < firstLength; i++) {
+ set.add(Array.get(first, i));
+ }
+ for (int i = 0; i < lastLength; i++) {
+ set.add(Array.get(last, i));
+ }
+ final Class<?> clazz = first.getClass().getComponentType();
+ final int setSize = set.size();
+ final Object res = Array.newInstance(clazz, setSize);
+ for (int i = 0; i < setSize; i++) {
+ Array.set(res, i, set.valueAt(i));
+ }
+ return res;
+ }
+
@SuppressWarnings("unchecked")
private static @NonNull Object arrayListAppend(@NonNull Object first, @NonNull Object last) {
if (!(first instanceof ArrayList)) {
@@ -374,6 +415,13 @@
return res;
}
+ private static @NonNull Object stringAppend(@NonNull Object first, @NonNull Object last) {
+ if (!(first instanceof String)) {
+ throw new IllegalArgumentException("Unable to append " + first.getClass());
+ }
+ return ((String) first) + ((String) last);
+ }
+
public static final @android.annotation.NonNull Parcelable.Creator<BundleMerger> CREATOR =
new Parcelable.Creator<BundleMerger>() {
@Override
diff --git a/core/java/android/os/PerfettoTrace.java b/core/java/android/os/PerfettoTrace.java
index 741d542..932836f 100644
--- a/core/java/android/os/PerfettoTrace.java
+++ b/core/java/android/os/PerfettoTrace.java
@@ -232,10 +232,6 @@
* @param eventName The event name to appear in the trace.
*/
public static PerfettoTrackEventExtra.Builder instant(Category category, String eventName) {
- if (!category.isEnabled()) {
- return PerfettoTrackEventExtra.noOpBuilder();
- }
-
return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_INSTANT, category)
.setEventName(eventName);
}
@@ -247,10 +243,6 @@
* @param eventName The event name to appear in the trace.
*/
public static PerfettoTrackEventExtra.Builder begin(Category category, String eventName) {
- if (!category.isEnabled()) {
- return PerfettoTrackEventExtra.noOpBuilder();
- }
-
return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_SLICE_BEGIN, category)
.setEventName(eventName);
}
@@ -261,10 +253,6 @@
* @param category The perfetto category.
*/
public static PerfettoTrackEventExtra.Builder end(Category category) {
- if (!category.isEnabled()) {
- return PerfettoTrackEventExtra.noOpBuilder();
- }
-
return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_SLICE_END, category);
}
@@ -275,10 +263,6 @@
* @param value The value of the counter.
*/
public static PerfettoTrackEventExtra.Builder counter(Category category, long value) {
- if (!category.isEnabled()) {
- return PerfettoTrackEventExtra.noOpBuilder();
- }
-
return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_COUNTER, category)
.setCounter(value);
}
@@ -302,10 +286,6 @@
* @param value The value of the counter.
*/
public static PerfettoTrackEventExtra.Builder counter(Category category, double value) {
- if (!category.isEnabled()) {
- return PerfettoTrackEventExtra.noOpBuilder();
- }
-
return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_COUNTER, category)
.setCounter(value);
}
diff --git a/core/java/android/os/PerfettoTrackEventExtra.java b/core/java/android/os/PerfettoTrackEventExtra.java
index 6844229..f4b5dfe 100644
--- a/core/java/android/os/PerfettoTrackEventExtra.java
+++ b/core/java/android/os/PerfettoTrackEventExtra.java
@@ -35,8 +35,8 @@
*/
@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class PerfettoTrackEventExtra {
+ private static final boolean DEBUG = false;
private static final int DEFAULT_EXTRA_CACHE_SIZE = 5;
- private static final Builder NO_OP_BUILDER = new NoOpBuilder();
private static final ThreadLocal<PerfettoTrackEventExtra> sTrackEventExtra =
new ThreadLocal<PerfettoTrackEventExtra>() {
@Override
@@ -46,7 +46,7 @@
};
private static final AtomicLong sNamedTrackId = new AtomicLong();
private static final Supplier<Flow> sFlowSupplier = Flow::new;
- private static final Supplier<BuilderImpl> sBuilderSupplier = BuilderImpl::new;
+ private static final Supplier<Builder> sBuilderSupplier = Builder::new;
private static final Supplier<FieldInt64> sFieldInt64Supplier = FieldInt64::new;
private static final Supplier<FieldDouble> sFieldDoubleSupplier = FieldDouble::new;
private static final Supplier<FieldString> sFieldStringSupplier = FieldString::new;
@@ -56,6 +56,8 @@
private CounterInt64 mCounterInt64;
private CounterDouble mCounterDouble;
private Proto mProto;
+ private Flow mFlow;
+ private Flow mTerminatingFlow;
/**
* Represents a native pointer to a Perfetto C SDK struct. E.g. PerfettoTeHlExtra.
@@ -135,245 +137,10 @@
}
}
- public interface Builder {
- /**
- * Emits the track event.
- */
- void emit();
-
- /**
- * Initialize the builder for a new trace event.
- */
- Builder init(int traceType, PerfettoTrace.Category category);
-
- /**
- * Sets the event name for the track event.
- */
- Builder setEventName(String eventName);
-
- /**
- * Adds a debug arg with key {@code name} and value {@code val}.
- */
- Builder addArg(String name, long val);
-
- /**
- * Adds a debug arg with key {@code name} and value {@code val}.
- */
- Builder addArg(String name, boolean val);
-
- /**
- * Adds a debug arg with key {@code name} and value {@code val}.
- */
- Builder addArg(String name, double val);
-
- /**
- * Adds a debug arg with key {@code name} and value {@code val}.
- */
- Builder addArg(String name, String val);
-
- /**
- * Adds a flow with {@code id}.
- */
- Builder addFlow(int id);
-
- /**
- * Adds a terminating flow with {@code id}.
- */
- Builder addTerminatingFlow(int id);
-
- /**
- * Adds the events to a named track instead of the thread track where the
- * event occurred.
- */
- Builder usingNamedTrack(long parentUuid, String name);
-
- /**
- * Adds the events to a process scoped named track instead of the thread track where the
- * event occurred.
- */
- Builder usingProcessNamedTrack(String name);
-
- /**
- * Adds the events to a thread scoped named track instead of the thread track where the
- * event occurred.
- */
- Builder usingThreadNamedTrack(long tid, String name);
-
- /**
- * Adds the events to a counter track instead. This is required for
- * setting counter values.
- */
- Builder usingCounterTrack(long parentUuid, String name);
-
- /**
- * Adds the events to a process scoped counter track instead. This is required for
- * setting counter values.
- */
- Builder usingProcessCounterTrack(String name);
-
- /**
- * Adds the events to a thread scoped counter track instead. This is required for
- * setting counter values.
- */
- Builder usingThreadCounterTrack(long tid, String name);
-
- /**
- * Sets a long counter value on the event.
- *
- */
- Builder setCounter(long val);
-
- /**
- * Sets a double counter value on the event.
- *
- */
- Builder setCounter(double val);
-
- /**
- * Adds a proto field with field id {@code id} and value {@code val}.
- */
- Builder addField(long id, long val);
-
- /**
- * Adds a proto field with field id {@code id} and value {@code val}.
- */
- Builder addField(long id, double val);
-
- /**
- * Adds a proto field with field id {@code id} and value {@code val}.
- */
- Builder addField(long id, String val);
-
- /**
- * Begins a proto field with field
- * Fields can be added from this point and there must be a corresponding
- * {@link endProto}.
- *
- * The proto field is a singleton and all proto fields get added inside the
- * one {@link beginProto} and {@link endProto} within the {@link Builder}.
- */
- Builder beginProto();
-
- /**
- * Ends a proto field.
- */
- Builder endProto();
-
- /**
- * Begins a nested proto field with field id {@code id}.
- * Fields can be added from this point and there must be a corresponding
- * {@link endNested}.
- */
- Builder beginNested(long id);
-
- /**
- * Ends a nested proto field.
- */
- Builder endNested();
- }
-
- @android.ravenwood.annotation.RavenwoodKeepWholeClass
- public static final class NoOpBuilder implements Builder {
- @Override
- public void emit() {}
- @Override
- public Builder init(int traceType, PerfettoTrace.Category category) {
- return this;
- }
- @Override
- public Builder setEventName(String eventName) {
- return this;
- }
- @Override
- public Builder addArg(String name, long val) {
- return this;
- }
- @Override
- public Builder addArg(String name, boolean val) {
- return this;
- }
- @Override
- public Builder addArg(String name, double val) {
- return this;
- }
- @Override
- public Builder addArg(String name, String val) {
- return this;
- }
- @Override
- public Builder addFlow(int id) {
- return this;
- }
- @Override
- public Builder addTerminatingFlow(int id) {
- return this;
- }
- @Override
- public Builder usingNamedTrack(long parentUuid, String name) {
- return this;
- }
- @Override
- public Builder usingProcessNamedTrack(String name) {
- return this;
- }
- @Override
- public Builder usingThreadNamedTrack(long tid, String name) {
- return this;
- }
- @Override
- public Builder usingCounterTrack(long parentUuid, String name) {
- return this;
- }
- @Override
- public Builder usingProcessCounterTrack(String name) {
- return this;
- }
- @Override
- public Builder usingThreadCounterTrack(long tid, String name) {
- return this;
- }
- @Override
- public Builder setCounter(long val) {
- return this;
- }
- @Override
- public Builder setCounter(double val) {
- return this;
- }
- @Override
- public Builder addField(long id, long val) {
- return this;
- }
- @Override
- public Builder addField(long id, double val) {
- return this;
- }
- @Override
- public Builder addField(long id, String val) {
- return this;
- }
- @Override
- public Builder beginProto() {
- return this;
- }
- @Override
- public Builder endProto() {
- return this;
- }
- @Override
- public Builder beginNested(long id) {
- return this;
- }
- @Override
- public Builder endNested() {
- return this;
- }
- }
-
/**
* Builder for Perfetto track event extras.
*/
- public static final class BuilderImpl implements Builder {
+ public static final class Builder {
// For performance reasons, we hold a reference to mExtra as a holder for
// perfetto pointers being added. This way, we avoid an additional list to hold
// the pointers in Java and we can pass them down directly to native code.
@@ -386,10 +153,13 @@
private Builder mParent;
private FieldContainer mCurrentContainer;
+ private boolean mIsCategoryEnabled;
private final CounterInt64 mCounterInt64;
private final CounterDouble mCounterDouble;
private final Proto mProto;
+ private final Flow mFlow;
+ private final Flow mTerminatingFlow;
private final RingBuffer<NamedTrack> mNamedTrackCache;
private final RingBuffer<CounterTrack> mCounterTrackCache;
@@ -403,9 +173,9 @@
private final Pool<FieldString> mFieldStringCache;
private final Pool<FieldNested> mFieldNestedCache;
private final Pool<Flow> mFlowCache;
- private final Pool<BuilderImpl> mBuilderCache;
+ private final Pool<Builder> mBuilderCache;
- private BuilderImpl() {
+ private Builder() {
mExtra = sTrackEventExtra.get();
mNamedTrackCache = mExtra.mNamedTrackCache;
mCounterTrackCache = mExtra.mCounterTrackCache;
@@ -423,20 +193,32 @@
mCounterInt64 = mExtra.getCounterInt64();
mCounterDouble = mExtra.getCounterDouble();
mProto = mExtra.getProto();
+ mFlow = mExtra.getFlow();
+ mTerminatingFlow = mExtra.getTerminatingFlow();
}
- @Override
+ /**
+ * Emits the track event.
+ */
public void emit() {
- checkParent();
- mIsBuilt = true;
+ if (!mIsCategoryEnabled) {
+ return;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
+ mIsBuilt = true;
native_emit(mTraceType, mCategory.getPtr(), mEventName, mExtra.getPtr());
- // Reset after emitting to free any the extras used to trace the event.
- mExtra.reset();
}
- @Override
+ /**
+ * Initialize the builder for a new trace event.
+ */
public Builder init(int traceType, PerfettoTrace.Category category) {
+ if (!category.isEnabled()) {
+ return this;
+ }
mTraceType = traceType;
mCategory = category;
mEventName = "";
@@ -449,18 +231,27 @@
mExtra.reset();
// Reset after on init in case the thread created builders without calling emit
- return initInternal(this, null);
+ return initInternal(this, null, true);
}
- @Override
+ /**
+ * Sets the event name for the track event.
+ */
public Builder setEventName(String eventName) {
mEventName = eventName;
return this;
}
- @Override
+ /**
+ * Adds a debug arg with key {@code name} and value {@code val}.
+ */
public Builder addArg(String name, long val) {
- checkParent();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
ArgInt64 arg = mArgInt64Cache.get(name.hashCode());
if (arg == null || !arg.getName().equals(name)) {
arg = new ArgInt64(name);
@@ -471,9 +262,16 @@
return this;
}
- @Override
+ /**
+ * Adds a debug arg with key {@code name} and value {@code val}.
+ */
public Builder addArg(String name, boolean val) {
- checkParent();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
ArgBool arg = mArgBoolCache.get(name.hashCode());
if (arg == null || !arg.getName().equals(name)) {
arg = new ArgBool(name);
@@ -484,9 +282,16 @@
return this;
}
- @Override
+ /**
+ * Adds a debug arg with key {@code name} and value {@code val}.
+ */
public Builder addArg(String name, double val) {
- checkParent();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
ArgDouble arg = mArgDoubleCache.get(name.hashCode());
if (arg == null || !arg.getName().equals(name)) {
arg = new ArgDouble(name);
@@ -497,9 +302,16 @@
return this;
}
- @Override
+ /**
+ * Adds a debug arg with key {@code name} and value {@code val}.
+ */
public Builder addArg(String name, String val) {
- checkParent();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
ArgString arg = mArgStringCache.get(name.hashCode());
if (arg == null || !arg.getName().equals(name)) {
arg = new ArgString(name);
@@ -510,27 +322,79 @@
return this;
}
- @Override
+ /**
+ * Adds a flow with {@code id}.
+ */
public Builder addFlow(int id) {
- checkParent();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
Flow flow = mFlowCache.get(sFlowSupplier);
flow.setProcessFlow(id);
mExtra.addPerfettoPointer(flow);
return this;
}
- @Override
+ /**
+ * Adds a terminating flow with {@code id}.
+ */
public Builder addTerminatingFlow(int id) {
- checkParent();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
Flow flow = mFlowCache.get(sFlowSupplier);
flow.setProcessTerminatingFlow(id);
mExtra.addPerfettoPointer(flow);
return this;
}
- @Override
+ /**
+ * Adds a flow with {@code id}.
+ */
+ public Builder setFlow(int id) {
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
+ mFlow.setProcessFlow(id);
+ mExtra.addPerfettoPointer(mFlow);
+ return this;
+ }
+
+ /**
+ * Adds a terminating flow with {@code id}.
+ */
+ public Builder setTerminatingFlow(int id) {
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
+ mTerminatingFlow.setProcessTerminatingFlow(id);
+ mExtra.addPerfettoPointer(mTerminatingFlow);
+ return this;
+ }
+
+ /**
+ * Adds the events to a named track instead of the thread track where the
+ * event occurred.
+ */
public Builder usingNamedTrack(long parentUuid, String name) {
- checkParent();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
NamedTrack track = mNamedTrackCache.get(name.hashCode());
if (track == null || !track.getName().equals(name)) {
@@ -541,19 +405,39 @@
return this;
}
- @Override
+ /**
+ * Adds the events to a process scoped named track instead of the thread track where the
+ * event occurred.
+ */
public Builder usingProcessNamedTrack(String name) {
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
return usingNamedTrack(PerfettoTrace.getProcessTrackUuid(), name);
}
- @Override
+ /**
+ * Adds the events to a thread scoped named track instead of the thread track where the
+ * event occurred.
+ */
public Builder usingThreadNamedTrack(long tid, String name) {
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
return usingNamedTrack(PerfettoTrace.getThreadTrackUuid(tid), name);
}
- @Override
+ /**
+ * Adds the events to a counter track instead. This is required for
+ * setting counter values.
+ */
public Builder usingCounterTrack(long parentUuid, String name) {
- checkParent();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
CounterTrack track = mCounterTrackCache.get(name.hashCode());
if (track == null || !track.getName().equals(name)) {
@@ -564,95 +448,178 @@
return this;
}
- @Override
+ /**
+ * Adds the events to a process scoped counter track instead. This is required for
+ * setting counter values.
+ */
public Builder usingProcessCounterTrack(String name) {
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
return usingCounterTrack(PerfettoTrace.getProcessTrackUuid(), name);
}
- @Override
+ /**
+ * Adds the events to a thread scoped counter track instead. This is required for
+ * setting counter values.
+ */
public Builder usingThreadCounterTrack(long tid, String name) {
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
return usingCounterTrack(PerfettoTrace.getThreadTrackUuid(tid), name);
}
- @Override
+ /**
+ * Sets a long counter value on the event.
+ *
+ */
public Builder setCounter(long val) {
- checkParent();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
mCounterInt64.setValue(val);
mExtra.addPerfettoPointer(mCounterInt64);
return this;
}
- @Override
+ /**
+ * Sets a double counter value on the event.
+ *
+ */
public Builder setCounter(double val) {
- checkParent();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
mCounterDouble.setValue(val);
mExtra.addPerfettoPointer(mCounterDouble);
return this;
}
- @Override
+ /**
+ * Adds a proto field with field id {@code id} and value {@code val}.
+ */
public Builder addField(long id, long val) {
- checkContainer();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkContainer();
+ }
FieldInt64 field = mFieldInt64Cache.get(sFieldInt64Supplier);
field.setValue(id, val);
mExtra.addPerfettoPointer(mCurrentContainer, field);
return this;
}
- @Override
+ /**
+ * Adds a proto field with field id {@code id} and value {@code val}.
+ */
public Builder addField(long id, double val) {
- checkContainer();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkContainer();
+ }
FieldDouble field = mFieldDoubleCache.get(sFieldDoubleSupplier);
field.setValue(id, val);
mExtra.addPerfettoPointer(mCurrentContainer, field);
return this;
}
- @Override
+ /**
+ * Adds a proto field with field id {@code id} and value {@code val}.
+ */
public Builder addField(long id, String val) {
- checkContainer();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkContainer();
+ }
FieldString field = mFieldStringCache.get(sFieldStringSupplier);
field.setValue(id, val);
mExtra.addPerfettoPointer(mCurrentContainer, field);
return this;
}
- @Override
+ /**
+ * Begins a proto field.
+ * Fields can be added from this point and there must be a corresponding
+ * {@link endProto}.
+ *
+ * The proto field is a singleton and all proto fields get added inside the
+ * one {@link beginProto} and {@link endProto} within the {@link Builder}.
+ */
public Builder beginProto() {
- checkParent();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
mProto.clearFields();
mExtra.addPerfettoPointer(mProto);
- return mBuilderCache.get(sBuilderSupplier).initInternal(this, mProto);
+ return mBuilderCache.get(sBuilderSupplier).initInternal(this, mProto, true);
}
- @Override
+ /**
+ * Ends a proto field.
+ */
public Builder endProto() {
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
if (mParent == null || mCurrentContainer == null) {
throw new IllegalStateException("No proto to end");
}
return mParent;
}
- @Override
+ /**
+ * Begins a nested proto field with field id {@code id}.
+ * Fields can be added from this point and there must be a corresponding
+ * {@link endNested}.
+ */
public Builder beginNested(long id) {
- checkContainer();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkContainer();
+ }
FieldNested field = mFieldNestedCache.get(sFieldNestedSupplier);
field.setId(id);
mExtra.addPerfettoPointer(mCurrentContainer, field);
- return mBuilderCache.get(sBuilderSupplier).initInternal(this, field);
+ return mBuilderCache.get(sBuilderSupplier).initInternal(this, field, true);
}
- @Override
+ /**
+ * Ends a nested proto field.
+ */
public Builder endNested() {
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
if (mParent == null || mCurrentContainer == null) {
throw new IllegalStateException("No nested field to end");
}
return mParent;
}
- private Builder initInternal(Builder parent, FieldContainer container) {
+
+ private Builder initInternal(Builder parent, FieldContainer field,
+ boolean isCategoryEnabled) {
mParent = parent;
- mCurrentContainer = container;
+ mCurrentContainer = field;
+ mIsCategoryEnabled = isCategoryEnabled;
mIsBuilt = false;
return this;
@@ -685,14 +652,8 @@
* Start a {@link Builder} to build a {@link PerfettoTrackEventExtra}.
*/
public static Builder builder() {
- return sTrackEventExtra.get().mBuilderCache.get(sBuilderSupplier).initInternal(null, null);
- }
-
- /**
- * Returns a no-op {@link Builder}. Useful if a category is disabled.
- */
- public static Builder noOpBuilder() {
- return NO_OP_BUILDER;
+ return sTrackEventExtra.get().mBuilderCache.get(sBuilderSupplier).initInternal(null, null,
+ false);
}
private final RingBuffer<NamedTrack> mNamedTrackCache =
@@ -710,7 +671,7 @@
private final Pool<FieldString> mFieldStringCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
private final Pool<FieldNested> mFieldNestedCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
private final Pool<Flow> mFlowCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
- private final Pool<BuilderImpl> mBuilderCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
+ private final Pool<Builder> mBuilderCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
private static final NativeAllocationRegistry sRegistry =
NativeAllocationRegistry.createMalloced(
@@ -757,6 +718,7 @@
mPendingPointers.clear();
}
+ @android.ravenwood.annotation.RavenwoodReplace
private CounterInt64 getCounterInt64() {
if (mCounterInt64 == null) {
mCounterInt64 = new CounterInt64();
@@ -764,6 +726,7 @@
return mCounterInt64;
}
+ @android.ravenwood.annotation.RavenwoodReplace
private CounterDouble getCounterDouble() {
if (mCounterDouble == null) {
mCounterDouble = new CounterDouble();
@@ -771,6 +734,7 @@
return mCounterDouble;
}
+ @android.ravenwood.annotation.RavenwoodReplace
private Proto getProto() {
if (mProto == null) {
mProto = new Proto();
@@ -778,6 +742,22 @@
return mProto;
}
+ @android.ravenwood.annotation.RavenwoodReplace
+ private Flow getFlow() {
+ if (mFlow == null) {
+ mFlow = new Flow();
+ }
+ return mFlow;
+ }
+
+ @android.ravenwood.annotation.RavenwoodReplace
+ private Flow getTerminatingFlow() {
+ if (mTerminatingFlow == null) {
+ mTerminatingFlow = new Flow();
+ }
+ return mTerminatingFlow;
+ }
+
private static final class Flow implements PerfettoPointer {
private static final NativeAllocationRegistry sRegistry =
NativeAllocationRegistry.createMalloced(
@@ -1337,4 +1317,29 @@
// Tracing currently completely disabled under Ravenwood
return 0;
}
+
+ private CounterInt64 getCounterInt64$ravenwood() {
+ // Tracing currently completely disabled under Ravenwood
+ return null;
+ }
+
+ private CounterDouble getCounterDouble$ravenwood() {
+ // Tracing currently completely disabled under Ravenwood
+ return null;
+ }
+
+ private Proto getProto$ravenwood() {
+ // Tracing currently completely disabled under Ravenwood
+ return null;
+ }
+
+ private Flow getFlow$ravenwood() {
+ // Tracing currently completely disabled under Ravenwood
+ return null;
+ }
+
+ private Flow getTerminatingFlow$ravenwood() {
+ // Tracing currently completely disabled under Ravenwood
+ return null;
+ }
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 33bf4a2..767019d 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -3324,8 +3324,7 @@
@FlaggedApi(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE)
@RequiresPermission(anyOf = {
Manifest.permission.MANAGE_USERS,
- Manifest.permission.CREATE_USERS},
- conditional = true)
+ Manifest.permission.CREATE_USERS})
@UserHandleAware
public boolean canAddPrivateProfile() {
if (!android.multiuser.Flags.enablePrivateSpaceFeatures()) return false;
diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig
index 7ceb948..0615578 100644
--- a/core/java/android/os/vibrator/flags.aconfig
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -145,3 +145,13 @@
purpose: PURPOSE_FEATURE
}
}
+
+flag {
+ namespace: "haptics"
+ name: "fix_vibration_thread_callback_handling"
+ description: "Fix how the VibrationThread handles late callbacks from the vibrator HAL"
+ bug: "395005081"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index d469a2f..ca24c0c 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -514,3 +514,12 @@
description: "Force AttributionSource.myAttributionSource() to return a default device id"
bug: "343121936"
}
+
+flag {
+ name: "grant_read_blocked_numbers_to_system_ui_intelligence"
+ is_exported: true
+ is_fixed_read_only: true
+ namespace: "permissions"
+ description: "This flag is used to add role protection to READ_BLOCKED_NUMBERS for SYSTEM_UI_INTELLIGENCE"
+ bug: "354758615"
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4ebfe53..538283e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -13017,18 +13017,16 @@
* false/0.
* @hide
*/
- @Readable
public static final String REDACT_OTP_NOTIFICATION_WHILE_CONNECTED_TO_WIFI =
"redact_otp_on_wifi";
/**
- * Toggle for whether to immediately redact OTP notifications, or require the device to be
- * locked for 10 minutes. Defaults to false/0
+ * Time (in milliseconds) that the device should need to be locked, in order for an OTP
+ * notification to be redacted. Default is 10 minutes (600,000 ms)
* @hide
*/
- @Readable
- public static final String REDACT_OTP_NOTIFICATION_IMMEDIATELY =
- "remove_otp_redaction_delay";
+ public static final String OTP_NOTIFICATION_REDACTION_LOCK_TIME =
+ "otp_redaction_lock_time";
/**
* These entries are considered common between the personal and the managed profile,
diff --git a/core/java/android/text/AlteredCharSequence.java b/core/java/android/text/AlteredCharSequence.java
index 971a47d..a05c690a 100644
--- a/core/java/android/text/AlteredCharSequence.java
+++ b/core/java/android/text/AlteredCharSequence.java
@@ -24,6 +24,7 @@
* @deprecated The functionality this class offers is easily implemented outside the framework.
*/
@Deprecated
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AlteredCharSequence
implements CharSequence, GetChars
{
diff --git a/core/java/android/text/AndroidBidi.java b/core/java/android/text/AndroidBidi.java
index 31da799..fcdd50a 100644
--- a/core/java/android/text/AndroidBidi.java
+++ b/core/java/android/text/AndroidBidi.java
@@ -28,6 +28,7 @@
* @hide
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AndroidBidi {
/**
diff --git a/core/java/android/text/AndroidCharacter.java b/core/java/android/text/AndroidCharacter.java
index c5f1a01..37c4fdb 100644
--- a/core/java/android/text/AndroidCharacter.java
+++ b/core/java/android/text/AndroidCharacter.java
@@ -22,6 +22,7 @@
* @deprecated Use various methods from {@link android.icu.lang.UCharacter}, instead.
*/
@Deprecated
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AndroidCharacter
{
public static final int EAST_ASIAN_WIDTH_NEUTRAL = 0;
diff --git a/core/java/android/text/Annotation.java b/core/java/android/text/Annotation.java
index bb5d3ea..ac3e5a9 100644
--- a/core/java/android/text/Annotation.java
+++ b/core/java/android/text/Annotation.java
@@ -23,6 +23,7 @@
* TextView save/restore cycles and can be used to keep application-specific
* data that needs to be maintained for regions of text.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Annotation implements ParcelableSpan {
private final String mKey;
private final String mValue;
diff --git a/core/java/android/text/AutoGrowArray.java b/core/java/android/text/AutoGrowArray.java
index e428377..06c74c3 100644
--- a/core/java/android/text/AutoGrowArray.java
+++ b/core/java/android/text/AutoGrowArray.java
@@ -30,6 +30,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class AutoGrowArray {
private static final int MIN_CAPACITY_INCREMENT = 12;
private static final int MAX_CAPACITY_TO_BE_KEPT = 10000;
diff --git a/core/java/android/text/AutoText.java b/core/java/android/text/AutoText.java
index c5339a4..d7b0547 100644
--- a/core/java/android/text/AutoText.java
+++ b/core/java/android/text/AutoText.java
@@ -31,6 +31,7 @@
/**
* This class accesses a dictionary of corrections to frequent misspellings.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AutoText {
// struct trie {
// char c;
diff --git a/core/java/android/text/BidiFormatter.java b/core/java/android/text/BidiFormatter.java
index dfa172d..6d4103c 100644
--- a/core/java/android/text/BidiFormatter.java
+++ b/core/java/android/text/BidiFormatter.java
@@ -82,6 +82,7 @@
* first-strong estimation algorithm. It can also be configured to use a custom directionality
* estimation object.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class BidiFormatter {
/**
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index 4fdcecc..2b410e6 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -45,6 +45,7 @@
* {@link android.graphics.Canvas#drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint)
* Canvas.drawText()} directly.</p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback {
/**
diff --git a/core/java/android/text/CharSequenceCharacterIterator.java b/core/java/android/text/CharSequenceCharacterIterator.java
index 9b07d29..1599be8 100644
--- a/core/java/android/text/CharSequenceCharacterIterator.java
+++ b/core/java/android/text/CharSequenceCharacterIterator.java
@@ -24,6 +24,7 @@
* An implementation of {@link java.text.CharacterIterator} that iterates over a given CharSequence.
* {@hide}
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class CharSequenceCharacterIterator implements CharacterIterator {
private final int mBeginIndex, mEndIndex;
private int mIndex;
diff --git a/core/java/android/text/ClipboardManager.java b/core/java/android/text/ClipboardManager.java
index d030910..41990f0 100644
--- a/core/java/android/text/ClipboardManager.java
+++ b/core/java/android/text/ClipboardManager.java
@@ -21,6 +21,7 @@
* {@link android.content.ClipboardManager} for the modern API.
*/
@Deprecated
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class ClipboardManager {
/**
* Returns the text on the clipboard. It will eventually be possible
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 6b1aef7..3b66ce0 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -53,6 +53,7 @@
* {@link android.graphics.Canvas#drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint)
* Canvas.drawText()} directly.</p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class DynamicLayout extends Layout {
private static final int PRIORITY = 128;
private static final int BLOCK_MINIMUM_CHARACTER_LENGTH = 400;
diff --git a/core/java/android/text/Editable.java b/core/java/android/text/Editable.java
index a942f6c..53d819f 100644
--- a/core/java/android/text/Editable.java
+++ b/core/java/android/text/Editable.java
@@ -22,6 +22,7 @@
* to immutable text like Strings). If you make a {@link DynamicLayout}
* of an Editable, the layout will be reflowed as the text is changed.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface Editable
extends CharSequence, GetChars, Spannable, Appendable
{
diff --git a/core/java/android/text/Emoji.java b/core/java/android/text/Emoji.java
index cf0e3c2..28c37c0 100644
--- a/core/java/android/text/Emoji.java
+++ b/core/java/android/text/Emoji.java
@@ -23,6 +23,7 @@
* An utility class for Emoji.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Emoji {
public static int COMBINING_ENCLOSING_KEYCAP = 0x20E3;
diff --git a/core/java/android/text/EmojiConsistency.java b/core/java/android/text/EmojiConsistency.java
index dfaa217..9823305 100644
--- a/core/java/android/text/EmojiConsistency.java
+++ b/core/java/android/text/EmojiConsistency.java
@@ -48,6 +48,7 @@
* </ol>
* </p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class EmojiConsistency {
/* Cannot construct */
private EmojiConsistency() { }
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index 783f3b7..5a4d3a8 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -55,6 +55,7 @@
*/
@SystemApi
@TestApi
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class FontConfig implements Parcelable {
private final @NonNull List<FontFamily> mFamilies;
private final @NonNull List<Alias> mAliases;
diff --git a/core/java/android/text/GetChars.java b/core/java/android/text/GetChars.java
index 348a911..229f543 100644
--- a/core/java/android/text/GetChars.java
+++ b/core/java/android/text/GetChars.java
@@ -21,6 +21,7 @@
* getChars() method like the one in String that is faster than
* calling charAt() multiple times.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface GetChars
extends CharSequence
{
diff --git a/core/java/android/text/GraphemeClusterSegmentFinder.java b/core/java/android/text/GraphemeClusterSegmentFinder.java
index 0f6fdaf..996223d 100644
--- a/core/java/android/text/GraphemeClusterSegmentFinder.java
+++ b/core/java/android/text/GraphemeClusterSegmentFinder.java
@@ -31,6 +31,7 @@
* @see <a href="https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries">Unicode Text
* Segmentation - Grapheme Cluster Boundaries</a>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class GraphemeClusterSegmentFinder extends SegmentFinder {
private static AutoGrowArray.FloatArray sTempAdvances = null;
private final boolean[] mIsGraphemeBreak;
diff --git a/core/java/android/text/GraphicsOperations.java b/core/java/android/text/GraphicsOperations.java
index 6c15446..f7fe805 100644
--- a/core/java/android/text/GraphicsOperations.java
+++ b/core/java/android/text/GraphicsOperations.java
@@ -26,6 +26,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface GraphicsOperations extends CharSequence {
/**
* Just like {@link Canvas#drawText}.
diff --git a/core/java/android/text/Highlights.java b/core/java/android/text/Highlights.java
index 693dbcf..217a38b 100644
--- a/core/java/android/text/Highlights.java
+++ b/core/java/android/text/Highlights.java
@@ -30,6 +30,7 @@
/**
* A class that represents of the highlight of the text.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Highlights {
private final List<Pair<Paint, int[]>> mHighlights;
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index a42eece..d412071 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -17,13 +17,13 @@
package android.text;
import android.app.ActivityThread;
-import android.app.Application;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Build;
+import android.ravenwood.annotation.RavenwoodReplace;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.AlignmentSpan;
import android.text.style.BackgroundColorSpan;
@@ -65,6 +65,7 @@
* This class processes HTML strings into displayable styled text.
* Not all HTML tags are supported.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Html {
/**
* Retrieves images for HTML <img> tags.
@@ -506,6 +507,15 @@
out.append("</p>\n");
}
+ @RavenwoodReplace(blockedBy = ActivityThread.class)
+ private static float getDisplayMetricsDensity() {
+ return ActivityThread.currentApplication().getResources().getDisplayMetrics().density;
+ }
+
+ private static float getDisplayMetricsDensity$ravenwood() {
+ return Resources.getSystem().getDisplayMetrics().density;
+ }
+
private static void withinParagraph(StringBuilder out, Spanned text, int start, int end) {
int next;
for (int i = start; i < end; i = next) {
@@ -559,8 +569,7 @@
AbsoluteSizeSpan s = ((AbsoluteSizeSpan) style[j]);
float sizeDip = s.getSize();
if (!s.getDip()) {
- Application application = ActivityThread.currentApplication();
- sizeDip /= application.getResources().getDisplayMetrics().density;
+ sizeDip /= getDisplayMetricsDensity();
}
// px in CSS is the equivalance of dip in Android
@@ -669,6 +678,7 @@
}
}
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
class HtmlToSpannedConverter implements ContentHandler {
private static final float[] HEADING_SIZES = {
@@ -843,6 +853,16 @@
}
}
+ @RavenwoodReplace(blockedBy = ActivityThread.class)
+ private static int getFontWeightAdjustment() {
+ return ActivityThread.currentApplication().getResources()
+ .getConfiguration().fontWeightAdjustment;
+ }
+
+ private static int getFontWeightAdjustment$ravenwood() {
+ return Resources.getSystem().getConfiguration().fontWeightAdjustment;
+ }
+
private void handleEndTag(String tag) {
if (tag.equalsIgnoreCase("br")) {
handleBr(mSpannableStringBuilder);
@@ -858,17 +878,11 @@
} else if (tag.equalsIgnoreCase("span")) {
endCssStyle(mSpannableStringBuilder);
} else if (tag.equalsIgnoreCase("strong")) {
- Application application = ActivityThread.currentApplication();
- int fontWeightAdjustment =
- application.getResources().getConfiguration().fontWeightAdjustment;
end(mSpannableStringBuilder, Bold.class, new StyleSpan(Typeface.BOLD,
- fontWeightAdjustment));
+ getFontWeightAdjustment()));
} else if (tag.equalsIgnoreCase("b")) {
- Application application = ActivityThread.currentApplication();
- int fontWeightAdjustment =
- application.getResources().getConfiguration().fontWeightAdjustment;
end(mSpannableStringBuilder, Bold.class, new StyleSpan(Typeface.BOLD,
- fontWeightAdjustment));
+ getFontWeightAdjustment()));
} else if (tag.equalsIgnoreCase("em")) {
end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC));
} else if (tag.equalsIgnoreCase("cite")) {
@@ -1036,11 +1050,8 @@
// Their ranges should not include the newlines at the end
Heading h = getLast(text, Heading.class);
if (h != null) {
- Application application = ActivityThread.currentApplication();
- int fontWeightAdjustment =
- application.getResources().getConfiguration().fontWeightAdjustment;
setSpanFromMark(text, h, new RelativeSizeSpan(HEADING_SIZES[h.mLevel]),
- new StyleSpan(Typeface.BOLD, fontWeightAdjustment));
+ new StyleSpan(Typeface.BOLD, getFontWeightAdjustment()));
}
endBlockElement(text);
diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java
index 6f0628a..7f9a8a1 100644
--- a/core/java/android/text/Hyphenator.java
+++ b/core/java/android/text/Hyphenator.java
@@ -21,6 +21,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Hyphenator {
private Hyphenator() {}
diff --git a/core/java/android/text/InputFilter.java b/core/java/android/text/InputFilter.java
index 96e7bd0..ed5de03e 100644
--- a/core/java/android/text/InputFilter.java
+++ b/core/java/android/text/InputFilter.java
@@ -27,6 +27,7 @@
* InputFilters can be attached to {@link Editable}s to constrain the
* changes that can be made to them.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface InputFilter
{
/**
diff --git a/core/java/android/text/InputType.java b/core/java/android/text/InputType.java
index 4ebecb7..03c9c02 100644
--- a/core/java/android/text/InputType.java
+++ b/core/java/android/text/InputType.java
@@ -44,6 +44,7 @@
* TYPE_DATETIME_VARIATION_TIME
* </dl>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface InputType {
/**
* Mask of bits that determine the overall class
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index b273a7f..44c3f9a 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -69,6 +69,7 @@
* which will be updated as the text changes.
* For text that will not change, use a {@link StaticLayout}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class Layout {
// These should match the constants in framework/base/libs/hwui/hwui/DrawTextFunctor.h
diff --git a/core/java/android/text/LoginFilter.java b/core/java/android/text/LoginFilter.java
index 0e4eec44..94f196f 100644
--- a/core/java/android/text/LoginFilter.java
+++ b/core/java/android/text/LoginFilter.java
@@ -23,6 +23,7 @@
* handle non-BMP characters.
*/
@Deprecated
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class LoginFilter implements InputFilter {
private boolean mAppendInvalid; // whether to append or ignore invalid characters
/**
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index 31a2263..b2e4459 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -68,6 +68,7 @@
* @hide
*/
@TestApi
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class MeasuredParagraph {
private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC';
diff --git a/core/java/android/text/NoCopySpan.java b/core/java/android/text/NoCopySpan.java
index e754d76..4cd3d04 100644
--- a/core/java/android/text/NoCopySpan.java
+++ b/core/java/android/text/NoCopySpan.java
@@ -21,6 +21,7 @@
* into a new Spanned when performing a slice or copy operation on the original
* Spanned it was placed in.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface NoCopySpan {
/**
* Convenience equivalent for when you would just want a new Object() for
diff --git a/core/java/android/text/PackedIntVector.java b/core/java/android/text/PackedIntVector.java
index 3e5bf56..11dd0c3 100644
--- a/core/java/android/text/PackedIntVector.java
+++ b/core/java/android/text/PackedIntVector.java
@@ -29,6 +29,7 @@
* @hide
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PackedIntVector {
private final int mColumns;
private int mRows;
diff --git a/core/java/android/text/PackedObjectVector.java b/core/java/android/text/PackedObjectVector.java
index b777e16..beb5ea4 100644
--- a/core/java/android/text/PackedObjectVector.java
+++ b/core/java/android/text/PackedObjectVector.java
@@ -21,6 +21,7 @@
import libcore.util.EmptyArray;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
class PackedObjectVector<E>
{
private int mColumns;
diff --git a/core/java/android/text/ParcelableSpan.java b/core/java/android/text/ParcelableSpan.java
index d7c1a4b..a9a4893 100644
--- a/core/java/android/text/ParcelableSpan.java
+++ b/core/java/android/text/ParcelableSpan.java
@@ -24,6 +24,7 @@
* This can only be used by code in the framework; it is not intended for
* applications to implement their own Parcelable spans.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface ParcelableSpan extends Parcelable {
/**
* Return a special type identifier for this span class.
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
index 5f6a9bd..71cacd9 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -75,6 +75,7 @@
* Note that any {@link android.text.NoCopySpan} attached to the original text won't be passed to
* PrecomputedText.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PrecomputedText implements Spannable {
private static final char LINE_FEED = '\n';
diff --git a/core/java/android/text/SegmentFinder.java b/core/java/android/text/SegmentFinder.java
index 047d07a..b7ab0e6 100644
--- a/core/java/android/text/SegmentFinder.java
+++ b/core/java/android/text/SegmentFinder.java
@@ -39,6 +39,7 @@
*
* @see Layout#getRangeForRect(RectF, SegmentFinder, Layout.TextInclusionStrategy)
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class SegmentFinder {
/**
* Return value of previousStartBoundary(int), previousEndBoundary(int), nextStartBoundary(int),
diff --git a/core/java/android/text/Selection.java b/core/java/android/text/Selection.java
index 711578c..674b473 100644
--- a/core/java/android/text/Selection.java
+++ b/core/java/android/text/Selection.java
@@ -27,6 +27,7 @@
* Utility class for manipulating cursors and selections in CharSequences.
* A cursor is a selection where the start and end are at the same offset.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Selection {
private Selection() { /* cannot be instantiated */ }
diff --git a/core/java/android/text/SpanColors.java b/core/java/android/text/SpanColors.java
index fcd242b..3b6a041 100644
--- a/core/java/android/text/SpanColors.java
+++ b/core/java/android/text/SpanColors.java
@@ -27,6 +27,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SpanColors {
public static final @ColorInt int NO_COLOR_FOUND = Color.TRANSPARENT;
diff --git a/core/java/android/text/SpanSet.java b/core/java/android/text/SpanSet.java
index d464278..4ad8106 100644
--- a/core/java/android/text/SpanSet.java
+++ b/core/java/android/text/SpanSet.java
@@ -31,6 +31,7 @@
* Note that empty spans are ignored by this class.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SpanSet<E> {
private final Class<? extends E> classType;
diff --git a/core/java/android/text/SpanWatcher.java b/core/java/android/text/SpanWatcher.java
index 01e82c8..31d6320 100644
--- a/core/java/android/text/SpanWatcher.java
+++ b/core/java/android/text/SpanWatcher.java
@@ -21,6 +21,7 @@
* will be called to notify it that other markup objects have been
* added, changed, or removed.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface SpanWatcher extends NoCopySpan {
/**
* This method is called to notify you that the specified object
diff --git a/core/java/android/text/Spannable.java b/core/java/android/text/Spannable.java
index 8315b2a..fac5131 100644
--- a/core/java/android/text/Spannable.java
+++ b/core/java/android/text/Spannable.java
@@ -21,6 +21,7 @@
* attached and detached. Not all Spannable classes have mutable text;
* see {@link Editable} for that.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface Spannable
extends Spanned
{
diff --git a/core/java/android/text/SpannableString.java b/core/java/android/text/SpannableString.java
index afb5df8..ee04a86 100644
--- a/core/java/android/text/SpannableString.java
+++ b/core/java/android/text/SpannableString.java
@@ -21,6 +21,7 @@
* markup objects can be attached and detached.
* For mutable text, see {@link SpannableStringBuilder}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SpannableString
extends SpannableStringInternal
implements CharSequence, GetChars, Spannable
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index 0e61eff..f8d7283 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -35,6 +35,7 @@
/**
* This is the class for text whose content and markup can both be changed.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SpannableStringBuilder implements CharSequence, GetChars, Spannable, Editable,
Appendable, GraphicsOperations {
private final static String TAG = "SpannableStringBuilder";
diff --git a/core/java/android/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java
index f2ab1bb..90d83d5 100644
--- a/core/java/android/text/SpannableStringInternal.java
+++ b/core/java/android/text/SpannableStringInternal.java
@@ -27,6 +27,7 @@
import java.lang.reflect.Array;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
/* package */ abstract class SpannableStringInternal
{
/* package */ SpannableStringInternal(CharSequence source,
diff --git a/core/java/android/text/Spanned.java b/core/java/android/text/Spanned.java
index a0d54c26c..6706ffd 100644
--- a/core/java/android/text/Spanned.java
+++ b/core/java/android/text/Spanned.java
@@ -22,6 +22,7 @@
* see {@link Spannable} for mutable markup and {@link Editable} for
* mutable text.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface Spanned
extends CharSequence
{
diff --git a/core/java/android/text/SpannedString.java b/core/java/android/text/SpannedString.java
index acee3c5..a3f1ee2 100644
--- a/core/java/android/text/SpannedString.java
+++ b/core/java/android/text/SpannedString.java
@@ -22,6 +22,7 @@
* For mutable markup, see {@link SpannableString}; for mutable text,
* see {@link SpannableStringBuilder}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class SpannedString
extends SpannableStringInternal
implements CharSequence, GetChars, Spanned
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index a5d52957..8193cd2 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -55,6 +55,7 @@
* float, float, android.graphics.Paint)
* Canvas.drawText()} directly.</p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class StaticLayout extends Layout {
/*
* The break iteration is done in native code. The protocol for using the native code is as
diff --git a/core/java/android/text/TextDirectionHeuristic.java b/core/java/android/text/TextDirectionHeuristic.java
index 8a4ba42..66cea85 100644
--- a/core/java/android/text/TextDirectionHeuristic.java
+++ b/core/java/android/text/TextDirectionHeuristic.java
@@ -19,6 +19,7 @@
/**
* Interface for objects that use a heuristic for guessing at the paragraph direction by examining text.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface TextDirectionHeuristic {
/**
* Guess if a chars array is in the RTL direction or not.
diff --git a/core/java/android/text/TextDirectionHeuristics.java b/core/java/android/text/TextDirectionHeuristics.java
index 85260f4..3af8fb7 100644
--- a/core/java/android/text/TextDirectionHeuristics.java
+++ b/core/java/android/text/TextDirectionHeuristics.java
@@ -32,6 +32,7 @@
* class.
*
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TextDirectionHeuristics {
/**
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 3015791..091eb60 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -53,6 +53,7 @@
* @hide
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TextLine {
private static final boolean DEBUG = false;
diff --git a/core/java/android/text/TextPaint.java b/core/java/android/text/TextPaint.java
index 73825b1..ff063f2 100644
--- a/core/java/android/text/TextPaint.java
+++ b/core/java/android/text/TextPaint.java
@@ -25,6 +25,7 @@
* TextPaint is an extension of Paint that leaves room for some extra
* data used during text measuring and drawing.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TextPaint extends Paint {
// Special value 0 means no background paint
diff --git a/core/java/android/text/TextShaper.java b/core/java/android/text/TextShaper.java
index 6da0b63..6d17401 100644
--- a/core/java/android/text/TextShaper.java
+++ b/core/java/android/text/TextShaper.java
@@ -169,6 +169,7 @@
* @see TextShaper#shapeText(CharSequence, int, int, TextDirectionHeuristic, TextPaint,
* GlyphsConsumer)
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TextShaper {
private TextShaper() {}
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 6dc82c4..042966b 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -34,6 +34,7 @@
import android.icu.util.ULocale;
import android.os.Parcel;
import android.os.Parcelable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.sysprop.DisplayProperties;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.AccessibilityClickableSpan;
@@ -85,8 +86,7 @@
import java.util.Locale;
import java.util.regex.Pattern;
-@android.ravenwood.annotation.RavenwoodKeepStaticInitializer
-@android.ravenwood.annotation.RavenwoodKeepPartialClass
+@RavenwoodKeepWholeClass
public class TextUtils {
private static final String TAG = "TextUtils";
@@ -147,7 +147,6 @@
private TextUtils() { /* cannot be instantiated */ }
- @android.ravenwood.annotation.RavenwoodKeep
public static void getChars(CharSequence s, int start, int end,
char[] dest, int destoff) {
Class<? extends CharSequence> c = s.getClass();
@@ -166,12 +165,10 @@
}
}
- @android.ravenwood.annotation.RavenwoodKeep
public static int indexOf(CharSequence s, char ch) {
return indexOf(s, ch, 0);
}
- @android.ravenwood.annotation.RavenwoodKeep
public static int indexOf(CharSequence s, char ch, int start) {
Class<? extends CharSequence> c = s.getClass();
@@ -181,7 +178,6 @@
return indexOf(s, ch, start, s.length());
}
- @android.ravenwood.annotation.RavenwoodKeep
public static int indexOf(CharSequence s, char ch, int start, int end) {
Class<? extends CharSequence> c = s.getClass();
@@ -219,12 +215,10 @@
return -1;
}
- @android.ravenwood.annotation.RavenwoodKeep
public static int lastIndexOf(CharSequence s, char ch) {
return lastIndexOf(s, ch, s.length() - 1);
}
- @android.ravenwood.annotation.RavenwoodKeep
public static int lastIndexOf(CharSequence s, char ch, int last) {
Class<? extends CharSequence> c = s.getClass();
@@ -234,7 +228,6 @@
return lastIndexOf(s, ch, 0, last);
}
- @android.ravenwood.annotation.RavenwoodKeep
public static int lastIndexOf(CharSequence s, char ch,
int start, int last) {
if (last < 0)
@@ -280,17 +273,14 @@
return -1;
}
- @android.ravenwood.annotation.RavenwoodKeep
public static int indexOf(CharSequence s, CharSequence needle) {
return indexOf(s, needle, 0, s.length());
}
- @android.ravenwood.annotation.RavenwoodKeep
public static int indexOf(CharSequence s, CharSequence needle, int start) {
return indexOf(s, needle, start, s.length());
}
- @android.ravenwood.annotation.RavenwoodKeep
public static int indexOf(CharSequence s, CharSequence needle,
int start, int end) {
int nlen = needle.length();
@@ -318,7 +308,6 @@
return -1;
}
- @android.ravenwood.annotation.RavenwoodKeep
public static boolean regionMatches(CharSequence one, int toffset,
CharSequence two, int ooffset,
int len) {
@@ -351,7 +340,6 @@
* in that it does not preserve any style runs in the source sequence,
* allowing a more efficient implementation.
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static String substring(CharSequence source, int start, int end) {
if (source instanceof String)
return ((String) source).substring(start, end);
@@ -424,7 +412,6 @@
* calling object.toString(). If tokens is null, a NullPointerException will be thrown. If
* tokens is an empty array, an empty string will be returned.
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static String join(@NonNull CharSequence delimiter, @NonNull Object[] tokens) {
final int length = tokens.length;
if (length == 0) {
@@ -448,7 +435,6 @@
* calling object.toString(). If tokens is null, a NullPointerException will be thrown. If
* tokens is empty, an empty string will be returned.
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static String join(@NonNull CharSequence delimiter, @NonNull Iterable tokens) {
final Iterator<?> it = tokens.iterator();
if (!it.hasNext()) {
@@ -481,7 +467,6 @@
*
* @throws NullPointerException if expression or text is null
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static String[] split(String text, String expression) {
if (text.length() == 0) {
return EmptyArray.STRING;
@@ -507,7 +492,6 @@
*
* @throws NullPointerException if expression or text is null
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static String[] split(String text, Pattern pattern) {
if (text.length() == 0) {
return EmptyArray.STRING;
@@ -545,7 +529,6 @@
* be returned for the empty string after that delimeter. That is, splitting <tt>"a,b,"</tt> on
* comma will return <tt>"a", "b"</tt>, not <tt>"a", "b", ""</tt>.
*/
- @android.ravenwood.annotation.RavenwoodKeepWholeClass
public static class SimpleStringSplitter implements StringSplitter, Iterator<String> {
private String mString;
private char mDelimiter;
@@ -609,31 +592,26 @@
* @param str the string to be examined
* @return true if str is null or zero length
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static boolean isEmpty(@Nullable CharSequence str) {
return str == null || str.length() == 0;
}
/** {@hide} */
- @android.ravenwood.annotation.RavenwoodKeep
public static String nullIfEmpty(@Nullable String str) {
return isEmpty(str) ? null : str;
}
/** {@hide} */
- @android.ravenwood.annotation.RavenwoodKeep
public static String emptyIfNull(@Nullable String str) {
return str == null ? "" : str;
}
/** {@hide} */
- @android.ravenwood.annotation.RavenwoodKeep
public static String firstNotEmpty(@Nullable String a, @NonNull String b) {
return !isEmpty(a) ? a : Preconditions.checkStringNotEmpty(b);
}
/** {@hide} */
- @android.ravenwood.annotation.RavenwoodKeep
public static int length(@Nullable String s) {
return s != null ? s.length() : 0;
}
@@ -642,7 +620,6 @@
* @return interned string if it's null.
* @hide
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static String safeIntern(String s) {
return (s != null) ? s.intern() : null;
}
@@ -652,7 +629,6 @@
* spaces and ASCII control characters were trimmed from the start and end,
* as by {@link String#trim}.
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static int getTrimmedLength(CharSequence s) {
int len = s.length();
@@ -677,7 +653,6 @@
* @param b second CharSequence to check
* @return true if a and b are equal
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static boolean equals(@Nullable CharSequence a, @Nullable CharSequence b) {
if (a == b) return true;
int length;
@@ -1713,7 +1688,6 @@
return true;
}
- @android.ravenwood.annotation.RavenwoodKeep
/* package */ static char[] obtain(int len) {
char[] buf;
@@ -1728,7 +1702,6 @@
return buf;
}
- @android.ravenwood.annotation.RavenwoodKeep
/* package */ static void recycle(char[] temp) {
if (temp.length > 1000)
return;
@@ -1743,7 +1716,6 @@
* @param s the string to be encoded
* @return the encoded string
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static String htmlEncode(String s) {
StringBuilder sb = new StringBuilder();
char c;
@@ -1830,7 +1802,6 @@
/**
* Returns whether the given CharSequence contains any printable characters.
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static boolean isGraphic(CharSequence str) {
final int len = str.length();
for (int cp, i=0; i<len; i+=Character.charCount(cp)) {
@@ -1857,7 +1828,6 @@
* @deprecated Use {@link #isGraphic(CharSequence)} instead.
*/
@Deprecated
- @android.ravenwood.annotation.RavenwoodKeep
public static boolean isGraphic(char c) {
int gc = Character.getType(c);
return gc != Character.CONTROL
@@ -1872,7 +1842,6 @@
/**
* Returns whether the given CharSequence contains only digits.
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static boolean isDigitsOnly(CharSequence str) {
final int len = str.length();
for (int cp, i = 0; i < len; i += Character.charCount(cp)) {
@@ -1887,7 +1856,6 @@
/**
* @hide
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static boolean isPrintableAscii(final char c) {
final int asciiFirst = 0x20;
final int asciiLast = 0x7E; // included
@@ -1898,7 +1866,6 @@
* @hide
*/
@UnsupportedAppUsage
- @android.ravenwood.annotation.RavenwoodKeep
public static boolean isPrintableAsciiOnly(final CharSequence str) {
final int len = str.length();
for (int i = 0; i < len; i++) {
@@ -1950,7 +1917,6 @@
* {@link #CAP_MODE_CHARACTERS}, {@link #CAP_MODE_WORDS}, and
* {@link #CAP_MODE_SENTENCES}.
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static int getCapsMode(CharSequence cs, int off, int reqModes) {
if (off < 0) {
return 0;
@@ -2162,7 +2128,6 @@
*
* Be careful: this code will need to be updated when vertical scripts will be supported
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static int getLayoutDirectionFromLocale(Locale locale) {
return ((locale != null && !locale.equals(Locale.ROOT)
&& ULocale.forLocale(locale).isRightToLeft())
@@ -2197,7 +2162,6 @@
* match the supported grammar described above.
* @hide
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static @NonNull String formatSimple(@NonNull String format, Object... args) {
final StringBuilder sb = new StringBuilder(format);
int j = 0;
@@ -2387,7 +2351,6 @@
}
/** @hide */
- @android.ravenwood.annotation.RavenwoodKeep
public static boolean isNewline(int codePoint) {
int type = Character.getType(codePoint);
return type == Character.PARAGRAPH_SEPARATOR || type == Character.LINE_SEPARATOR
@@ -2395,19 +2358,16 @@
}
/** @hide */
- @android.ravenwood.annotation.RavenwoodKeep
public static boolean isWhitespace(int codePoint) {
return Character.isWhitespace(codePoint) || codePoint == NBSP_CODE_POINT;
}
/** @hide */
- @android.ravenwood.annotation.RavenwoodKeep
public static boolean isWhitespaceExceptNewline(int codePoint) {
return isWhitespace(codePoint) && !isNewline(codePoint);
}
/** @hide */
- @android.ravenwood.annotation.RavenwoodKeep
public static boolean isPunctuation(int codePoint) {
int type = Character.getType(codePoint);
return type == Character.CONNECTOR_PUNCTUATION
diff --git a/core/java/android/text/TextWatcher.java b/core/java/android/text/TextWatcher.java
index a0aef69..5963ca7 100644
--- a/core/java/android/text/TextWatcher.java
+++ b/core/java/android/text/TextWatcher.java
@@ -20,6 +20,7 @@
* When an object of this type is attached to an Editable, its methods will
* be called when the text is changed.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface TextWatcher extends NoCopySpan {
/**
* This method is called to notify you that, within <code>s</code>,
diff --git a/core/java/android/text/WordSegmentFinder.java b/core/java/android/text/WordSegmentFinder.java
index b0a70ea..b8702d7 100644
--- a/core/java/android/text/WordSegmentFinder.java
+++ b/core/java/android/text/WordSegmentFinder.java
@@ -33,6 +33,7 @@
* @see <a href="https://unicode.org/reports/tr29/#Word_Boundaries">Unicode Text Segmentation - Word
* Boundaries</a>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class WordSegmentFinder extends SegmentFinder {
private final CharSequence mText;
private final WordIterator mWordIterator;
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index e6dad27..ef60d30 100644
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -63,6 +63,7 @@
* Note that the non-{@code format} methods in this class are implemented by
* {@code SimpleDateFormat}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class DateFormat {
/**
* @deprecated Use a literal {@code '} instead.
diff --git a/core/java/android/text/format/DateIntervalFormat.java b/core/java/android/text/format/DateIntervalFormat.java
index 8dea322..5ec9561 100644
--- a/core/java/android/text/format/DateIntervalFormat.java
+++ b/core/java/android/text/format/DateIntervalFormat.java
@@ -37,6 +37,7 @@
* @hide
*/
@VisibleForTesting(visibility = PACKAGE)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class DateIntervalFormat {
private static final LruCache<String, android.icu.text.DateIntervalFormat> CACHED_FORMATTERS =
diff --git a/core/java/android/text/format/DateTimeFormat.java b/core/java/android/text/format/DateTimeFormat.java
index 064d717..c8dd61d 100644
--- a/core/java/android/text/format/DateTimeFormat.java
+++ b/core/java/android/text/format/DateTimeFormat.java
@@ -29,6 +29,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
class DateTimeFormat {
private static final FormatterCache CACHED_FORMATTERS = new FormatterCache();
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index 518a549..12ad764 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -44,6 +44,7 @@
* This class contains various date-related utilities for creating text for things like
* elapsed time and date ranges, strings for days of the week and months, and AM/PM text etc.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class DateUtils
{
private static final Object sLock = new Object();
diff --git a/core/java/android/text/format/DateUtilsBridge.java b/core/java/android/text/format/DateUtilsBridge.java
index 92ec9cf..752a8c0 100644
--- a/core/java/android/text/format/DateUtilsBridge.java
+++ b/core/java/android/text/format/DateUtilsBridge.java
@@ -46,6 +46,7 @@
* @hide
*/
@VisibleForTesting(visibility = PACKAGE)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class DateUtilsBridge {
/**
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index 7653bdb..e7783dc 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -41,6 +41,7 @@
* Utility class to aid in formatting common values that are not covered
* by the {@link java.util.Formatter} class in {@link java.util}
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class Formatter {
/** {@hide} */
diff --git a/core/java/android/text/format/RelativeDateTimeFormatter.java b/core/java/android/text/format/RelativeDateTimeFormatter.java
index 9096469..6b940f8 100644
--- a/core/java/android/text/format/RelativeDateTimeFormatter.java
+++ b/core/java/android/text/format/RelativeDateTimeFormatter.java
@@ -42,6 +42,7 @@
* @hide
*/
@VisibleForTesting(visibility = PACKAGE)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class RelativeDateTimeFormatter {
public static final long SECOND_IN_MILLIS = 1000;
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index bac7c6c..1beb573 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -53,6 +53,7 @@
* @deprecated Use {@link java.util.GregorianCalendar} instead.
*/
@Deprecated
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Time {
private static final String Y_M_D_T_H_M_S_000 = "%Y-%m-%dT%H:%M:%S.000";
private static final String Y_M_D_T_H_M_S_000_Z = "%Y-%m-%dT%H:%M:%S.000Z";
diff --git a/core/java/android/text/format/TimeFormatter.java b/core/java/android/text/format/TimeFormatter.java
index e42ad63..dd703d8 100644
--- a/core/java/android/text/format/TimeFormatter.java
+++ b/core/java/android/text/format/TimeFormatter.java
@@ -40,6 +40,7 @@
*
* <p>This class is not thread safe.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
class TimeFormatter {
// An arbitrary value outside the range representable by a char.
private static final int FORCE_LOWER_CASE = -1;
diff --git a/core/java/android/text/format/TimeMigrationUtils.java b/core/java/android/text/format/TimeMigrationUtils.java
index 17bac8d..b2f5024 100644
--- a/core/java/android/text/format/TimeMigrationUtils.java
+++ b/core/java/android/text/format/TimeMigrationUtils.java
@@ -22,6 +22,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TimeMigrationUtils {
private TimeMigrationUtils() {}
diff --git a/core/java/android/text/method/AllCapsTransformationMethod.java b/core/java/android/text/method/AllCapsTransformationMethod.java
index 305b056..70dcc52 100644
--- a/core/java/android/text/method/AllCapsTransformationMethod.java
+++ b/core/java/android/text/method/AllCapsTransformationMethod.java
@@ -33,6 +33,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AllCapsTransformationMethod implements TransformationMethod2 {
private static final String TAG = "AllCapsTransformationMethod";
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 37474e5..6099220 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -30,6 +30,7 @@
* A movement method that provides cursor movement and selection.
* Supports displaying the context menu on DPad Center.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ArrowKeyMovementMethod extends BaseMovementMethod implements MovementMethod {
private static boolean isSelecting(Spannable buffer) {
return ((MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SHIFT_ON) == 1) ||
diff --git a/core/java/android/text/method/BaseKeyListener.java b/core/java/android/text/method/BaseKeyListener.java
index e427908..5ebfd99 100644
--- a/core/java/android/text/method/BaseKeyListener.java
+++ b/core/java/android/text/method/BaseKeyListener.java
@@ -47,6 +47,7 @@
* with hardware keyboards. Software input methods have no obligation to trigger
* the methods in this class.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class BaseKeyListener extends MetaKeyKeyListener
implements KeyListener {
/* package */ static final Object OLD_SEL_START = new NoCopySpan.Concrete();
diff --git a/core/java/android/text/method/BaseMovementMethod.java b/core/java/android/text/method/BaseMovementMethod.java
index 7a4b3a0..0c2e52e 100644
--- a/core/java/android/text/method/BaseMovementMethod.java
+++ b/core/java/android/text/method/BaseMovementMethod.java
@@ -27,6 +27,7 @@
/**
* Base classes for movement methods.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class BaseMovementMethod implements MovementMethod {
@Override
public boolean canSelectArbitrarily() {
diff --git a/core/java/android/text/method/CharacterPickerDialog.java b/core/java/android/text/method/CharacterPickerDialog.java
index 7d838e0..f084d03 100644
--- a/core/java/android/text/method/CharacterPickerDialog.java
+++ b/core/java/android/text/method/CharacterPickerDialog.java
@@ -38,6 +38,7 @@
/**
* Dialog for choosing accented characters related to a base character.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class CharacterPickerDialog extends Dialog
implements OnItemClickListener, OnClickListener {
private View mView;
diff --git a/core/java/android/text/method/DateKeyListener.java b/core/java/android/text/method/DateKeyListener.java
index 0accbf6..acf1822 100644
--- a/core/java/android/text/method/DateKeyListener.java
+++ b/core/java/android/text/method/DateKeyListener.java
@@ -35,6 +35,7 @@
* with hardware keyboards. Software input methods have no obligation to trigger
* the methods in this class.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class DateKeyListener extends NumberKeyListener
{
public int getInputType() {
diff --git a/core/java/android/text/method/DateTimeKeyListener.java b/core/java/android/text/method/DateTimeKeyListener.java
index 1593db5..a46ae45 100644
--- a/core/java/android/text/method/DateTimeKeyListener.java
+++ b/core/java/android/text/method/DateTimeKeyListener.java
@@ -35,6 +35,7 @@
* with hardware keyboards. Software input methods have no obligation to trigger
* the methods in this class.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class DateTimeKeyListener extends NumberKeyListener
{
public int getInputType() {
diff --git a/core/java/android/text/method/DialerKeyListener.java b/core/java/android/text/method/DialerKeyListener.java
index 17abed6..9eea51a 100644
--- a/core/java/android/text/method/DialerKeyListener.java
+++ b/core/java/android/text/method/DialerKeyListener.java
@@ -28,6 +28,7 @@
* with hardware keyboards. Software input methods have no obligation to trigger
* the methods in this class.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class DialerKeyListener extends NumberKeyListener
{
@Override
diff --git a/core/java/android/text/method/DigitsKeyListener.java b/core/java/android/text/method/DigitsKeyListener.java
index d9f2dcf..c97d4af 100644
--- a/core/java/android/text/method/DigitsKeyListener.java
+++ b/core/java/android/text/method/DigitsKeyListener.java
@@ -40,6 +40,7 @@
* with hardware keyboards. Software input methods have no obligation to trigger
* the methods in this class.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class DigitsKeyListener extends NumberKeyListener
{
private char[] mAccepted;
diff --git a/core/java/android/text/method/HideReturnsTransformationMethod.java b/core/java/android/text/method/HideReturnsTransformationMethod.java
index 40ce871..8b93b35 100644
--- a/core/java/android/text/method/HideReturnsTransformationMethod.java
+++ b/core/java/android/text/method/HideReturnsTransformationMethod.java
@@ -24,6 +24,7 @@
* to be hidden by displaying them as zero-width non-breaking space
* characters (\uFEFF).
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class HideReturnsTransformationMethod
extends ReplacementTransformationMethod {
private static char[] ORIGINAL = new char[] { '\r' };
diff --git a/core/java/android/text/method/InsertModeTransformationMethod.java b/core/java/android/text/method/InsertModeTransformationMethod.java
index 6c6576f..ace2d25 100644
--- a/core/java/android/text/method/InsertModeTransformationMethod.java
+++ b/core/java/android/text/method/InsertModeTransformationMethod.java
@@ -58,6 +58,7 @@
* the new transformed text: "hello abc\n\n world", and the highlight range will be [5, 11).
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class InsertModeTransformationMethod implements TransformationMethod, TextWatcher {
/** The start offset of the highlight range in the original text, inclusive. */
private int mStart;
diff --git a/core/java/android/text/method/KeyListener.java b/core/java/android/text/method/KeyListener.java
index ce7054c..447c4d9 100644
--- a/core/java/android/text/method/KeyListener.java
+++ b/core/java/android/text/method/KeyListener.java
@@ -34,6 +34,7 @@
* targetting Jelly Bean or later, and will only deliver it for some
* key presses to applications targetting Ice Cream Sandwich or earlier.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface KeyListener {
/**
* Return the type of text that this key listener is manipulating,
diff --git a/core/java/android/text/method/LinkMovementMethod.java b/core/java/android/text/method/LinkMovementMethod.java
index 9f4a0ae..484bc1a 100644
--- a/core/java/android/text/method/LinkMovementMethod.java
+++ b/core/java/android/text/method/LinkMovementMethod.java
@@ -33,6 +33,7 @@
* A movement method that traverses links in the text buffer and scrolls if necessary.
* Supports clicking on links with DPad Center or Enter.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class LinkMovementMethod extends ScrollingMovementMethod {
private static final int CLICK = 1;
private static final int UP = 2;
diff --git a/core/java/android/text/method/MetaKeyKeyListener.java b/core/java/android/text/method/MetaKeyKeyListener.java
index d1d7c96..7c9c2f1 100644
--- a/core/java/android/text/method/MetaKeyKeyListener.java
+++ b/core/java/android/text/method/MetaKeyKeyListener.java
@@ -71,6 +71,7 @@
* }
* </code>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class MetaKeyKeyListener {
/**
* Flag that indicates that the SHIFT key is on.
diff --git a/core/java/android/text/method/MovementMethod.java b/core/java/android/text/method/MovementMethod.java
index f6fe575..5ea439d 100644
--- a/core/java/android/text/method/MovementMethod.java
+++ b/core/java/android/text/method/MovementMethod.java
@@ -32,6 +32,7 @@
* directly by applications.
* </p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface MovementMethod {
public void initialize(TextView widget, Spannable text);
public boolean onKeyDown(TextView widget, Spannable text, int keyCode, KeyEvent event);
diff --git a/core/java/android/text/method/MultiTapKeyListener.java b/core/java/android/text/method/MultiTapKeyListener.java
index 5770482..022853ab 100644
--- a/core/java/android/text/method/MultiTapKeyListener.java
+++ b/core/java/android/text/method/MultiTapKeyListener.java
@@ -36,6 +36,7 @@
* with hardware keyboards. Software input methods have no obligation to trigger
* the methods in this class.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class MultiTapKeyListener extends BaseKeyListener
implements SpanWatcher {
private static MultiTapKeyListener[] sInstance =
diff --git a/core/java/android/text/method/NumberKeyListener.java b/core/java/android/text/method/NumberKeyListener.java
index 2b038dd..e32ccd4 100644
--- a/core/java/android/text/method/NumberKeyListener.java
+++ b/core/java/android/text/method/NumberKeyListener.java
@@ -39,6 +39,7 @@
* with hardware keyboards. Software input methods have no obligation to trigger
* the methods in this class.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class NumberKeyListener extends BaseKeyListener
implements InputFilter
{
diff --git a/core/java/android/text/method/OffsetMapping.java b/core/java/android/text/method/OffsetMapping.java
index fcf3de6..99613d3 100644
--- a/core/java/android/text/method/OffsetMapping.java
+++ b/core/java/android/text/method/OffsetMapping.java
@@ -27,6 +27,7 @@
* {@link TransformationMethod} that alters the text length.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface OffsetMapping {
/**
* The mapping strategy for a character offset.
diff --git a/core/java/android/text/method/PasswordTransformationMethod.java b/core/java/android/text/method/PasswordTransformationMethod.java
index 53553be..4a61d9a 100644
--- a/core/java/android/text/method/PasswordTransformationMethod.java
+++ b/core/java/android/text/method/PasswordTransformationMethod.java
@@ -33,6 +33,7 @@
import java.lang.ref.WeakReference;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PasswordTransformationMethod
implements TransformationMethod, TextWatcher
{
diff --git a/core/java/android/text/method/QwertyKeyListener.java b/core/java/android/text/method/QwertyKeyListener.java
index c43864d..27c58ea 100644
--- a/core/java/android/text/method/QwertyKeyListener.java
+++ b/core/java/android/text/method/QwertyKeyListener.java
@@ -37,6 +37,7 @@
* with hardware keyboards. Software input methods have no obligation to trigger
* the methods in this class.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class QwertyKeyListener extends BaseKeyListener {
private static QwertyKeyListener[] sInstance =
new QwertyKeyListener[Capitalize.values().length * 2];
diff --git a/core/java/android/text/method/ReplacementTransformationMethod.java b/core/java/android/text/method/ReplacementTransformationMethod.java
index d6f879a..05899d7 100644
--- a/core/java/android/text/method/ReplacementTransformationMethod.java
+++ b/core/java/android/text/method/ReplacementTransformationMethod.java
@@ -30,6 +30,7 @@
* array to be replaced by the corresponding characters in the
* {@link #getReplacement} array.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class ReplacementTransformationMethod
implements TransformationMethod
{
diff --git a/core/java/android/text/method/ScrollingMovementMethod.java b/core/java/android/text/method/ScrollingMovementMethod.java
index 4f422cb..2e0eda9 100644
--- a/core/java/android/text/method/ScrollingMovementMethod.java
+++ b/core/java/android/text/method/ScrollingMovementMethod.java
@@ -25,6 +25,7 @@
/**
* A movement method that interprets movement keys by scrolling the text buffer.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ScrollingMovementMethod extends BaseMovementMethod implements MovementMethod {
@Override
protected boolean left(TextView widget, Spannable buffer) {
diff --git a/core/java/android/text/method/SingleLineTransformationMethod.java b/core/java/android/text/method/SingleLineTransformationMethod.java
index 818526a..d6eff86 100644
--- a/core/java/android/text/method/SingleLineTransformationMethod.java
+++ b/core/java/android/text/method/SingleLineTransformationMethod.java
@@ -21,6 +21,7 @@
* displayed as spaces instead of causing line breaks, and causes
* carriage return characters (\r) to have no appearance.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SingleLineTransformationMethod
extends ReplacementTransformationMethod {
private static char[] ORIGINAL = new char[] { '\n', '\r' };
diff --git a/core/java/android/text/method/TextKeyListener.java b/core/java/android/text/method/TextKeyListener.java
index 2eb917b..1b0ae61 100644
--- a/core/java/android/text/method/TextKeyListener.java
+++ b/core/java/android/text/method/TextKeyListener.java
@@ -43,6 +43,7 @@
* with hardware keyboards. Software input methods have no obligation to trigger
* the methods in this class.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TextKeyListener extends BaseKeyListener implements SpanWatcher {
private static TextKeyListener[] sInstance =
new TextKeyListener[Capitalize.values().length * 2];
diff --git a/core/java/android/text/method/TimeKeyListener.java b/core/java/android/text/method/TimeKeyListener.java
index f11f400..337611c 100644
--- a/core/java/android/text/method/TimeKeyListener.java
+++ b/core/java/android/text/method/TimeKeyListener.java
@@ -35,6 +35,7 @@
* with hardware keyboards. Software input methods have no obligation to trigger
* the methods in this class.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TimeKeyListener extends NumberKeyListener
{
public int getInputType() {
diff --git a/core/java/android/text/method/Touch.java b/core/java/android/text/method/Touch.java
index 44811cb..85aadba 100644
--- a/core/java/android/text/method/Touch.java
+++ b/core/java/android/text/method/Touch.java
@@ -25,6 +25,7 @@
import android.view.ViewConfiguration;
import android.widget.TextView;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Touch {
private Touch() { }
diff --git a/core/java/android/text/method/TransformationMethod.java b/core/java/android/text/method/TransformationMethod.java
index 8f3b334..5246baa 100644
--- a/core/java/android/text/method/TransformationMethod.java
+++ b/core/java/android/text/method/TransformationMethod.java
@@ -24,6 +24,7 @@
* characters of passwords with dots, or keeping the newline characters
* from causing line breaks in single-line text fields.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface TransformationMethod
{
/**
diff --git a/core/java/android/text/method/TransformationMethod2.java b/core/java/android/text/method/TransformationMethod2.java
index 8d5ec24..6e0feb419 100644
--- a/core/java/android/text/method/TransformationMethod2.java
+++ b/core/java/android/text/method/TransformationMethod2.java
@@ -23,6 +23,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface TransformationMethod2 extends TransformationMethod {
/**
* Relax the contract of TransformationMethod to allow length changes,
diff --git a/core/java/android/text/method/TranslationTransformationMethod.java b/core/java/android/text/method/TranslationTransformationMethod.java
index 43d186e..8f43d3d 100644
--- a/core/java/android/text/method/TranslationTransformationMethod.java
+++ b/core/java/android/text/method/TranslationTransformationMethod.java
@@ -33,6 +33,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TranslationTransformationMethod implements TransformationMethod2 {
private static final String TAG = "TranslationTransformationMethod";
diff --git a/core/java/android/text/method/WordIterator.java b/core/java/android/text/method/WordIterator.java
index 2956f84..d57fa9b 100644
--- a/core/java/android/text/method/WordIterator.java
+++ b/core/java/android/text/method/WordIterator.java
@@ -37,6 +37,7 @@
* Also provides methods to determine word boundaries.
* {@hide}
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class WordIterator implements Selection.PositionIterator {
// Size of the window for the word iterator, should be greater than the longest word's length
private static final int WINDOW_WIDTH = 50;
diff --git a/core/java/android/text/style/AbsoluteSizeSpan.java b/core/java/android/text/style/AbsoluteSizeSpan.java
index 6d4f05a..1bc5d71 100644
--- a/core/java/android/text/style/AbsoluteSizeSpan.java
+++ b/core/java/android/text/style/AbsoluteSizeSpan.java
@@ -32,6 +32,7 @@
* <img src="{@docRoot}reference/android/images/text/style/absolutesizespan.png" />
* <figcaption>Text with text size updated.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AbsoluteSizeSpan extends MetricAffectingSpan implements ParcelableSpan {
private final int mSize;
diff --git a/core/java/android/text/style/AccessibilityClickableSpan.java b/core/java/android/text/style/AccessibilityClickableSpan.java
index ee8d156..5741f2a 100644
--- a/core/java/android/text/style/AccessibilityClickableSpan.java
+++ b/core/java/android/text/style/AccessibilityClickableSpan.java
@@ -43,6 +43,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AccessibilityClickableSpan extends ClickableSpan
implements ParcelableSpan {
// The id of the span this one replaces
diff --git a/core/java/android/text/style/AccessibilityReplacementSpan.java b/core/java/android/text/style/AccessibilityReplacementSpan.java
index e4fc147..af3a324 100644
--- a/core/java/android/text/style/AccessibilityReplacementSpan.java
+++ b/core/java/android/text/style/AccessibilityReplacementSpan.java
@@ -31,6 +31,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AccessibilityReplacementSpan extends ReplacementSpan
implements ParcelableSpan {
diff --git a/core/java/android/text/style/AccessibilityURLSpan.java b/core/java/android/text/style/AccessibilityURLSpan.java
index e280bdf..1fb76e7 100644
--- a/core/java/android/text/style/AccessibilityURLSpan.java
+++ b/core/java/android/text/style/AccessibilityURLSpan.java
@@ -27,6 +27,7 @@
* @hide
*/
@SuppressWarnings("ParcelableCreator")
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AccessibilityURLSpan extends URLSpan implements Parcelable {
final AccessibilityClickableSpan mAccessibilityClickableSpan;
diff --git a/core/java/android/text/style/AlignmentSpan.java b/core/java/android/text/style/AlignmentSpan.java
index 31db78a..53cbd63 100644
--- a/core/java/android/text/style/AlignmentSpan.java
+++ b/core/java/android/text/style/AlignmentSpan.java
@@ -25,6 +25,7 @@
/**
* Span that allows defining the alignment of text at the paragraph level.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface AlignmentSpan extends ParagraphStyle {
/**
diff --git a/core/java/android/text/style/BackgroundColorSpan.java b/core/java/android/text/style/BackgroundColorSpan.java
index 13647d9..bb04d0ff 100644
--- a/core/java/android/text/style/BackgroundColorSpan.java
+++ b/core/java/android/text/style/BackgroundColorSpan.java
@@ -34,6 +34,7 @@
* <img src="{@docRoot}reference/android/images/text/style/backgroundcolorspan.png" />
* <figcaption>Set a background color for the text.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class BackgroundColorSpan extends CharacterStyle
implements UpdateAppearance, ParcelableSpan {
diff --git a/core/java/android/text/style/BulletSpan.java b/core/java/android/text/style/BulletSpan.java
index f70e6c5..24ae6e2 100644
--- a/core/java/android/text/style/BulletSpan.java
+++ b/core/java/android/text/style/BulletSpan.java
@@ -63,6 +63,7 @@
* <img src="{@docRoot}reference/android/images/text/style/custombulletspan.png" />
* <figcaption>Customized BulletSpan.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class BulletSpan implements LeadingMarginSpan, ParcelableSpan {
// Bullet is slightly bigger to avoid aliasing artifacts on mdpi devices.
private static final int STANDARD_BULLET_RADIUS = 4;
diff --git a/core/java/android/text/style/CharacterStyle.java b/core/java/android/text/style/CharacterStyle.java
index 5b95f1a..2ea05e6 100644
--- a/core/java/android/text/style/CharacterStyle.java
+++ b/core/java/android/text/style/CharacterStyle.java
@@ -23,6 +23,7 @@
* class. Most extend its subclass {@link MetricAffectingSpan}, but simple
* ones may just implement {@link UpdateAppearance}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class CharacterStyle {
public abstract void updateDrawState(TextPaint tp);
diff --git a/core/java/android/text/style/ClickableSpan.java b/core/java/android/text/style/ClickableSpan.java
index 238da55..9e35d75 100644
--- a/core/java/android/text/style/ClickableSpan.java
+++ b/core/java/android/text/style/ClickableSpan.java
@@ -36,6 +36,7 @@
* <img src="{@docRoot}reference/android/images/text/style/clickablespan.png" />
* <figcaption>Text with <code>ClickableSpan</code>.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class ClickableSpan extends CharacterStyle implements UpdateAppearance {
private static int sIdCounter = 0;
diff --git a/core/java/android/text/style/ForegroundColorSpan.java b/core/java/android/text/style/ForegroundColorSpan.java
index 5c97426..337c49f 100644
--- a/core/java/android/text/style/ForegroundColorSpan.java
+++ b/core/java/android/text/style/ForegroundColorSpan.java
@@ -34,6 +34,7 @@
* <img src="{@docRoot}reference/android/images/text/style/foregroundcolorspan.png" />
* <figcaption>Set a text color.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ForegroundColorSpan extends CharacterStyle
implements UpdateAppearance, ParcelableSpan {
diff --git a/core/java/android/text/style/IconMarginSpan.java b/core/java/android/text/style/IconMarginSpan.java
index a6c5139..cc946e9 100644
--- a/core/java/android/text/style/IconMarginSpan.java
+++ b/core/java/android/text/style/IconMarginSpan.java
@@ -44,6 +44,7 @@
* @see DrawableMarginSpan for working with a {@link android.graphics.drawable.Drawable} instead of
* a {@link Bitmap}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class IconMarginSpan implements LeadingMarginSpan, LineHeightSpan {
@NonNull
diff --git a/core/java/android/text/style/LeadingMarginSpan.java b/core/java/android/text/style/LeadingMarginSpan.java
index 5bd2d60..60c4578 100644
--- a/core/java/android/text/style/LeadingMarginSpan.java
+++ b/core/java/android/text/style/LeadingMarginSpan.java
@@ -32,6 +32,7 @@
* LeadingMarginSpans should be attached from the first character to the last
* character of a single paragraph.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface LeadingMarginSpan
extends ParagraphStyle
{
diff --git a/core/java/android/text/style/LineBackgroundSpan.java b/core/java/android/text/style/LineBackgroundSpan.java
index 7cb9147..c2d38ce 100644
--- a/core/java/android/text/style/LineBackgroundSpan.java
+++ b/core/java/android/text/style/LineBackgroundSpan.java
@@ -28,6 +28,7 @@
/**
* Used to change the background of lines where the span is attached to.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface LineBackgroundSpan extends ParagraphStyle
{
/**
diff --git a/core/java/android/text/style/LineBreakConfigSpan.java b/core/java/android/text/style/LineBreakConfigSpan.java
index eeb6383..1af1eed 100644
--- a/core/java/android/text/style/LineBreakConfigSpan.java
+++ b/core/java/android/text/style/LineBreakConfigSpan.java
@@ -31,6 +31,7 @@
* LineBreakSpan for changing line break style of the specific region of the text.
*/
@FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class LineBreakConfigSpan implements ParcelableSpan {
private final LineBreakConfig mLineBreakConfig;
diff --git a/core/java/android/text/style/LineHeightSpan.java b/core/java/android/text/style/LineHeightSpan.java
index ae565d1..71e8932 100644
--- a/core/java/android/text/style/LineHeightSpan.java
+++ b/core/java/android/text/style/LineHeightSpan.java
@@ -30,6 +30,7 @@
/**
* The classes that affect the line height of paragraph should implement this interface.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface LineHeightSpan extends ParagraphStyle, WrapTogetherSpan {
/**
* Classes that implement this should define how the height is being calculated.
diff --git a/core/java/android/text/style/LocaleSpan.java b/core/java/android/text/style/LocaleSpan.java
index 489ceea..be5525a 100644
--- a/core/java/android/text/style/LocaleSpan.java
+++ b/core/java/android/text/style/LocaleSpan.java
@@ -32,6 +32,7 @@
/**
* Changes the {@link Locale} of the text to which the span is attached.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class LocaleSpan extends MetricAffectingSpan implements ParcelableSpan {
@NonNull
private final LocaleList mLocales;
diff --git a/core/java/android/text/style/MaskFilterSpan.java b/core/java/android/text/style/MaskFilterSpan.java
index 587d1b4..44db012 100644
--- a/core/java/android/text/style/MaskFilterSpan.java
+++ b/core/java/android/text/style/MaskFilterSpan.java
@@ -30,6 +30,7 @@
* <img src="{@docRoot}reference/android/images/text/style/maskfilterspan.png" />
* <figcaption>Text blurred with the <code>MaskFilterSpan</code>.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class MaskFilterSpan extends CharacterStyle implements UpdateAppearance {
private MaskFilter mFilter;
diff --git a/core/java/android/text/style/MetricAffectingSpan.java b/core/java/android/text/style/MetricAffectingSpan.java
index 61b7947..f30fdc1 100644
--- a/core/java/android/text/style/MetricAffectingSpan.java
+++ b/core/java/android/text/style/MetricAffectingSpan.java
@@ -23,6 +23,7 @@
* The classes that affect character-level text formatting in a way that
* changes the width or height of characters extend this class.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class MetricAffectingSpan
extends CharacterStyle
implements UpdateLayout {
diff --git a/core/java/android/text/style/NoWritingToolsSpan.java b/core/java/android/text/style/NoWritingToolsSpan.java
index 90f85aa..c7dfcfa 100644
--- a/core/java/android/text/style/NoWritingToolsSpan.java
+++ b/core/java/android/text/style/NoWritingToolsSpan.java
@@ -32,6 +32,7 @@
* tools should only rewrite the user input text, and not modify the quoted text.
*/
@FlaggedApi(FLAG_WRITING_TOOLS)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class NoWritingToolsSpan implements ParcelableSpan {
/**
diff --git a/core/java/android/text/style/ParagraphStyle.java b/core/java/android/text/style/ParagraphStyle.java
index 423156e..27c1e26 100644
--- a/core/java/android/text/style/ParagraphStyle.java
+++ b/core/java/android/text/style/ParagraphStyle.java
@@ -20,6 +20,7 @@
* The classes that affect paragraph-level text formatting implement
* this interface.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface ParagraphStyle
{
diff --git a/core/java/android/text/style/QuoteSpan.java b/core/java/android/text/style/QuoteSpan.java
index 393ede6..99c9574 100644
--- a/core/java/android/text/style/QuoteSpan.java
+++ b/core/java/android/text/style/QuoteSpan.java
@@ -57,6 +57,7 @@
* <img src="{@docRoot}reference/android/images/text/style/customquotespan.png" />
* <figcaption>Customized <code>QuoteSpan</code>.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class QuoteSpan implements LeadingMarginSpan, ParcelableSpan {
/**
* Default stripe width in pixels.
diff --git a/core/java/android/text/style/RasterizerSpan.java b/core/java/android/text/style/RasterizerSpan.java
index f0be50a..cf8599c 100644
--- a/core/java/android/text/style/RasterizerSpan.java
+++ b/core/java/android/text/style/RasterizerSpan.java
@@ -22,6 +22,7 @@
/**
* @removed Rasterizer is not supported for hw-accerlerated and PDF rendering
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class RasterizerSpan extends CharacterStyle implements UpdateAppearance {
private Rasterizer mRasterizer;
diff --git a/core/java/android/text/style/RelativeSizeSpan.java b/core/java/android/text/style/RelativeSizeSpan.java
index 5c91b20..38d5d38 100644
--- a/core/java/android/text/style/RelativeSizeSpan.java
+++ b/core/java/android/text/style/RelativeSizeSpan.java
@@ -34,6 +34,7 @@
* <img src="{@docRoot}reference/android/images/text/style/relativesizespan.png" />
* <figcaption>Text increased by 50% with <code>RelativeSizeSpan</code>.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class RelativeSizeSpan extends MetricAffectingSpan implements ParcelableSpan {
private final float mProportion;
diff --git a/core/java/android/text/style/ReplacementSpan.java b/core/java/android/text/style/ReplacementSpan.java
index 9430fd3..a6fe1fe 100644
--- a/core/java/android/text/style/ReplacementSpan.java
+++ b/core/java/android/text/style/ReplacementSpan.java
@@ -23,6 +23,7 @@
import android.graphics.Paint;
import android.text.TextPaint;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class ReplacementSpan extends MetricAffectingSpan {
private CharSequence mContentDescription = null;
diff --git a/core/java/android/text/style/ScaleXSpan.java b/core/java/android/text/style/ScaleXSpan.java
index d022b07..009973e 100644
--- a/core/java/android/text/style/ScaleXSpan.java
+++ b/core/java/android/text/style/ScaleXSpan.java
@@ -36,6 +36,7 @@
* <img src="{@docRoot}reference/android/images/text/style/scalexspan.png" />
* <figcaption>Text scaled by 100% with <code>ScaleXSpan</code>.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ScaleXSpan extends MetricAffectingSpan implements ParcelableSpan {
private final float mProportion;
diff --git a/core/java/android/text/style/SpanUtils.java b/core/java/android/text/style/SpanUtils.java
index 6b4bd1a..21a96cd 100644
--- a/core/java/android/text/style/SpanUtils.java
+++ b/core/java/android/text/style/SpanUtils.java
@@ -30,6 +30,7 @@
/**
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SpanUtils {
private SpanUtils() {} // Do not instantiate
diff --git a/core/java/android/text/style/SpellCheckSpan.java b/core/java/android/text/style/SpellCheckSpan.java
index e8ec3c6..39cd279 100644
--- a/core/java/android/text/style/SpellCheckSpan.java
+++ b/core/java/android/text/style/SpellCheckSpan.java
@@ -28,6 +28,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SpellCheckSpan implements ParcelableSpan {
private boolean mSpellCheckInProgress;
diff --git a/core/java/android/text/style/StrikethroughSpan.java b/core/java/android/text/style/StrikethroughSpan.java
index 65ee347..3654870 100644
--- a/core/java/android/text/style/StrikethroughSpan.java
+++ b/core/java/android/text/style/StrikethroughSpan.java
@@ -32,6 +32,7 @@
* <img src="{@docRoot}reference/android/images/text/style/strikethroughspan.png" />
* <figcaption>Strikethrough text.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class StrikethroughSpan extends CharacterStyle
implements UpdateAppearance, ParcelableSpan {
diff --git a/core/java/android/text/style/StyleSpan.java b/core/java/android/text/style/StyleSpan.java
index 378682b..c01e134 100644
--- a/core/java/android/text/style/StyleSpan.java
+++ b/core/java/android/text/style/StyleSpan.java
@@ -44,6 +44,7 @@
* <img src="{@docRoot}reference/android/images/text/style/stylespan.png" />
* <figcaption>Text styled bold and italic with the <code>StyleSpan</code>.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class StyleSpan extends MetricAffectingSpan implements ParcelableSpan {
private final int mStyle;
diff --git a/core/java/android/text/style/SubscriptSpan.java b/core/java/android/text/style/SubscriptSpan.java
index 729a9ad..54c765d 100644
--- a/core/java/android/text/style/SubscriptSpan.java
+++ b/core/java/android/text/style/SubscriptSpan.java
@@ -37,6 +37,7 @@
* Note: Since the span affects the position of the text, if the text is on the last line of a
* TextView, it may appear cut.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SubscriptSpan extends MetricAffectingSpan implements ParcelableSpan {
/**
diff --git a/core/java/android/text/style/SuggestionRangeSpan.java b/core/java/android/text/style/SuggestionRangeSpan.java
index 1eee99a..640fae4 100644
--- a/core/java/android/text/style/SuggestionRangeSpan.java
+++ b/core/java/android/text/style/SuggestionRangeSpan.java
@@ -27,6 +27,7 @@
* A SuggestionRangeSpan is used to show which part of an EditText is affected by a suggestion
* popup window.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class SuggestionRangeSpan extends CharacterStyle implements ParcelableSpan {
private int mBackgroundColor;
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index 0cf96f6..d819062 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -48,6 +48,7 @@
*
* @see TextView#isSuggestionsEnabled()
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
private static final String TAG = "SuggestionSpan";
diff --git a/core/java/android/text/style/SuperscriptSpan.java b/core/java/android/text/style/SuperscriptSpan.java
index 5610223..d3b339c 100644
--- a/core/java/android/text/style/SuperscriptSpan.java
+++ b/core/java/android/text/style/SuperscriptSpan.java
@@ -35,6 +35,7 @@
* TextView, it may appear cut. This can be avoided by decreasing the text size with an {@link
* AbsoluteSizeSpan}
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SuperscriptSpan extends MetricAffectingSpan implements ParcelableSpan {
/**
* Creates a {@link SuperscriptSpan}.
diff --git a/core/java/android/text/style/TabStopSpan.java b/core/java/android/text/style/TabStopSpan.java
index 8128475..e6733a2 100644
--- a/core/java/android/text/style/TabStopSpan.java
+++ b/core/java/android/text/style/TabStopSpan.java
@@ -24,6 +24,7 @@
* the leading margin of the line. <code>TabStopSpan</code> will only affect the first tab
* encountered on the first line of the text.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface TabStopSpan extends ParagraphStyle {
/**
diff --git a/core/java/android/text/style/TextAppearanceSpan.java b/core/java/android/text/style/TextAppearanceSpan.java
index 245a9db..7ede349 100644
--- a/core/java/android/text/style/TextAppearanceSpan.java
+++ b/core/java/android/text/style/TextAppearanceSpan.java
@@ -58,6 +58,7 @@
* @attr ref android.R.styleable#TextAppearance_fontVariationSettings
*
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TextAppearanceSpan extends MetricAffectingSpan implements ParcelableSpan {
private final String mFamilyName;
private final int mStyle;
diff --git a/core/java/android/text/style/TtsSpan.java b/core/java/android/text/style/TtsSpan.java
index e0d4ec1..6d776d1 100644
--- a/core/java/android/text/style/TtsSpan.java
+++ b/core/java/android/text/style/TtsSpan.java
@@ -42,6 +42,7 @@
* The inner classes are there for convenience and provide builders for each
* TtsSpan type.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TtsSpan implements ParcelableSpan {
private final String mType;
private final PersistableBundle mArgs;
diff --git a/core/java/android/text/style/TypefaceSpan.java b/core/java/android/text/style/TypefaceSpan.java
index bdfc772..86f7f76 100644
--- a/core/java/android/text/style/TypefaceSpan.java
+++ b/core/java/android/text/style/TypefaceSpan.java
@@ -50,6 +50,7 @@
* <figcaption>Text with <code>TypefaceSpan</code>s constructed based on a font from resource and
* from a font family.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TypefaceSpan extends MetricAffectingSpan implements ParcelableSpan {
@Nullable
diff --git a/core/java/android/text/style/URLSpan.java b/core/java/android/text/style/URLSpan.java
index 9969d29..f06627d 100644
--- a/core/java/android/text/style/URLSpan.java
+++ b/core/java/android/text/style/URLSpan.java
@@ -41,6 +41,7 @@
* <img src="{@docRoot}reference/android/images/text/style/urlspan.png" />
* <figcaption>Text with <code>URLSpan</code>.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class URLSpan extends ClickableSpan implements ParcelableSpan {
private final String mURL;
diff --git a/core/java/android/text/style/UnderlineSpan.java b/core/java/android/text/style/UnderlineSpan.java
index 075e70b..b3bb142 100644
--- a/core/java/android/text/style/UnderlineSpan.java
+++ b/core/java/android/text/style/UnderlineSpan.java
@@ -32,6 +32,7 @@
* <img src="{@docRoot}reference/android/images/text/style/underlinespan.png" />
* <figcaption>Underlined text.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class UnderlineSpan extends CharacterStyle
implements UpdateAppearance, ParcelableSpan {
diff --git a/core/java/android/text/style/UpdateAppearance.java b/core/java/android/text/style/UpdateAppearance.java
index 7112347..7b0a6d3 100644
--- a/core/java/android/text/style/UpdateAppearance.java
+++ b/core/java/android/text/style/UpdateAppearance.java
@@ -22,5 +22,6 @@
* that if the class also impacts size or other metrics, it should instead
* implement {@link UpdateLayout}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface UpdateAppearance {
}
diff --git a/core/java/android/text/style/UpdateLayout.java b/core/java/android/text/style/UpdateLayout.java
index 591075e..5af4141 100644
--- a/core/java/android/text/style/UpdateLayout.java
+++ b/core/java/android/text/style/UpdateLayout.java
@@ -22,4 +22,5 @@
* this interface. This interface also includes {@link UpdateAppearance}
* since such a change implicitly also impacts the appearance.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface UpdateLayout extends UpdateAppearance { }
diff --git a/core/java/android/text/style/WrapTogetherSpan.java b/core/java/android/text/style/WrapTogetherSpan.java
index 11721a8..cf74c1b 100644
--- a/core/java/android/text/style/WrapTogetherSpan.java
+++ b/core/java/android/text/style/WrapTogetherSpan.java
@@ -16,6 +16,7 @@
package android.text.style;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface WrapTogetherSpan
extends ParagraphStyle
{
diff --git a/core/java/android/text/util/Rfc822Token.java b/core/java/android/text/util/Rfc822Token.java
index 2f207db..d6e987b 100644
--- a/core/java/android/text/util/Rfc822Token.java
+++ b/core/java/android/text/util/Rfc822Token.java
@@ -22,6 +22,7 @@
* This class stores an RFC 822-like name, address, and comment,
* and provides methods to convert them to quoted strings.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Rfc822Token {
@Nullable
private String mName, mAddress, mComment;
diff --git a/core/java/android/text/util/Rfc822Tokenizer.java b/core/java/android/text/util/Rfc822Tokenizer.java
index 68334e4..8a9252a 100644
--- a/core/java/android/text/util/Rfc822Tokenizer.java
+++ b/core/java/android/text/util/Rfc822Tokenizer.java
@@ -27,6 +27,7 @@
* a string of addresses (such as might be typed into such a field)
* into a series of Rfc822Tokens.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Rfc822Tokenizer implements MultiAutoCompleteTextView.Tokenizer {
/**
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 231aa68..3f45e29 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -108,6 +108,7 @@
private final String mOwnerPackageName;
private final Resources mResources;
private DisplayAdjustments mDisplayAdjustments;
+ private boolean mRefreshRateChangesRegistered;
@UnsupportedAppUsage
private DisplayInfo mDisplayInfo; // never null
@@ -1217,6 +1218,10 @@
*/
public float getRefreshRate() {
synchronized (mLock) {
+ if (!mRefreshRateChangesRegistered) {
+ DisplayManagerGlobal.getInstance().registerForRefreshRateChanges();
+ mRefreshRateChangesRegistered = true;
+ }
updateDisplayInfoLocked();
return mDisplayInfo.getRefreshRate();
}
@@ -1601,7 +1606,7 @@
.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
| DisplayManagerGlobal
.INTERNAL_EVENT_FLAG_DISPLAY_HDR_SDR_RATIO_CHANGED,
- ActivityThread.currentPackageName());
+ ActivityThread.currentPackageName(), /* isEventFilterImplicit */ true);
}
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index c174fbe..3659e78 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1299,7 +1299,11 @@
// Handle the pending show request for other insets types since the IME insets
// has being requested hidden.
handlePendingControlRequest(statsToken);
- getImeSourceConsumer().removeSurface();
+ if (!Flags.refactorInsetsController()) {
+ // the surface can't be removed until the end of the animation. This is handled by
+ // IMMS after the window was requested to be hidden.
+ getImeSourceConsumer().removeSurface();
+ }
}
applyAnimation(typesReady, false /* show */, fromIme, false /* skipsCallbacks */,
statsToken);
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 0903d22..c4347f0 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -3080,6 +3080,21 @@
}
/**
+ * Changes the default ApplyToken.
+ *
+ * ApplyToken is used to determine the order in which Transactions are applied.
+ * Transactions applied with the same ApplyToken will be applied in the order
+ * they were queued in SurfaceFlinger. Transactions are sent via binder so the
+ * caller should be aware of the order in which binder calls are executed in
+ * SurfaceFlinger. This along with the ApplyToken will determine the order
+ * in which Transactions are applied. Transactions with different apply tokens
+ * will be applied in arbitrary order regardless of when they were queued in
+ * SurfaceFlinger.
+ *
+ * Caller must keep track of the previous ApplyToken if they want to restore it.
+ *
+ * Note each buffer producer should have its own ApplyToken in order to ensure
+ * that Transactions are not delayed by Transactions from other buffer producers.
*
* @hide
*/
@@ -3088,6 +3103,7 @@
}
/**
+ * Returns the default ApplyToken.
*
* @hide
*/
@@ -3096,8 +3112,10 @@
}
/**
- * Apply the transaction, clearing it's state, and making it usable
+ * Apply the transaction, clearing its state, and making it usable
* as a new transaction.
+ *
+ * This method will also increment the transaction ID for debugging purposes.
*/
public void apply() {
apply(/*sync*/ false);
@@ -3116,7 +3134,7 @@
/**
- * Clear the transaction object, without applying it.
+ * Clear the transaction object, without applying it. The transction ID is preserved.
*
* @hide
*/
@@ -3375,6 +3393,9 @@
* If two siblings share the same Z order the ordering is undefined. Surfaces
* with a negative Z will be placed below the parent surface.
*
+ * Calling setLayer after setRelativeLayer will reset the relative layer
+ * in the same transaction.
+ *
* @param sc The SurfaceControl to set the Z order on
* @param z The Z-order
* @return This Transaction.
@@ -3392,6 +3413,22 @@
}
/**
+ * Set the Z-order for a given SurfaceControl, relative to the specified SurfaceControl.
+ * The SurfaceControl with a negative z will be placed below the relativeTo
+ * SurfaceControl and the SurfaceControl with a positive z will be placed above the
+ * relativeTo SurfaceControl.
+ *
+ * Calling setLayer will reset the relative layer. Calling setRelativeLayer after setLayer
+ * will override the setLayer call.
+ *
+ * If a layer is set to be relative to a layer that is destroyed, the layer will be
+ * offscreen until setLayer is called or setRelativeLayer is called with a valid
+ * SurfaceControl.
+ *
+ * @param sc The SurfaceControl to set the Z order on
+ * @param relativeTo The SurfaceControl to set the Z order relative to
+ * @param z The Z-order
+ * @return This Transaction.
* @hide
*/
public Transaction setRelativeLayer(SurfaceControl sc, SurfaceControl relativeTo, int z) {
@@ -3405,6 +3442,9 @@
}
/**
+ * The hint from the buffer producer as to what portion of the layer is
+ * transparent.
+ *
* @hide
*/
public Transaction setTransparentRegionHint(SurfaceControl sc, Region transparentRegion) {
@@ -3438,6 +3478,10 @@
}
/**
+ * Sets the input channel for a given SurfaceControl. The position and order of the
+ * SurfaceControl in conjunction with the touchable region in the InputWindowHandle
+ * determines the hit region.
+ *
* @hide
*/
public Transaction setInputWindowInfo(SurfaceControl sc, InputWindowHandle handle) {
@@ -3549,6 +3593,8 @@
* surface. If no crop is specified and the surface has no buffer, the surface bounds is
* only constrained by the size of its parent bounds.
*
+ * To unset the crop, pass in an invalid Rect (0, 0, -1, -1)
+ *
* @param sc SurfaceControl to set crop of.
* @param crop Bounds of the crop to apply.
* @hide
@@ -3578,6 +3624,8 @@
* surface. If no crop is specified and the surface has no buffer, the surface bounds is
* only constrained by the size of its parent bounds.
*
+ * To unset the crop, pass in an invalid Rect (0, 0, -1, -1)
+ *
* @param sc SurfaceControl to set crop of.
* @param crop Bounds of the crop to apply.
* @return this This transaction for chaining
@@ -3625,6 +3673,8 @@
* surface. If no crop is specified and the surface has no buffer, the surface bounds is
* only constrained by the size of its parent bounds.
*
+ * To unset the crop, pass in an invalid Rect (0, 0, -1, -1)
+ *
* @param sc SurfaceControl to set crop of.
* @param crop Bounds of the crop to apply.
* @return this This transaction for chaining
@@ -3643,7 +3693,12 @@
}
/**
- * Sets the corner radius of a {@link SurfaceControl}.
+ * Sets the corner radius of a {@link SurfaceControl}. This corner radius is applied to the
+ * SurfaceControl and its children. The API expects a crop to be set on the SurfaceControl
+ * to ensure that the corner radius is applied to the correct region. If the crop does not
+ * intersect with the SurfaceControl's visible content, the corner radius will not be
+ * applied.
+ *
* @param sc SurfaceControl
* @param cornerRadius Corner radius in pixels.
* @return Itself.
@@ -3753,6 +3808,9 @@
}
/**
+ * Associates a layer with a display. The layer will be drawn on the display with the
+ * specified layer stack. If the layer is not a root layer, this call has no effect.
+ *
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
@@ -3791,6 +3849,7 @@
/**
* Fills the surface with the specified color.
+ *
* @param color A float array with three values to represent r, g, b in range [0..1]. An
* invalid color will remove the color fill.
* @hide
@@ -3809,8 +3868,9 @@
/**
* Removes color fill.
- * @hide
- */
+ *
+ * @hide
+ */
public Transaction unsetColor(SurfaceControl sc) {
checkPreconditions(sc);
if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
@@ -3898,6 +3958,8 @@
}
/**
+ * Sets the surface to render contents of the display to.
+ *
* @hide
*/
public Transaction setDisplaySurface(IBinder displayToken, Surface surface) {
@@ -3916,6 +3978,9 @@
}
/**
+ * Sets the layer stack of the display.
+ *
+ * All layers with the same layer stack will be drawn on this display.
* @hide
*/
public Transaction setDisplayLayerStack(IBinder displayToken, int layerStack) {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index dd32947..b98f4db 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.view.flags.Flags.FLAG_DEPRECATE_SURFACE_VIEW_Z_ORDER_APIS;
import static android.view.flags.Flags.FLAG_SURFACE_VIEW_GET_SURFACE_PACKAGE;
import static android.view.flags.Flags.FLAG_SURFACE_VIEW_SET_COMPOSITION_ORDER;
import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
@@ -812,7 +813,12 @@
* window is attached to the window manager.
*
* <p>Calling this overrides any previous call to {@link #setZOrderOnTop}.
+ *
+ * @deprecated Use {@link #setCompositionOrder(int)} instead. It provides more
+ * control over the Z ordering behavior.
*/
+ @Deprecated
+ @FlaggedApi(FLAG_DEPRECATE_SURFACE_VIEW_Z_ORDER_APIS)
public void setZOrderMediaOverlay(boolean isMediaOverlay) {
mRequestedSubLayer = isMediaOverlay
? APPLICATION_MEDIA_OVERLAY_SUBLAYER : APPLICATION_MEDIA_SUBLAYER;
@@ -834,7 +840,12 @@
* <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
*
* @param onTop Whether to show the surface on top of this view's window.
+ *
+ * @deprecated Use {@link #setCompositionOrder(int)} instead. It provides more
+ * control over the Z ordering behavior.
*/
+ @Deprecated
+ @FlaggedApi(FLAG_DEPRECATE_SURFACE_VIEW_Z_ORDER_APIS)
public void setZOrderOnTop(boolean onTop) {
// In R and above we allow dynamic layer changes.
final boolean allowDynamicChange = getContext().getApplicationInfo().targetSdkVersion
@@ -866,7 +877,11 @@
* @return Whether the Z ordering changed.
*
* @hide
+ *
+ * @deprecated Use {@link #setCompositionOrder(int)} instead. It provides more control
+ * over the Z ordering behavior.
*/
+ @Deprecated
public boolean setZOrderedOnTop(boolean onTop, boolean allowDynamicChange) {
final int subLayer;
if (onTop) {
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index e3ea6b22..a952967 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -1290,7 +1290,8 @@
int newTop = Math.max(0, insets.top - top);
int newRight = Math.max(0, insets.right - right);
int newBottom = Math.max(0, insets.bottom - bottom);
- if (newLeft == left && newTop == top && newRight == right && newBottom == bottom) {
+ if (newLeft == insets.left && newTop == insets.top
+ && newRight == insets.right && newBottom == insets.bottom) {
return insets;
}
return Insets.of(newLeft, newTop, newRight, newBottom);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 93eed37..24647f4 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1199,6 +1199,43 @@
"android.window.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE";
/**
+ * Application-level [PackageManager][android.content.pm.PackageManager.Property] tag that (when
+ * set to false) informs the system the app has opted out of the camera compatibility treatment
+ * for fixed-orientation apps, which simulates running on a portrait device, in the orientation
+ * requested by the app.
+ *
+ * <p>This treatment aims to mitigate camera issues on large screens, like stretched or sideways
+ * previews. It simulates running on a portrait device by:
+ * <ul>
+ * <li>Letterboxing the app window,
+ * <li>Cropping the camera buffer to match the app's requested orientation,
+ * <li>Setting the camera sensor orientation to portrait.
+ * <li>Setting the display rotation to match the app's requested orientation, given portrait
+ * natural orientation,
+ * <li>Refreshes the activity to trigger new camera setup, with sandboxed values.
+ * </ul>
+ *
+ * <p>To opt out of the camera compatibility treatment, add this property to your app manifest
+ * and set the value to {@code false}.
+ *
+ * <p>Not setting this property at all, or setting this property to {@code true} has no effect.
+ *
+ * <p><b>Syntax:</b>
+ * <pre>
+ * <application>
+ * <property
+ * android:name="android.window.PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION"
+ * android:value="true|false"/>
+ * </application>
+ * </pre>
+ *
+ * @hide
+ */
+ //TODO(b/394590412): Make this property public.
+ String PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION =
+ "android.window.PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION";
+
+ /**
* Application level {@link android.content.pm.PackageManager.Property PackageManager.Property}
* for an app to inform the system that the app should be excluded from the compatibility
* override for orientation set by the device manufacturer. When the orientation override is
diff --git a/core/java/android/view/flags/view_flags.aconfig b/core/java/android/view/flags/view_flags.aconfig
index f6fdec9..d06f885 100644
--- a/core/java/android/view/flags/view_flags.aconfig
+++ b/core/java/android/view/flags/view_flags.aconfig
@@ -129,6 +129,14 @@
}
flag {
+ name: "deprecate_surface_view_z_order_apis"
+ namespace: "window_surfaces"
+ description: "Deprecate SurfaceView z order control APIs."
+ bug: "341021569"
+ is_fixed_read_only: true
+}
+
+flag {
name: "use_refactored_round_scrollbar"
namespace: "wear_frameworks"
description: "Use refactored round scrollbar."
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 421001f..0b34600 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -532,6 +532,7 @@
final H mH;
// Our generic input connection if the current target does not have its own.
+ @NonNull
private final RemoteInputConnectionImpl mFallbackInputConnection;
private final int mDisplayId;
diff --git a/core/java/android/view/textclassifier/intent/OWNERS b/core/java/android/view/textclassifier/intent/OWNERS
index 3465fe6..dc18514 100644
--- a/core/java/android/view/textclassifier/intent/OWNERS
+++ b/core/java/android/view/textclassifier/intent/OWNERS
@@ -1,6 +1,5 @@
# Bug component: 709498
-mns@google.com
toki@google.com
svetoslavganov@android.com
svetoslavganov@google.com
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 0a5c14e..222a7b3 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -9216,6 +9216,7 @@
public static RemoteResponse fromFillInIntent(@NonNull Intent fillIntent) {
RemoteResponse response = new RemoteResponse();
response.mFillIntent = fillIntent;
+ fillIntent.collectExtraIntentKeys();
return response;
}
@@ -9224,6 +9225,7 @@
RemoteResponse response = new RemoteResponse();
response.mPendingIntent = pendingIntent;
response.mFillIntent = intent;
+ intent.collectExtraIntentKeys();
return response;
}
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index b44620f..4aeedbb 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -102,9 +102,15 @@
ENABLE_HOLD_TO_DRAG_APP_HANDLE(Flags::enableHoldToDragAppHandle, true),
ENABLE_MINIMIZE_BUTTON(Flags::enableMinimizeButton, true),
ENABLE_MODALS_FULLSCREEN_WITH_PERMISSIONS(Flags::enableModalsFullscreenWithPermission, false),
+ ENABLE_OPAQUE_BACKGROUND_FOR_TRANSPARENT_WINDOWS(
+ Flags::enableOpaqueBackgroundForTransparentWindows, false),
+ ENABLE_QUICKSWITCH_DESKTOP_SPLIT_BUGFIX(Flags::enableQuickswitchDesktopSplitBugfix, true),
ENABLE_RESIZING_METRICS(Flags::enableResizingMetrics, true),
ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE(
Flags::enableRestoreToPreviousSizeFromDesktopImmersive, true),
+ ENABLE_START_LAUNCH_TRANSITION_FROM_TASKBAR_BUGFIX(
+ Flags::enableStartLaunchTransitionFromTaskbarBugfix, 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),
@@ -117,6 +123,8 @@
ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS(
Flags::enableWindowingTransitionHandlersObservers, false),
EXCLUDE_CAPTION_FROM_APP_BOUNDS(Flags::excludeCaptionFromAppBounds, false),
+ IGNORE_ASPECT_RATIO_RESTRICTIONS_FOR_RESIZEABLE_FREEFORM_ACTIVITIES(
+ Flags::ignoreAspectRatioRestrictionsForResizeableFreeformActivities, true),
INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC(
Flags::includeTopTransparentFullscreenTaskInDesktopHeuristic, true)
// go/keep-sorted end
diff --git a/core/java/android/window/TransitionFilter.java b/core/java/android/window/TransitionFilter.java
index 61fc622..d8d2011 100644
--- a/core/java/android/window/TransitionFilter.java
+++ b/core/java/android/window/TransitionFilter.java
@@ -31,8 +31,6 @@
import android.os.Parcelable;
import android.view.WindowManager;
-import com.android.window.flags.Flags;
-
/**
* A parcelable filter that can be used for rerouting transitions to a remote. This is a local
* representation so that the transition system doesn't need to make blocking queries over
@@ -261,9 +259,7 @@
// only applies to activity/task
&& (change.getTaskInfo() != null
|| change.getActivityComponent() != null)) {
- final TransitionInfo.AnimationOptions opts =
- Flags.moveAnimationOptionsToChange() ? change.getAnimationOptions()
- : info.getAnimationOptions();
+ final TransitionInfo.AnimationOptions opts = change.getAnimationOptions();
if (opts != null) {
boolean canActuallyOverride = change.getTaskInfo() == null
|| opts.getOverrideTaskTransition();
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 4f34aa3..32175f1 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -57,8 +57,6 @@
import android.view.SurfaceControl;
import android.view.WindowManager;
-import com.android.window.flags.Flags;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -220,10 +218,6 @@
private final ArrayList<Change> mChanges = new ArrayList<>();
private final ArrayList<Root> mRoots = new ArrayList<>();
- // TODO(b/327332488): Clean-up usages after the flag is fully enabled.
- @Deprecated
- private AnimationOptions mOptions;
-
/** This is only a BEST-EFFORT id used for log correlation. DO NOT USE for any real work! */
private int mDebugId = -1;
@@ -238,7 +232,6 @@
mFlags = in.readInt();
in.readTypedList(mChanges, Change.CREATOR);
in.readTypedList(mRoots, Root.CREATOR);
- mOptions = in.readTypedObject(AnimationOptions.CREATOR);
mDebugId = in.readInt();
mTrack = in.readInt();
}
@@ -250,7 +243,6 @@
dest.writeInt(mFlags);
dest.writeTypedList(mChanges);
dest.writeTypedList(mRoots, flags);
- dest.writeTypedObject(mOptions, flags);
dest.writeInt(mDebugId);
dest.writeInt(mTrack);
}
@@ -286,18 +278,6 @@
mRoots.add(other);
}
- /**
- * @deprecated Set {@link AnimationOptions} to change. This method is only used if
- * {@link Flags#FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE} is disabled.
- */
- @Deprecated
- public void setAnimationOptions(@Nullable AnimationOptions options) {
- if (Flags.moveAnimationOptionsToChange()) {
- return;
- }
- mOptions = options;
- }
-
public @TransitionType int getType() {
return mType;
}
@@ -360,16 +340,6 @@
}
/**
- * @deprecated Use {@link Change#getAnimationOptions()} instead. This method is called only
- * if {@link Flags#FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE} is disabled.
- */
- @Deprecated
- @Nullable
- public AnimationOptions getAnimationOptions() {
- return mOptions;
- }
-
- /**
* @return the list of {@link Change}s in this transition. The list is sorted top-to-bottom
* in Z (meaning index 0 is the top-most container).
*/
@@ -455,9 +425,6 @@
StringBuilder sb = new StringBuilder();
sb.append("{id=").append(mDebugId).append(" t=").append(transitTypeToString(mType))
.append(" f=0x").append(Integer.toHexString(mFlags)).append(" trk=").append(mTrack);
- if (mOptions != null) {
- sb.append(" opt=").append(mOptions);
- }
sb.append(" r=[");
for (int i = 0; i < mRoots.size(); ++i) {
if (i > 0) {
@@ -656,8 +623,6 @@
for (int i = 0; i < mRoots.size(); ++i) {
out.mRoots.add(mRoots.get(i).localRemoteCopy());
}
- // Doesn't have any native stuff, so no need for actual copy
- out.mOptions = mOptions;
return out;
}
@@ -860,9 +825,6 @@
* Sets {@link AnimationOptions} to override animation.
*/
public void setAnimationOptions(@Nullable AnimationOptions options) {
- if (!Flags.moveAnimationOptionsToChange()) {
- return;
- }
mAnimationOptions = options;
}
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 3e3b8e1..2e36e9a 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -206,6 +206,13 @@
}
flag {
+ name: "enable_camera_compat_for_desktop_windowing_opt_out_api"
+ namespace: "lse_desktop_experience"
+ description: "Introduces a developer API to opt-out of Camera Compat treatment for fixed-orientation apps in desktop windowing mode"
+ bug: "397165621"
+}
+
+flag {
name: "enable_task_stack_observer_in_shell"
namespace: "lse_desktop_experience"
description: "Introduces a new observer in shell to track the task stack."
@@ -681,6 +688,16 @@
}
flag {
+ name: "enable_opaque_background_for_transparent_windows"
+ namespace: "lse_desktop_experience"
+ description: "Whether an opaque background should be forcefully set for windows with only transparent background."
+ bug: "397219542"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "enable_desktop_mode_through_dev_option"
namespace: "lse_desktop_experience"
description: "Enables support for desktop mode through developer options for devices eligible for desktop mode."
@@ -705,6 +722,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 +788,27 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "enable_taskbar_overflow"
+ namespace: "lse_desktop_experience"
+ description: "Show recent apps in the taskbar overflow."
+ bug: "368119679"
+}
+
+flag {
+ name: "enable_projected_display_desktop_mode"
+ namespace: "lse_desktop_experience"
+ description: "Enable Desktop Mode on Projected Mode devices but constrained to the external display."
+ bug: "384568161"
+}
+
+flag {
+ name: "enable_persisting_density_scale_for_connected_displays"
+ namespace: "lse_desktop_experience"
+ description: "Enables persisting density scale on resolution change for connected displays."
+ bug: "392855657"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 54d0eef..6e45d3d 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -62,16 +62,6 @@
flag {
namespace: "windowing_sdk"
- name: "move_animation_options_to_change"
- description: "Move AnimationOptions from TransitionInfo to each Change"
- bug: "327332488"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- namespace: "windowing_sdk"
name: "rear_display_disable_force_desktop_system_decorations"
description: "Block system decorations from being added to a rear display when desktop mode is forced"
bug: "346103150"
diff --git a/core/java/com/android/internal/app/MediaRouteControllerContentManager.java b/core/java/com/android/internal/app/MediaRouteControllerContentManager.java
index dc4caa3..3a8b94f 100644
--- a/core/java/com/android/internal/app/MediaRouteControllerContentManager.java
+++ b/core/java/com/android/internal/app/MediaRouteControllerContentManager.java
@@ -17,7 +17,12 @@
package com.android.internal.app;
import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.AnimationDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.StateListDrawable;
import android.media.MediaRouter;
+import android.util.TypedValue;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.SeekBar;
@@ -35,9 +40,14 @@
*/
public interface Delegate {
/**
- * Updates the title of the cast device
+ * Updates the title of the media route device
*/
- void setCastDeviceTitle(CharSequence title);
+ void setMediaRouteDeviceTitle(CharSequence title);
+
+ /**
+ * Updates the icon of the media route device
+ */
+ void setMediaRouteDeviceIcon(Drawable icon);
/**
* Dismiss the UI to transition to a different workflow.
@@ -45,6 +55,7 @@
void dismissView();
}
+ private final Context mContext;
private final Delegate mDelegate;
// Time to wait before updating the volume when the user lets go of the seek bar
@@ -53,15 +64,25 @@
private static final int VOLUME_UPDATE_DELAY_MILLIS = 250;
private final MediaRouter mRouter;
+ private final MediaRouteControllerContentManager.MediaRouterCallback mCallback;
private final MediaRouter.RouteInfo mRoute;
+ private Drawable mMediaRouteButtonDrawable;
+ private final int[] mMediaRouteConnectingState = { R.attr.state_checked, R.attr.state_enabled };
+ private final int[] mMediaRouteOnState = { R.attr.state_activated, R.attr.state_enabled };
+ private Drawable mCurrentIconDrawable;
+
+ private boolean mAttachedToWindow;
+
private LinearLayout mVolumeLayout;
private SeekBar mVolumeSlider;
private boolean mVolumeSliderTouched;
public MediaRouteControllerContentManager(Context context, Delegate delegate) {
+ mContext = context;
mDelegate = delegate;
mRouter = context.getSystemService(MediaRouter.class);
+ mCallback = new MediaRouteControllerContentManager.MediaRouterCallback();
mRoute = mRouter.getSelectedRoute();
}
@@ -70,7 +91,7 @@
* given container view.
*/
public void bindViews(View containerView) {
- mDelegate.setCastDeviceTitle(mRoute.getName());
+ mDelegate.setMediaRouteDeviceTitle(mRoute.getName());
mVolumeLayout = containerView.findViewById(R.id.media_route_volume_layout);
mVolumeSlider = containerView.findViewById(R.id.media_route_volume_slider);
mVolumeSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@@ -108,20 +129,58 @@
}
}
});
+
+ mMediaRouteButtonDrawable = obtainMediaRouteButtonDrawable();
+ }
+
+ /**
+ * Called when this UI is attached to a window..
+ */
+ public void onAttachedToWindow() {
+ mAttachedToWindow = true;
+ mRouter.addCallback(0, mCallback, MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS);
+ update();
+ }
+
+ /**
+ * Called when this UI is detached from a window..
+ */
+ public void onDetachedFromWindow() {
+ mRouter.removeCallback(mCallback);
+ mAttachedToWindow = false;
}
/**
* Updates all the views to reflect new states.
*/
public void update() {
- mDelegate.setCastDeviceTitle(mRoute.getName());
+ if (!mRoute.isSelected() || mRoute.isDefault()) {
+ mDelegate.dismissView();
+ }
+
+ mDelegate.setMediaRouteDeviceTitle(mRoute.getName());
updateVolume();
+
+ Drawable icon = getIconDrawable();
+ if (icon != mCurrentIconDrawable) {
+ mCurrentIconDrawable = icon;
+ if (icon instanceof AnimationDrawable animDrawable) {
+ if (!mAttachedToWindow && !mRoute.isConnecting()) {
+ // When the route is already connected before the view is attached, show the
+ // last frame of the connected animation immediately.
+ if (animDrawable.isRunning()) {
+ animDrawable.stop();
+ }
+ icon = animDrawable.getFrame(animDrawable.getNumberOfFrames() - 1);
+ } else if (!animDrawable.isRunning()) {
+ animDrawable.start();
+ }
+ }
+ mDelegate.setMediaRouteDeviceIcon(icon);
+ }
}
- /**
- * Updates the volume layout and slider.
- */
- public void updateVolume() {
+ private void updateVolume() {
if (!mVolumeSliderTouched) {
if (isVolumeControlAvailable()) {
mVolumeLayout.setVisibility(View.VISIBLE);
@@ -150,4 +209,61 @@
private boolean isVolumeControlAvailable() {
return mRoute.getVolumeHandling() == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE;
}
+
+ private Drawable obtainMediaRouteButtonDrawable() {
+ TypedValue value = new TypedValue();
+ if (!mContext.getTheme().resolveAttribute(R.attr.mediaRouteButtonStyle, value, true)) {
+ return null;
+ }
+ int[] drawableAttrs = new int[] { R.attr.externalRouteEnabledDrawable };
+ TypedArray a = mContext.obtainStyledAttributes(value.data, drawableAttrs);
+ Drawable drawable = a.getDrawable(0);
+ a.recycle();
+ return drawable;
+ }
+
+ private Drawable getIconDrawable() {
+ if (!(mMediaRouteButtonDrawable instanceof StateListDrawable)) {
+ return mMediaRouteButtonDrawable;
+ } else if (mRoute.isConnecting()) {
+ StateListDrawable stateListDrawable = (StateListDrawable) mMediaRouteButtonDrawable;
+ stateListDrawable.setState(mMediaRouteConnectingState);
+ return stateListDrawable.getCurrent();
+ } else {
+ StateListDrawable stateListDrawable = (StateListDrawable) mMediaRouteButtonDrawable;
+ stateListDrawable.setState(mMediaRouteOnState);
+ return stateListDrawable.getCurrent();
+ }
+ }
+
+ private final class MediaRouterCallback extends MediaRouter.SimpleCallback {
+ @Override
+ public void onRouteUnselected(MediaRouter router, int type, MediaRouter.RouteInfo info) {
+ update();
+ }
+
+ @Override
+ public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo route) {
+ update();
+ }
+
+ @Override
+ public void onRouteVolumeChanged(MediaRouter router, MediaRouter.RouteInfo route) {
+ if (route == mRoute) {
+ updateVolume();
+ }
+ }
+
+ @Override
+ public void onRouteGrouped(MediaRouter router, MediaRouter.RouteInfo info,
+ MediaRouter.RouteGroup group, int index) {
+ update();
+ }
+
+ @Override
+ public void onRouteUngrouped(MediaRouter router, MediaRouter.RouteInfo info,
+ MediaRouter.RouteGroup group) {
+ update();
+ }
+ }
}
diff --git a/core/java/com/android/internal/app/MediaRouteControllerDialog.java b/core/java/com/android/internal/app/MediaRouteControllerDialog.java
index 45a4a13..5899963 100644
--- a/core/java/com/android/internal/app/MediaRouteControllerDialog.java
+++ b/core/java/com/android/internal/app/MediaRouteControllerDialog.java
@@ -21,15 +21,9 @@
import android.app.MediaRouteButton;
import android.content.Context;
import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.StateListDrawable;
import android.media.MediaRouter;
-import android.media.MediaRouter.RouteGroup;
-import android.media.MediaRouter.RouteInfo;
import android.os.Bundle;
-import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.View;
@@ -48,19 +42,11 @@
*/
public class MediaRouteControllerDialog extends AlertDialog implements
MediaRouteControllerContentManager.Delegate {
- // TODO(b/360050020): Eventually these 3 variables should be in the content manager instead of
+ // TODO(b/360050020): Eventually these 2 variables should be in the content manager instead of
// here. So these should be removed when the migration is completed.
private final MediaRouter mRouter;
- private final MediaRouterCallback mCallback;
private final MediaRouter.RouteInfo mRoute;
- private Drawable mMediaRouteButtonDrawable;
- private int[] mMediaRouteConnectingState = { R.attr.state_checked, R.attr.state_enabled };
- private int[] mMediaRouteOnState = { R.attr.state_activated, R.attr.state_enabled };
- private Drawable mCurrentIconDrawable;
-
- private boolean mAttachedToWindow;
-
private final MediaRouteControllerContentManager mContentManager;
public MediaRouteControllerDialog(Context context, int theme) {
@@ -68,7 +54,6 @@
mContentManager = new MediaRouteControllerContentManager(context, this);
mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
- mCallback = new MediaRouterCallback();
mRoute = mRouter.getSelectedRoute();
}
@@ -87,24 +72,18 @@
customPanelView.setMinimumHeight(0);
}
- mMediaRouteButtonDrawable = obtainMediaRouteButtonDrawable();
- update();
+ mContentManager.update();
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
- mAttachedToWindow = true;
-
- mRouter.addCallback(0, mCallback, MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS);
- update();
+ mContentManager.onAttachedToWindow();
}
@Override
public void onDetachedFromWindow() {
- mRouter.removeCallback(mCallback);
- mAttachedToWindow = false;
-
+ mContentManager.onDetachedFromWindow();
super.onDetachedFromWindow();
}
@@ -128,95 +107,17 @@
}
@Override
- public void setCastDeviceTitle(CharSequence title) {
+ public void setMediaRouteDeviceTitle(CharSequence title) {
setTitle(title);
}
@Override
+ public void setMediaRouteDeviceIcon(Drawable icon) {
+ setIcon(icon);
+ }
+
+ @Override
public void dismissView() {
dismiss();
}
-
- private void update() {
- if (!mRoute.isSelected() || mRoute.isDefault()) {
- dismissView();
- }
-
- mContentManager.update();
-
- Drawable icon = getIconDrawable();
- if (icon != mCurrentIconDrawable) {
- mCurrentIconDrawable = icon;
- if (icon instanceof AnimationDrawable animDrawable) {
- if (!mAttachedToWindow && !mRoute.isConnecting()) {
- // When the route is already connected before the view is attached, show the
- // last frame of the connected animation immediately.
- if (animDrawable.isRunning()) {
- animDrawable.stop();
- }
- icon = animDrawable.getFrame(animDrawable.getNumberOfFrames() - 1);
- } else if (!animDrawable.isRunning()) {
- animDrawable.start();
- }
- }
- setIcon(icon);
- }
- }
-
- private Drawable obtainMediaRouteButtonDrawable() {
- Context context = getContext();
- TypedValue value = new TypedValue();
- if (!context.getTheme().resolveAttribute(R.attr.mediaRouteButtonStyle, value, true)) {
- return null;
- }
- int[] drawableAttrs = new int[] { R.attr.externalRouteEnabledDrawable };
- TypedArray a = context.obtainStyledAttributes(value.data, drawableAttrs);
- Drawable drawable = a.getDrawable(0);
- a.recycle();
- return drawable;
- }
-
- private Drawable getIconDrawable() {
- if (!(mMediaRouteButtonDrawable instanceof StateListDrawable)) {
- return mMediaRouteButtonDrawable;
- } else if (mRoute.isConnecting()) {
- StateListDrawable stateListDrawable = (StateListDrawable) mMediaRouteButtonDrawable;
- stateListDrawable.setState(mMediaRouteConnectingState);
- return stateListDrawable.getCurrent();
- } else {
- StateListDrawable stateListDrawable = (StateListDrawable) mMediaRouteButtonDrawable;
- stateListDrawable.setState(mMediaRouteOnState);
- return stateListDrawable.getCurrent();
- }
- }
-
- private final class MediaRouterCallback extends MediaRouter.SimpleCallback {
- @Override
- public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
- update();
- }
-
- @Override
- public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo route) {
- update();
- }
-
- @Override
- public void onRouteVolumeChanged(MediaRouter router, MediaRouter.RouteInfo route) {
- if (route == mRoute) {
- mContentManager.updateVolume();
- }
- }
-
- @Override
- public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
- int index) {
- update();
- }
-
- @Override
- public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
- update();
- }
- }
}
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 454323b..a0f45b0 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -257,15 +257,11 @@
@Override
public void surfaceDestroyed() {
-
- // Wait a while to give the system a chance for the remaining
- // frames to arrive, then force finish the session.
- mHandler.postDelayed(() -> {
+ mHandler.post(() -> {
if (!mMetricsFinalized) {
end(REASON_END_SURFACE_DESTROYED);
- finish();
}
- }, 50);
+ });
}
};
// This callback has a reference to FrameTracker,
@@ -367,9 +363,9 @@
// Send a flush jank data transaction.
if (mSurfaceControl != null && mSurfaceControl.isValid()) {
SurfaceControl.Transaction.sendSurfaceFlushJankData(mSurfaceControl);
- if (mJankDataListenerRegistration != null) {
- mJankDataListenerRegistration.flush();
- }
+ }
+ if (mJankDataListenerRegistration != null) {
+ mJankDataListenerRegistration.flush();
}
long delay;
@@ -650,6 +646,8 @@
Log.w(TAG, "Missing SF jank callback for vsyncId: " + info.frameVsyncId
+ ", CUJ=" + name);
}
+ } else if (Flags.useSfFrameDuration() && info.surfaceControlCallbackFired) {
+ maxFrameTimeNanos = Math.max(info.totalDurationNanos, maxFrameTimeNanos);
}
}
maxSuccessiveMissedFramesCount = Math.max(
diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
index fe616e0..5e9c87a 100644
--- a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
+++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
@@ -16,6 +16,7 @@
package com.android.internal.os;
+import android.annotation.CheckResult;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -196,7 +197,8 @@
/**
* Populates the array with the accumulated counts for the specified state.
*/
- public void getCounts(long[] counts, int state) {
+ @CheckResult
+ public boolean getCounts(long[] counts, int state) {
if (state < 0 || state >= mStateCount) {
throw new IllegalArgumentException(
"State: " + state + ", outside the range: [0-" + mStateCount + "]");
@@ -205,7 +207,7 @@
throw new IllegalArgumentException(
"Invalid array length: " + counts.length + ", expected: " + mLength);
}
- native_getCounts(mNativeObject, counts, state);
+ return native_getCounts(mNativeObject, counts, state);
}
@Override
@@ -282,7 +284,8 @@
@FastNative
@RavenwoodRedirect
- private static native void native_getCounts(long nativeObject, long[] counts, int state);
+ @CheckResult
+ private static native boolean native_getCounts(long nativeObject, long[] counts, int state);
@FastNative
@RavenwoodRedirect
diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter_ravenwood.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter_ravenwood.java
index 4f5f37d..403e8c1 100644
--- a/core/java/com/android/internal/os/LongArrayMultiStateCounter_ravenwood.java
+++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter_ravenwood.java
@@ -168,8 +168,20 @@
}
}
- public void getValues(long[] values, int state) {
- System.arraycopy(mStates[state].mCounter, 0, values, 0, mArrayLength);
+ public boolean getValues(long[] values, int state) {
+ long[] counts = mStates[state].mCounter;
+ boolean allZeros = true;
+ for (int i = 0; i < counts.length; i++) {
+ if (counts[i] != 0) {
+ allZeros = false;
+ break;
+ }
+ }
+ if (allZeros) {
+ return false;
+ }
+ System.arraycopy(counts, 0, values, 0, mArrayLength);
+ return true;
}
public void reset() {
@@ -316,8 +328,8 @@
getInstance(instanceId).addCounts(counts);
}
- public static void native_getCounts(long instanceId, long[] counts, int state) {
- getInstance(instanceId).getValues(counts, state);
+ public static boolean native_getCounts(long instanceId, long[] counts, int state) {
+ return getInstance(instanceId).getValues(counts, state);
}
public static void native_reset(long instanceId) {
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 641ecc9..ce46da1 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -250,7 +250,8 @@
mPeopleHelper.animateViewForceHidden(mImportanceRingView, forceHidden);
mPeopleHelper.animateViewForceHidden(mIcon, forceHidden);
});
- mConversationText = findViewById(R.id.conversation_text);
+ mConversationText = findViewById(notificationsRedesignTemplates()
+ ? R.id.title : R.id.conversation_text);
mExpandButtonContainer = findViewById(R.id.expand_button_container);
mExpandButtonContainerA11yContainer =
findViewById(R.id.expand_button_a11y_container);
@@ -716,17 +717,10 @@
}
private void updateImageMessages() {
- View newMessage = null;
- if (mIsCollapsed && !mGroups.isEmpty()) {
-
- // When collapsed, we're displaying the image message in a dedicated container
- // on the right of the layout instead of inline. Let's add the isolated image there
- MessagingGroup messagingGroup = mGroups.getLast();
- MessagingImageMessage isolatedMessage = messagingGroup.getIsolatedMessage();
- if (isolatedMessage != null) {
- newMessage = isolatedMessage.getView();
- }
+ if (mImageMessageContainer == null) {
+ return;
}
+ View newMessage = getNewImageMessage();
// Remove all messages that don't belong into the image layout
View previousMessage = mImageMessageContainer.getChildAt(0);
if (previousMessage != newMessage) {
@@ -738,6 +732,20 @@
mImageMessageContainer.setVisibility(newMessage != null ? VISIBLE : GONE);
}
+ @Nullable
+ private View getNewImageMessage() {
+ if (mIsCollapsed && !mGroups.isEmpty()) {
+ // When collapsed, we're displaying the image message in a dedicated container
+ // on the right of the layout instead of inline. Let's add the isolated image there
+ MessagingGroup messagingGroup = mGroups.getLast();
+ MessagingImageMessage isolatedMessage = messagingGroup.getIsolatedMessage();
+ if (isolatedMessage != null) {
+ return isolatedMessage.getView();
+ }
+ }
+ return null;
+ }
+
public void bindFacePile(ImageView bottomBackground, ImageView bottomView, ImageView topView) {
applyNotificationBackgroundColor(bottomBackground);
// Let's find the two last conversations:
@@ -841,6 +849,10 @@
}
private void updateAppName() {
+ if (notificationsRedesignTemplates()) {
+ return;
+ }
+
mAppName.setVisibility(mIsCollapsed ? GONE : VISIBLE);
}
@@ -1533,6 +1545,10 @@
}
private void updateExpandButton() {
+ if (notificationsRedesignTemplates()) {
+ return;
+ }
+
int buttonGravity;
ViewGroup newContainer;
if (mIsCollapsed) {
@@ -1565,6 +1581,10 @@
}
private void updateContentEndPaddings() {
+ if (notificationsRedesignTemplates()) {
+ return;
+ }
+
// Let's make sure the conversation header can't run into the expand button when we're
// collapsed and update the paddings of the content
int headerPaddingEnd;
@@ -1593,6 +1613,10 @@
}
private void onAppNameVisibilityChanged() {
+ if (notificationsRedesignTemplates()) {
+ return;
+ }
+
boolean appNameGone = mAppName.getVisibility() == GONE;
if (appNameGone != mAppNameGone) {
mAppNameGone = appNameGone;
@@ -1601,10 +1625,18 @@
}
private void updateAppNameDividerVisibility() {
+ if (notificationsRedesignTemplates()) {
+ return;
+ }
+
mAppNameDivider.setVisibility(mAppNameGone ? GONE : VISIBLE);
}
public void updateExpandability(boolean expandable, @Nullable OnClickListener onClickListener) {
+ if (notificationsRedesignTemplates()) {
+ return;
+ }
+
mExpandable = expandable;
if (expandable) {
mExpandButtonContainer.setVisibility(VISIBLE);
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index 31d9770..b9a603cc 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -449,12 +449,8 @@
}
private void updateIconVisibility() {
- if (Flags.notificationsRedesignTemplates() && !mIsInConversation) {
- // We don't show any icon (other than the app icon) in the collapsed form. For
- // conversations, keeping this container helps with aligning the message to the icon
- // when collapsed, but the old messaging style already has this alignment built into
- // the template like all other layouts. Conversations are special because we use the
- // same base layout for both the collapsed and expanded views.
+ if (Flags.notificationsRedesignTemplates()) {
+ // We don't show any icon (other than the app or person icon) in the collapsed form.
mMessagingIconContainer.setVisibility(mSingleLine ? GONE : VISIBLE);
}
}
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index 64b44df..eb22e7c 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -337,19 +337,10 @@
}
private void updateImageMessages() {
- View newMessage = null;
if (mImageMessageContainer == null) {
return;
}
- if (mIsCollapsed && !mGroups.isEmpty()) {
- // When collapsed, we're displaying the image message in a dedicated container
- // on the right of the layout instead of inline. Let's add the isolated image there
- MessagingGroup messagingGroup = mGroups.getLast();
- MessagingImageMessage isolatedMessage = messagingGroup.getIsolatedMessage();
- if (isolatedMessage != null) {
- newMessage = isolatedMessage.getView();
- }
- }
+ View newMessage = getNewImageMessage();
// Remove all messages that don't belong into the image layout
View previousMessage = mImageMessageContainer.getChildAt(0);
if (previousMessage != newMessage) {
@@ -368,6 +359,20 @@
}
}
+ @Nullable
+ private View getNewImageMessage() {
+ if (mIsCollapsed && !mGroups.isEmpty()) {
+ // When collapsed, we're displaying the image message in a dedicated container
+ // on the right of the layout instead of inline. Let's add the isolated image there
+ MessagingGroup messagingGroup = mGroups.getLast();
+ MessagingImageMessage isolatedMessage = messagingGroup.getIsolatedMessage();
+ if (isolatedMessage != null) {
+ return isolatedMessage.getView();
+ }
+ }
+ return null;
+ }
+
private void removeGroups(ArrayList<MessagingGroup> oldGroups) {
int size = oldGroups.size();
for (int i = 0; i < size; i++) {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 06702e2..92a841f 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -76,6 +76,9 @@
srcs: [
"android_animation_PropertyValuesHolder.cpp",
"android_content_res_ApkAssets.cpp",
+ "android_media_ImageReader.cpp",
+ "android_media_PublicFormatUtils.cpp",
+ "android_media_Utils.cpp",
"android_os_SystemClock.cpp",
"android_os_SystemProperties.cpp",
"android_text_AndroidCharacter.cpp",
@@ -134,10 +137,7 @@
"android_app_ActivityThread.cpp",
"android_app_NativeActivity.cpp",
"android_app_admin_SecurityLog.cpp",
- "android_media_ImageReader.cpp",
"android_media_ImageWriter.cpp",
- "android_media_PublicFormatUtils.cpp",
- "android_media_Utils.cpp",
"android_opengl_EGL14.cpp",
"android_opengl_EGL15.cpp",
"android_opengl_EGLExt.cpp",
@@ -489,6 +489,7 @@
"libsqlite",
"libgui_window_info_static",
"libbinder",
+ "libbinder_ndk",
"libhidlbase", // libhwbinder is in here
],
version_script: "platform/linux/libandroid_runtime_export.txt",
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index a526783..1a7490e6 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -26,6 +26,7 @@
#include <nativehelper/JNIHelp.h>
#include <utils/Log.h>
#include <utils/RefBase.h>
+#include <utils/StrongPointer.h>
#include "core_jni_helpers.h"
@@ -124,7 +125,7 @@
static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName,
jboolean updateDestinationFrame) {
ScopedUtfChars name(env, jName);
- sp<BLASTBufferQueue> queue = new BLASTBufferQueue(name.c_str(), updateDestinationFrame);
+ sp<BLASTBufferQueue> queue = sp<BLASTBufferQueue>::make(name.c_str(), updateDestinationFrame);
queue->incStrong((void*)nativeCreate);
return reinterpret_cast<jlong>(queue.get());
}
diff --git a/core/jni/android_media_ImageReader.cpp b/core/jni/android_media_ImageReader.cpp
index 20b9c57..a34cb39 100644
--- a/core/jni/android_media_ImageReader.cpp
+++ b/core/jni/android_media_ImageReader.cpp
@@ -25,13 +25,22 @@
#include <android_runtime/android_view_Surface.h>
#include <com_android_graphics_libgui_flags.h>
#include <cutils/atomic.h>
+#ifdef __ANDROID__
#include <grallocusage/GrallocUsageConversion.h>
+#else
+#define GRALLOC_USAGE_PROTECTED 0
+#define GRALLOC_USAGE_SW_READ_OFTEN 0
+#define GRALLOC_USAGE_SW_WRITE_OFTEN 0
+#endif
#include <gui/BufferItemConsumer.h>
#include <gui/Surface.h>
#include <inttypes.h>
#include <jni.h>
+#include <jni_wrappers.h>
#include <nativehelper/JNIHelp.h>
+#ifdef __ANDROID__
#include <private/android/AHardwareBufferHelpers.h>
+#endif
#include <stdint.h>
#include <ui/Rect.h>
#include <utils/List.h>
@@ -393,8 +402,12 @@
String8 consumerName = String8::format("ImageReader-%dx%df%xm%d-%d-%d",
width, height, nativeHalFormat, maxImages, getpid(),
createProcessUniqueId());
+#ifdef __ANDROID__
uint64_t consumerUsage =
android_hardware_HardwareBuffer_convertToGrallocUsageBits(ndkUsage);
+#else
+ uint64_t consumerUsage = 0;
+#endif
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
sp<BufferItemConsumer> bufferConsumer = new BufferItemConsumer(consumerUsage, maxImages,
@@ -773,6 +786,7 @@
return true;
}
+#ifdef __ANDROID__
static void ImageReader_unlockGraphicBuffer(JNIEnv* env, jobject /*thiz*/,
jobject buffer) {
sp<GraphicBuffer> graphicBuffer =
@@ -856,6 +870,7 @@
return imagePlanes;
}
+#endif
static jobjectArray Image_createSurfacePlanes(JNIEnv* env, jobject thiz,
int numPlanes, int halReaderFormat, uint64_t ndkReaderUsage)
@@ -964,6 +979,7 @@
}
}
+#ifdef __ANDROID__
static jobject Image_getHardwareBuffer(JNIEnv* env, jobject thiz) {
BufferItem* buffer = Image_getBufferItem(env, thiz);
if (buffer == nullptr) {
@@ -976,45 +992,48 @@
// to link against libandroid.so
return android_hardware_HardwareBuffer_createFromAHardwareBuffer(env, b);
}
+#endif
} // extern "C"
// ----------------------------------------------------------------------------
-static const JNINativeMethod gImageReaderMethods[] = {
- {"nativeClassInit", "()V", (void*)ImageReader_classInit },
- {"nativeInit", "(Ljava/lang/Object;IIIJII)V", (void*)ImageReader_init },
- {"nativeClose", "()V", (void*)ImageReader_close },
- {"nativeReleaseImage", "(Landroid/media/Image;)V", (void*)ImageReader_imageRelease },
- {"nativeImageSetup", "(Landroid/media/Image;)I", (void*)ImageReader_imageSetup },
- {"nativeGetSurface", "()Landroid/view/Surface;", (void*)ImageReader_getSurface },
- {"nativeDetachImage", "(Landroid/media/Image;Z)I", (void*)ImageReader_detachImage },
- {"nativeCreateImagePlanes",
- "(ILandroid/graphics/GraphicBuffer;IIIIII)[Landroid/media/ImageReader$ImagePlane;",
- (void*)ImageReader_createImagePlanes },
- {"nativeUnlockGraphicBuffer",
- "(Landroid/graphics/GraphicBuffer;)V", (void*)ImageReader_unlockGraphicBuffer },
- {"nativeDiscardFreeBuffers", "()V", (void*)ImageReader_discardFreeBuffers }
-};
+static const JNINativeMethod gImageReaderMethods[] =
+ {{"nativeClassInit", "()V", (void*)ImageReader_classInit},
+ {"nativeInit", "(Ljava/lang/Object;IIIJII)V", (void*)ImageReader_init},
+ {"nativeClose", "()V", (void*)ImageReader_close},
+ {"nativeReleaseImage", "(Landroid/media/Image;)V", (void*)ImageReader_imageRelease},
+ {"nativeImageSetup", "(Landroid/media/Image;)I", (void*)ImageReader_imageSetup},
+ {"nativeGetSurface", "()Landroid/view/Surface;", (void*)ImageReader_getSurface},
+ {"nativeDetachImage", "(Landroid/media/Image;Z)I", (void*)ImageReader_detachImage},
+#ifdef __ANDROID__
+ {"nativeCreateImagePlanes",
+ "(ILandroid/graphics/GraphicBuffer;IIIIII)[Landroid/media/ImageReader$ImagePlane;",
+ (void*)ImageReader_createImagePlanes},
+ {"nativeUnlockGraphicBuffer", "(Landroid/graphics/GraphicBuffer;)V",
+ (void*)ImageReader_unlockGraphicBuffer},
+#endif
+ {"nativeDiscardFreeBuffers", "()V", (void*)ImageReader_discardFreeBuffers}};
static const JNINativeMethod gImageMethods[] = {
- {"nativeCreatePlanes", "(IIJ)[Landroid/media/ImageReader$SurfaceImage$SurfacePlane;",
- (void*)Image_createSurfacePlanes },
- {"nativeGetWidth", "()I", (void*)Image_getWidth },
- {"nativeGetHeight", "()I", (void*)Image_getHeight },
- {"nativeGetFormat", "(I)I", (void*)Image_getFormat },
- {"nativeGetFenceFd", "()I", (void*)Image_getFenceFd },
- {"nativeGetHardwareBuffer", "()Landroid/hardware/HardwareBuffer;",
- (void*)Image_getHardwareBuffer },
+ {"nativeCreatePlanes", "(IIJ)[Landroid/media/ImageReader$SurfaceImage$SurfacePlane;",
+ (void*)Image_createSurfacePlanes},
+ {"nativeGetWidth", "()I", (void*)Image_getWidth},
+ {"nativeGetHeight", "()I", (void*)Image_getHeight},
+ {"nativeGetFormat", "(I)I", (void*)Image_getFormat},
+ {"nativeGetFenceFd", "()I", (void*)Image_getFenceFd},
+#ifdef __ANDROID__
+ {"nativeGetHardwareBuffer", "()Landroid/hardware/HardwareBuffer;",
+ (void*)Image_getHardwareBuffer},
+#endif
};
int register_android_media_ImageReader(JNIEnv *env) {
+ int ret1 = RegisterMethodsOrDie(env, "android/media/ImageReader", gImageReaderMethods,
+ NELEM(gImageReaderMethods));
- int ret1 = AndroidRuntime::registerNativeMethods(env,
- "android/media/ImageReader", gImageReaderMethods, NELEM(gImageReaderMethods));
-
- int ret2 = AndroidRuntime::registerNativeMethods(env,
- "android/media/ImageReader$SurfaceImage", gImageMethods, NELEM(gImageMethods));
+ int ret2 = RegisterMethodsOrDie(env, "android/media/ImageReader$SurfaceImage", gImageMethods,
+ NELEM(gImageMethods));
return (ret1 || ret2);
}
diff --git a/core/jni/android_media_PublicFormatUtils.cpp b/core/jni/android_media_PublicFormatUtils.cpp
index 04494ad..bcdf654 100644
--- a/core/jni/android_media_PublicFormatUtils.cpp
+++ b/core/jni/android_media_PublicFormatUtils.cpp
@@ -16,10 +16,10 @@
#define LOG_TAG "PublicFormatUtils_JNI"
-#include <utils/misc.h>
-#include <ui/PublicFormat.h>
-#include <android_runtime/AndroidRuntime.h>
#include <jni.h>
+#include <jni_wrappers.h>
+#include <ui/PublicFormat.h>
+#include <utils/misc.h>
using namespace android;
@@ -53,7 +53,6 @@
};
int register_android_media_PublicFormatUtils(JNIEnv *env) {
- return AndroidRuntime::registerNativeMethods(env,
- "android/media/PublicFormatUtils", gMethods, NELEM(gMethods));
+ return RegisterMethodsOrDie(env, "android/media/PublicFormatUtils", gMethods, NELEM(gMethods));
}
diff --git a/core/jni/android_media_Utils.cpp b/core/jni/android_media_Utils.cpp
index e8f8644..e484141 100644
--- a/core/jni/android_media_Utils.cpp
+++ b/core/jni/android_media_Utils.cpp
@@ -19,10 +19,12 @@
#include "android_media_Utils.h"
+#ifdef __ANDROID__ // Layoutlib does not support hardware
#include <aidl/android/hardware/graphics/common/PixelFormat.h>
#include <aidl/android/hardware/graphics/common/PlaneLayoutComponentType.h>
#include <ui/GraphicBufferMapper.h>
#include <ui/GraphicTypes.h>
+#endif
#include <utils/Log.h>
#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
@@ -34,7 +36,13 @@
// -----------Utility functions used by ImageReader/Writer JNI-----------------
+#ifdef __ANDROID__
using AidlPixelFormat = aidl::android::hardware::graphics::common::PixelFormat;
+#else
+namespace AidlPixelFormat {
+const int32_t YCBCR_P210 = 60;
+}
+#endif
enum {
IMAGE_MAX_NUM_PLANES = 3,
@@ -517,6 +525,7 @@
return OK;
}
+#ifdef __ANDROID__
static status_t extractP010Gralloc4PlaneLayout(
sp<GraphicBuffer> buffer, void *pData, int format, LockedImage *outputImage) {
using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
@@ -663,6 +672,7 @@
outputImage->chromaStep = 4;
return OK;
}
+#endif
status_t lockImageFromBuffer(sp<GraphicBuffer> buffer, uint32_t inUsage,
const Rect& rect, int fenceFd, LockedImage* outputImage) {
@@ -701,6 +711,7 @@
ALOGE("Lock buffer failed!");
return res;
}
+#ifdef __ANDROID__
if (isPossibly10BitYUV(format)) {
if (format == HAL_PIXEL_FORMAT_YCBCR_P010 &&
OK == extractP010Gralloc4PlaneLayout(buffer, pData, format, outputImage)) {
@@ -713,6 +724,7 @@
return OK;
}
}
+#endif
}
outputImage->data = reinterpret_cast<uint8_t*>(pData);
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/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
index 8f36ecb..7d94ef8 100644
--- a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
+++ b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
@@ -116,17 +116,26 @@
counter->reset();
}
-static void native_getCounts(JNIEnv *env, jclass, jlong nativePtr, jlongArray values, jint state) {
+static bool native_getCounts(JNIEnv *env, jclass, jlong nativePtr, jlongArray values, jint state) {
auto *counter = reinterpret_cast<LongArrayMultiStateCounter *>(nativePtr);
- ScopedLongArrayRW scopedArray(env, values);
auto *data = counter->getCount(state).data();
- auto size = env->GetArrayLength(values);
- auto *outData = scopedArray.get();
if (data == nullptr) {
- memset(outData, 0, size * sizeof(uint64_t));
- } else {
- memcpy(outData, data, size * sizeof(uint64_t));
+ return false;
}
+ auto size = env->GetArrayLength(values);
+ bool allZeros = true;
+ for (int i = 0; i < size; i++) {
+ if (data[i]) {
+ allZeros = false;
+ break;
+ }
+ }
+ if (allZeros) {
+ return false;
+ }
+ ScopedLongArrayRW scopedArray(env, values);
+ memcpy(scopedArray.get(), data, size * sizeof(uint64_t));
+ return true;
}
static jobject native_toString(JNIEnv *env, jclass, jlong nativePtr) {
@@ -255,7 +264,7 @@
// @CriticalNative
{"native_reset", "(J)V", (void *)native_reset},
// @FastNative
- {"native_getCounts", "(J[JI)V", (void *)native_getCounts},
+ {"native_getCounts", "(J[JI)Z", (void *)native_getCounts},
// @FastNative
{"native_toString", "(J)Ljava/lang/String;", (void *)native_toString},
// @FastNative
diff --git a/core/jni/jni_wrappers.h b/core/jni/jni_wrappers.h
index e3e17ee..1f44994 100644
--- a/core/jni/jni_wrappers.h
+++ b/core/jni/jni_wrappers.h
@@ -22,6 +22,8 @@
#include <log/log.h>
#include <nativehelper/JNIHelp.h>
+#include <string>
+
namespace android {
static inline jclass FindClassOrDie(JNIEnv* env, const char* class_name) {
diff --git a/core/jni/platform/host/HostRuntime.cpp b/core/jni/platform/host/HostRuntime.cpp
index 1a03283..746740b 100644
--- a/core/jni/platform/host/HostRuntime.cpp
+++ b/core/jni/platform/host/HostRuntime.cpp
@@ -48,6 +48,8 @@
* (see AndroidRuntime.cpp).
*/
+extern int register_android_media_ImageReader(JNIEnv* env);
+extern int register_android_media_PublicFormatUtils(JNIEnv* env);
extern int register_android_os_Binder(JNIEnv* env);
extern int register_libcore_util_NativeAllocationRegistry(JNIEnv* env);
@@ -126,6 +128,10 @@
{"android.database.sqlite.SQLiteDebug", REG_JNI(register_android_database_SQLiteDebug)},
{"android.database.sqlite.SQLiteRawStatement",
REG_JNI(register_android_database_SQLiteRawStatement)},
+#endif
+ {"android.media.ImageReader", REG_JNI(register_android_media_ImageReader)},
+ {"android.media.PublicFormatUtils", REG_JNI(register_android_media_PublicFormatUtils)},
+#ifdef __linux__
{"android.os.Binder", REG_JNI(register_android_os_Binder)},
{"android.os.FileObserver", REG_JNI(register_android_os_FileObserver)},
{"android.os.MessageQueue", REG_JNI(register_android_os_MessageQueue)},
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 78526ad..ee6899c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -7759,7 +7759,17 @@
@FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")
@hide -->
<permission android:name="android.permission.READ_BLOCKED_NUMBERS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature"
+ android:featureFlag="!android.permission.flags.grant_read_blocked_numbers_to_system_ui_intelligence" />
+
+ <!-- Allows the holder to read blocked numbers. See
+ {@link android.provider.BlockedNumberContract}.
+ @SystemApi
+ @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")
+ @hide -->
+ <permission android:name="android.permission.READ_BLOCKED_NUMBERS"
+ android:protectionLevel="signature|role"
+ android:featureFlag="android.permission.flags.grant_read_blocked_numbers_to_system_ui_intelligence" />
<!-- Allows the holder to write blocked numbers. See
{@link android.provider.BlockedNumberContract}.
diff --git a/core/res/res/layout/notification_2025_conversation_header.xml b/core/res/res/layout/notification_2025_conversation_header.xml
index 1bde173..68096f8 100644
--- a/core/res/res/layout/notification_2025_conversation_header.xml
+++ b/core/res/res/layout/notification_2025_conversation_header.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2024 The Android Open Source Project
+ ~ Copyright (C) 2025 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -15,157 +15,74 @@
~ limitations under the License
-->
-<com.android.internal.widget.ConversationHeaderLinearLayout
+<!-- extends RelativeLayout -->
+<NotificationHeaderView
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/conversation_header"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:id="@+id/notification_header"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_2025_header_height"
+ android:clipChildren="false"
+ android:gravity="center_vertical"
android:orientation="horizontal"
- android:paddingTop="@dimen/notification_2025_margin"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ android:importantForAccessibility="no"
>
- <TextView
- android:id="@+id/conversation_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
- android:textSize="16sp"
- android:singleLine="true"
- android:layout_weight="1"
- />
-
- <TextView
- android:id="@+id/app_name_divider"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
- android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
- android:text="@string/notification_header_divider_symbol"
- android:singleLine="true"
- android:visibility="gone"
- />
-
- <!-- App Name -->
- <com.android.internal.widget.ObservableTextView
- android:id="@+id/app_name_text"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
- android:singleLine="true"
- android:visibility="gone"
- />
-
- <TextView
- android:id="@+id/time_divider"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
- android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
- android:text="@string/notification_header_divider_symbol"
- android:singleLine="true"
- android:visibility="gone"
- />
-
- <DateTimeView
- android:id="@+id/time"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Time"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
- android:showRelative="true"
- android:singleLine="true"
- android:visibility="gone"
- />
-
- <ViewStub
- android:id="@+id/chronometer"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
- android:layout="@layout/notification_template_part_chronometer"
- android:visibility="gone"
- />
-
- <TextView
- android:id="@+id/verification_divider"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
- android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
- android:text="@string/notification_header_divider_symbol"
- android:singleLine="true"
- android:visibility="gone"
- />
-
<ImageView
- android:id="@+id/verification_icon"
- android:layout_width="@dimen/notification_verification_icon_size"
- android:layout_height="@dimen/notification_verification_icon_size"
- android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
- android:baseline="10dp"
- android:scaleType="fitCenter"
- android:src="@drawable/ic_notifications_alerted"
+ android:id="@+id/left_icon"
+ android:layout_width="@dimen/notification_2025_left_icon_size"
+ android:layout_height="@dimen/notification_2025_left_icon_size"
+ android:layout_alignParentStart="true"
+ android:layout_margin="@dimen/notification_2025_margin"
+ android:background="@drawable/notification_large_icon_outline"
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"
+ android:scaleType="centerCrop"
android:visibility="gone"
/>
- <TextView
- android:id="@+id/verification_text"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+ <include layout="@layout/notification_2025_conversation_icon_container" />
+
+ <!-- extends ViewGroup -->
+ <NotificationTopLineView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/notification_top_line"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
- android:layout_weight="100"
- android:showRelative="true"
- android:singleLine="true"
- android:visibility="gone"
+ android:layout_alignParentStart="true"
+ android:layout_toStartOf="@id/expand_button"
+ android:layout_alignWithParentIfMissing="true"
+ android:layout_marginVertical="@dimen/notification_2025_margin"
+ android:clipChildren="false"
+ android:gravity="center_vertical"
+ android:paddingStart="@dimen/notification_2025_content_margin_start"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ >
+
+ <include layout="@layout/notification_2025_top_line_views" />
+
+ </NotificationTopLineView>
+
+ <FrameLayout
+ android:id="@+id/alternate_expand_target"
+ android:layout_width="@dimen/notification_2025_content_margin_start"
+ android:layout_height="match_parent"
+ android:layout_alignParentStart="true"
+ android:importantForAccessibility="no"
+ android:focusable="false"
/>
- <ImageButton
- android:id="@+id/feedback"
- android:layout_width="@dimen/notification_feedback_size"
- android:layout_height="@dimen/notification_feedback_size"
- android:layout_marginStart="@dimen/notification_header_separating_margin"
- android:background="?android:selectableItemBackgroundBorderless"
- android:contentDescription="@string/notification_feedback_indicator"
- android:baseline="13dp"
- android:scaleType="fitCenter"
- android:src="@drawable/ic_feedback_indicator"
- android:visibility="gone"
- />
+ <include layout="@layout/notification_2025_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|end"
+ android:layout_alignParentEnd="true" />
- <ImageView
- android:id="@+id/phishing_alert"
- android:layout_width="@dimen/notification_2025_badge_size"
- android:layout_height="@dimen/notification_2025_badge_size"
- android:layout_marginStart="@dimen/notification_2025_badge_margin"
- android:baseline="@dimen/notification_2025_badge_baseline"
- android:scaleType="fitCenter"
- android:src="@drawable/ic_dialog_alert_material"
- android:visibility="gone"
- android:contentDescription="@string/notification_phishing_alert_content_description"
- />
+ <include layout="@layout/notification_close_button"
+ android:id="@+id/close_button"
+ android:layout_width="@dimen/notification_close_button_size"
+ android:layout_height="@dimen/notification_close_button_size"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentEnd="true" />
- <ImageView
- android:id="@+id/profile_badge"
- android:layout_width="@dimen/notification_2025_badge_size"
- android:layout_height="@dimen/notification_2025_badge_size"
- android:layout_marginStart="@dimen/notification_2025_badge_margin"
- android:baseline="@dimen/notification_2025_badge_baseline"
- android:scaleType="fitCenter"
- android:visibility="gone"
- android:contentDescription="@string/notification_work_profile_content_description"
- />
-
- <ImageView
- android:id="@+id/alerted_icon"
- android:layout_width="@dimen/notification_2025_badge_size"
- android:layout_height="@dimen/notification_2025_badge_size"
- android:layout_marginStart="@dimen/notification_2025_badge_margin"
- android:baseline="@dimen/notification_2025_badge_baseline"
- android:contentDescription="@string/notification_alerted_content_description"
- android:scaleType="fitCenter"
- android:src="@drawable/ic_notifications_alerted"
- android:visibility="gone"
- />
-</com.android.internal.widget.ConversationHeaderLinearLayout>
+</NotificationHeaderView>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_call.xml b/core/res/res/layout/notification_2025_template_collapsed_call.xml
index 6f3c15a..ee691e4 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_call.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_call.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright (C) 2024 The Android Open Source Project
+ ~ Copyright (C) 2025 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -25,55 +25,177 @@
android:theme="@style/Theme.DeviceDefault.Notification"
>
- <!-- CallLayout shares visual appearance with ConversationLayout, so shares layouts -->
- <include layout="@layout/notification_2025_conversation_icon_container" />
-
<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="@dimen/notification_2025_min_height"
- android:orientation="horizontal"
+ android:clipChildren="false"
+ android:orientation="vertical"
>
- <LinearLayout
- android:id="@+id/notification_main_column"
+ <com.android.internal.widget.NotificationMaxHeightFrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:orientation="vertical"
- android:paddingBottom="@dimen/notification_2025_margin"
+ android:minHeight="@dimen/notification_2025_min_height"
+ android:clipChildren="false"
>
- <include
- layout="@layout/notification_2025_conversation_header"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ <ImageView
+ android:id="@+id/left_icon"
+ android:layout_width="@dimen/notification_2025_left_icon_size"
+ android:layout_height="@dimen/notification_2025_left_icon_size"
+ android:layout_alignParentStart="true"
+ android:layout_margin="@dimen/notification_2025_margin"
+ android:background="@drawable/notification_large_icon_outline"
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"
+ android:scaleType="centerCrop"
+ android:visibility="gone"
/>
- <include layout="@layout/notification_template_text"
- android:layout_height="wrap_content"
- android:minHeight="@dimen/notification_text_height"
+ <!-- CallLayout shares visual appearance with ConversationLayout, so shares layouts -->
+ <include layout="@layout/notification_2025_conversation_icon_container" />
+
+ <FrameLayout
+ android:id="@+id/alternate_expand_target"
+ android:layout_width="@dimen/notification_2025_content_margin_start"
+ android:layout_height="match_parent"
+ android:layout_gravity="start"
+ android:importantForAccessibility="no"
+ android:focusable="false"
/>
+ <LinearLayout
+ android:id="@+id/notification_headerless_view_row"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:orientation="horizontal"
+ android:clipChildren="false"
+ >
+
+ <LinearLayout
+ android:id="@+id/notification_headerless_view_column"
+ android:layout_width="0px"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:layout_marginBottom="@dimen/notification_2025_margin"
+ android:layout_marginTop="@dimen/notification_2025_margin"
+ android:clipChildren="false"
+ android:orientation="vertical"
+ >
+
+ <NotificationTopLineView
+ android:id="@+id/notification_top_line"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_headerless_line_height"
+ android:clipChildren="false"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ >
+
+ <!--
+ NOTE: The notification_2025_top_line_views layout contains the app_name_text.
+ In order to include the title view at the beginning, the Notification.Builder
+ has logic to hide that view whenever this title view is to be visible.
+ -->
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:ellipsize="end"
+ android:fadingEdge="horizontal"
+ android:singleLine="true"
+ android:textAlignment="viewStart"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+ />
+
+ <include layout="@layout/notification_2025_top_line_views" />
+
+ </NotificationTopLineView>
+
+ <LinearLayout
+ android:id="@+id/notification_main_column"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:clipChildren="false"
+ >
+ <com.android.internal.widget.NotificationVanishingFrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_headerless_line_height"
+ >
+ <!-- This is the simplest way to keep this text vertically centered without
+ gravity="center_vertical" which causes jumpiness in expansion animations. -->
+ <include
+ layout="@layout/notification_2025_text"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_text_height"
+ android:layout_gravity="center_vertical"
+ android:layout_marginTop="0dp"
+ />
+ </com.android.internal.widget.NotificationVanishingFrameLayout>
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/right_icon"
+ android:layout_width="@dimen/notification_right_icon_size"
+ android:layout_height="@dimen/notification_right_icon_size"
+ android:layout_gravity="center_vertical|end"
+ android:layout_marginTop="@dimen/notification_right_icon_headerless_margin"
+ android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin"
+ android:layout_marginStart="@dimen/notification_right_icon_content_margin"
+ android:background="@drawable/notification_large_icon_outline"
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"
+ android:scaleType="centerCrop"
+ />
+
+ <FrameLayout
+ android:id="@+id/expand_button_touch_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/notification_content_margin_end"
+ >
+
+ <include layout="@layout/notification_2025_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|end"
+ />
+
+ </FrameLayout>
+
+ </LinearLayout>
+
+ <include layout="@layout/notification_close_button"
+ android:id="@+id/close_button"
+ android:layout_width="@dimen/notification_close_button_size"
+ android:layout_height="@dimen/notification_close_button_size"
+ android:layout_gravity="top|end" />
+
+ </com.android.internal.widget.NotificationMaxHeightFrameLayout>
+
+ <LinearLayout
+ android:id="@+id/notification_action_list_margin_target"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-20dp"
+ android:clipChildren="false"
+ android:orientation="vertical">
+ <include layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_content_margin"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end" />
+ <include layout="@layout/notification_material_action_list" />
</LinearLayout>
-
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:minWidth="@dimen/notification_content_margin_end"
- >
-
- <include
- layout="@layout/notification_2025_expand_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top|end"
- />
-
- </FrameLayout>
-
</LinearLayout>
</com.android.internal.widget.CallLayout>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_conversation.xml b/core/res/res/layout/notification_2025_template_collapsed_conversation.xml
new file mode 100644
index 0000000..f804111
--- /dev/null
+++ b/core/res/res/layout/notification_2025_template_collapsed_conversation.xml
@@ -0,0 +1,216 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<!-- extends FrameLayout -->
+<com.android.internal.widget.ConversationLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:tag="conversation"
+ >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:orientation="vertical"
+ >
+
+
+ <com.android.internal.widget.NotificationMaxHeightFrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_2025_min_height"
+ android:clipChildren="false"
+ >
+
+ <ImageView
+ android:id="@+id/left_icon"
+ android:layout_width="@dimen/notification_2025_left_icon_size"
+ android:layout_height="@dimen/notification_2025_left_icon_size"
+ android:layout_alignParentStart="true"
+ android:layout_margin="@dimen/notification_2025_margin"
+ android:background="@drawable/notification_large_icon_outline"
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"
+ android:scaleType="centerCrop"
+ android:visibility="gone"
+ />
+
+ <include layout="@layout/notification_2025_conversation_icon_container" />
+
+ <FrameLayout
+ android:id="@+id/alternate_expand_target"
+ android:layout_width="@dimen/notification_2025_content_margin_start"
+ android:layout_height="match_parent"
+ android:layout_gravity="start"
+ android:importantForAccessibility="no"
+ android:focusable="false"
+ />
+
+ <!--
+ NOTE: to make the expansion animation of id/notification_messaging happen vertically,
+ its X positioning must be the left edge of the notification, so instead of putting the
+ layout_marginStart on the id/notification_headerless_view_row, we put it on
+ id/notification_top_line, making the layout here just a bit different from the base.
+ -->
+ <LinearLayout
+ android:id="@+id/notification_headerless_view_row"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:clipChildren="false"
+ >
+
+ <!--
+ NOTE: because messaging will always have 2 lines, this LinearLayout should NOT
+ have the id/notification_headerless_view_column, as that is used for modifying
+ vertical margins to accommodate the single-line state that base supports
+ -->
+ <LinearLayout
+ android:layout_width="0px"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:layout_marginBottom="@dimen/notification_2025_margin"
+ android:layout_marginTop="@dimen/notification_2025_margin"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:clipChildren="false"
+ android:orientation="vertical"
+ >
+
+ <NotificationTopLineView
+ android:id="@+id/notification_top_line"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_headerless_line_height"
+ android:clipChildren="false"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ >
+
+ <!--
+ NOTE: The notification_2025_top_line_views layout contains the app_name_text.
+ In order to include the title view at the beginning, the Notification.Builder
+ has logic to hide that view whenever this title view is to be visible.
+ -->
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:ellipsize="end"
+ android:fadingEdge="horizontal"
+ android:singleLine="true"
+ android:textAlignment="viewStart"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+ />
+
+ <include layout="@layout/notification_2025_top_line_views" />
+
+ </NotificationTopLineView>
+
+ <LinearLayout
+ android:id="@+id/notification_main_column"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:clipChildren="false"
+ >
+ <com.android.internal.widget.MessagingLinearLayout
+ android:id="@+id/notification_messaging"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:spacing="@dimen/notification_messaging_spacing" />
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <!-- Images -->
+ <com.android.internal.widget.MessagingLinearLayout
+ android:id="@+id/conversation_image_message_container"
+ android:layout_width="@dimen/notification_right_icon_size"
+ android:layout_height="@dimen/notification_right_icon_size"
+ android:layout_gravity="center_vertical|end"
+ android:layout_marginTop="@dimen/notification_right_icon_headerless_margin"
+ android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin"
+ android:layout_marginStart="@dimen/notification_right_icon_content_margin"
+ android:forceHasOverlappingRendering="false"
+ android:spacing="0dp"
+ android:clipChildren="false"
+ android:visibility="gone"
+ />
+
+ <ImageView
+ android:id="@+id/right_icon"
+ android:layout_width="@dimen/notification_right_icon_size"
+ android:layout_height="@dimen/notification_right_icon_size"
+ android:layout_gravity="center_vertical|end"
+ android:layout_marginTop="@dimen/notification_right_icon_headerless_margin"
+ android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin"
+ android:layout_marginStart="@dimen/notification_right_icon_content_margin"
+ android:background="@drawable/notification_large_icon_outline"
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"
+ android:scaleType="centerCrop"
+ />
+
+ <FrameLayout
+ android:id="@+id/expand_button_touch_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/notification_content_margin_end"
+ >
+
+ <include layout="@layout/notification_2025_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|end"
+ />
+
+ </FrameLayout>
+
+ </LinearLayout>
+
+ <include layout="@layout/notification_close_button"
+ android:id="@+id/close_button"
+ android:layout_width="@dimen/notification_close_button_size"
+ android:layout_height="@dimen/notification_close_button_size"
+ android:layout_gravity="top|end" />
+
+ </com.android.internal.widget.NotificationMaxHeightFrameLayout>
+
+ <LinearLayout
+ android:id="@+id/notification_action_list_margin_target"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-20dp"
+ android:clipChildren="false"
+ android:orientation="vertical">
+ <include layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_content_margin"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end" />
+ <include layout="@layout/notification_material_action_list" />
+ </LinearLayout>
+</LinearLayout>
+</com.android.internal.widget.ConversationLayout>
diff --git a/core/res/res/layout/notification_2025_template_conversation.xml b/core/res/res/layout/notification_2025_template_conversation.xml
deleted file mode 100644
index 24b6ad0..0000000
--- a/core/res/res/layout/notification_2025_template_conversation.xml
+++ /dev/null
@@ -1,159 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2024 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<!-- extends FrameLayout -->
-<com.android.internal.widget.ConversationLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/status_bar_latest_event_content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clipChildren="false"
- android:tag="conversation"
- android:theme="@style/Theme.DeviceDefault.Notification"
- >
-
- <include layout="@layout/notification_2025_conversation_icon_container" />
-
- <!-- Wraps entire "expandable" notification -->
- <com.android.internal.widget.RemeasuringLinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="top"
- android:clipToPadding="false"
- android:clipChildren="false"
- android:orientation="vertical"
- >
- <!-- LinearLayout for Expand Button-->
- <com.android.internal.widget.RemeasuringLinearLayout
- android:id="@+id/expand_button_and_content_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:gravity="start|top"
- android:orientation="horizontal"
- android:clipChildren="false"
- android:clipToPadding="false">
- <!--TODO: move this into a separate layout and share logic with the header to bring back app opps etc-->
- <com.android.internal.widget.RemeasuringLinearLayout
- android:id="@+id/notification_action_list_margin_target"
- android:layout_width="0dp"
- android:orientation="vertical"
- android:layout_height="wrap_content"
- android:layout_weight="1">
-
- <!-- Header -->
-
- <!-- Use layout_marginStart instead of paddingStart to work around strange
- measurement behavior on lower display densities. -->
- <include
- layout="@layout/notification_2025_conversation_header"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="2dp"
- android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- />
-
- <!-- Messages -->
- <com.android.internal.widget.MessagingLinearLayout
- android:id="@+id/notification_messaging"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="@dimen/notification_text_size"
- android:spacing="@dimen/notification_messaging_spacing"
- android:clipToPadding="false"
- android:clipChildren="false"
- />
- </com.android.internal.widget.RemeasuringLinearLayout>
-
- <!-- This is where the expand button container will be placed when collapsed-->
- </com.android.internal.widget.RemeasuringLinearLayout>
-
- <include layout="@layout/notification_template_smart_reply_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/notification_content_margin"
- android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end" />
- <include layout="@layout/notification_material_action_list" />
- </com.android.internal.widget.RemeasuringLinearLayout>
-
- <!--expand_button_a11y_container ensures talkback focus order is correct when view is expanded.
- The -1px of marginTop and 1px of paddingTop make sure expand_button_a11y_container is prior to
- its sibling view in accessibility focus order.
- {see android.view.ViewGroup.addChildrenForAccessibility()}
- expand_button_container will be moved under expand_button_and_content_container when collapsed,
- this dynamic movement ensures message can flow under expand button when expanded-->
- <FrameLayout
- android:id="@+id/expand_button_a11y_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="end|top"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:layout_marginTop="-1px"
- android:paddingTop="1px"
- >
- <!--expand_button_container is dynamically placed between here and at the end of the
- layout. It starts here since only FrameLayout layout params have gravity-->
- <LinearLayout
- android:id="@+id/expand_button_container"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_gravity="end|top"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:orientation="vertical">
- <include layout="@layout/notification_close_button"
- android:layout_width="@dimen/notification_close_button_size"
- android:layout_height="@dimen/notification_close_button_size"
- android:layout_gravity="end"
- android:layout_marginEnd="20dp"
- />
- <!--expand_button_touch_container makes sure that we can nicely center the expand
- content in the collapsed layout while the parent makes sure that we're never laid out
- bigger than the messaging content.-->
- <LinearLayout
- android:id="@+id/expand_button_touch_container"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/conversation_expand_button_height"
- android:orientation="horizontal"
- android:layout_gravity="end|top"
- android:paddingEnd="0dp"
- android:clipToPadding="false"
- android:clipChildren="false"
- >
- <!-- Images -->
- <com.android.internal.widget.MessagingLinearLayout
- android:id="@+id/conversation_image_message_container"
- android:forceHasOverlappingRendering="false"
- android:layout_width="40dp"
- android:layout_height="40dp"
- android:layout_marginStart="@dimen/conversation_image_start_margin"
- android:spacing="0dp"
- android:layout_gravity="center"
- android:clipToPadding="false"
- android:clipChildren="false"
- />
- <include layout="@layout/notification_2025_expand_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top|end"
- />
- </LinearLayout>
- </LinearLayout>
- </FrameLayout>
-</com.android.internal.widget.ConversationLayout>
diff --git a/core/res/res/layout/notification_2025_template_expanded_call.xml b/core/res/res/layout/notification_2025_template_expanded_call.xml
index 0be6125..bbc2966 100644
--- a/core/res/res/layout/notification_2025_template_expanded_call.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_call.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2024 The Android Open Source Project
+ ~ Copyright (C) 2025 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -27,83 +27,47 @@
>
<!-- CallLayout shares visual appearance with ConversationLayout, so shares layouts -->
- <include layout="@layout/notification_2025_conversation_icon_container" />
+ <include layout="@layout/notification_2025_conversation_header"/>
- <LinearLayout
+ <com.android.internal.widget.RemeasuringLinearLayout
android:id="@+id/notification_action_list_margin_target"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/notification_content_margin"
- android:orientation="vertical"
- >
+ android:layout_gravity="top"
+ android:clipChildren="false"
+ android:orientation="vertical">
- <LinearLayout
+ <!-- Note: the top margin is being set in code based on the estimated space needed for
+ the header text. -->
+ <com.android.internal.widget.RemeasuringLinearLayout
+ android:id="@+id/notification_main_column"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_gravity="top"
android:layout_weight="1"
- android:orientation="horizontal"
- >
-
- <LinearLayout
- android:id="@+id/notification_main_column"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_weight="1"
- android:orientation="vertical"
- android:minHeight="68dp"
- >
-
- <include
- layout="@layout/notification_2025_conversation_header"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- />
-
- <include layout="@layout/notification_template_text_multiline" />
-
- <include
- android:layout_width="match_parent"
- android:layout_height="@dimen/notification_progress_bar_height"
- android:layout_marginTop="@dimen/notification_progress_margin_top"
- layout="@layout/notification_template_progress"
- />
- </LinearLayout>
-
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:minWidth="@dimen/notification_content_margin_end"
- >
-
- <include
- layout="@layout/notification_expand_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- />
-
- </FrameLayout>
-
- </LinearLayout>
-
- <ViewStub
- android:layout="@layout/notification_material_reply_text"
- android:id="@+id/notification_material_reply_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- />
-
- <include
- layout="@layout/notification_template_smart_reply_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:orientation="vertical"
+ android:clipChildren="false"
+ >
+
+ <include layout="@layout/notification_template_part_line1"/>
+
+ <include layout="@layout/notification_template_text_multiline" />
+
+ </com.android.internal.widget.RemeasuringLinearLayout>
+
+ <include layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
android:layout_marginTop="@dimen/notification_content_margin"
- />
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end" />
<include layout="@layout/notification_material_action_list" />
- </LinearLayout>
+ </com.android.internal.widget.RemeasuringLinearLayout>
+
+ <include layout="@layout/notification_template_right_icon" />
</com.android.internal.widget.CallLayout>
diff --git a/core/res/res/layout/notification_2025_template_expanded_conversation.xml b/core/res/res/layout/notification_2025_template_expanded_conversation.xml
new file mode 100644
index 0000000..d7e8bb3
--- /dev/null
+++ b/core/res/res/layout/notification_2025_template_expanded_conversation.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<!-- extends FrameLayout -->
+<com.android.internal.widget.ConversationLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ android:tag="conversation"
+ >
+
+ <include layout="@layout/notification_2025_conversation_header"/>
+
+ <com.android.internal.widget.RemeasuringLinearLayout
+ android:id="@+id/notification_action_list_margin_target"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:clipChildren="false"
+ android:orientation="vertical">
+
+ <!-- Note: the top margin is being set in code based on the estimated space needed for
+ the header text. -->
+ <com.android.internal.widget.RemeasuringLinearLayout
+ android:id="@+id/notification_main_column"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:layout_weight="1"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:orientation="vertical"
+ android:clipChildren="false"
+ >
+
+ <include layout="@layout/notification_template_part_line1"/>
+
+ <com.android.internal.widget.MessagingLinearLayout
+ android:id="@+id/notification_messaging"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:spacing="@dimen/notification_messaging_spacing" />
+ </com.android.internal.widget.RemeasuringLinearLayout>
+
+ <include layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_content_margin"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end" />
+
+ <include layout="@layout/notification_material_action_list" />
+
+ </com.android.internal.widget.RemeasuringLinearLayout>
+
+ <include layout="@layout/notification_template_right_icon" />
+
+</com.android.internal.widget.ConversationLayout>
diff --git a/core/res/res/layout/notification_2025_template_expanded_messaging.xml b/core/res/res/layout/notification_2025_template_expanded_messaging.xml
index 177706c..20abfee 100644
--- a/core/res/res/layout/notification_2025_template_expanded_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_messaging.xml
@@ -36,17 +36,21 @@
android:clipChildren="false"
android:orientation="vertical">
+ <!-- Note: the top margin is being set in code based on the estimated space needed for
+ the header text. -->
<com.android.internal.widget.RemeasuringLinearLayout
android:id="@+id/notification_main_column"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:layout_weight="1"
- android:layout_marginTop="@dimen/notification_2025_header_height"
android:layout_marginEnd="@dimen/notification_content_margin_end"
android:orientation="vertical"
android:clipChildren="false"
>
+
+ <include layout="@layout/notification_template_part_line1"/>
+
<com.android.internal.widget.MessagingLinearLayout
android:id="@+id/notification_messaging"
android:layout_width="match_parent"
diff --git a/core/res/res/layout/notification_2025_top_line_views.xml b/core/res/res/layout/notification_2025_top_line_views.xml
index 7487346..7431c42 100644
--- a/core/res/res/layout/notification_2025_top_line_views.xml
+++ b/core/res/res/layout/notification_2025_top_line_views.xml
@@ -20,7 +20,7 @@
<merge
xmlns:android="http://schemas.android.com/apk/res/android">
- <TextView
+ <com.android.internal.widget.ObservableTextView
android:id="@+id/app_name_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -108,6 +108,42 @@
android:visibility="gone"
/>
+ <TextView
+ android:id="@+id/verification_divider"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+ android:layout_marginStart="@dimen/notification_header_separating_margin"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:text="@string/notification_header_divider_symbol"
+ android:singleLine="true"
+ android:visibility="gone"
+ />
+
+ <ImageView
+ android:id="@+id/verification_icon"
+ android:layout_width="@dimen/notification_2025_badge_size"
+ android:layout_height="@dimen/notification_2025_badge_size"
+ android:layout_marginStart="@dimen/notification_header_separating_margin"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:baseline="@dimen/notification_2025_badge_baseline"
+ android:scaleType="fitCenter"
+ android:visibility="gone"
+ />
+
+ <TextView
+ android:id="@+id/verification_text"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_header_separating_margin"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:layout_weight="100"
+ android:showRelative="true"
+ android:singleLine="true"
+ android:visibility="gone"
+ />
+
<ImageButton
android:id="@+id/feedback"
android:layout_width="@dimen/notification_feedback_size"
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index f2ec56c..59ed25a 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -64,6 +64,13 @@
<integer name="auto_data_switch_performance_stability_time_threshold_millis">120000</integer>
<java-symbol type="integer" name="auto_data_switch_performance_stability_time_threshold_millis" />
+ <!-- Define the bar for switching data back to the default SIM when both SIMs are out of service
+ in milliseconds. A value of 0 means an immediate switch, otherwise for a negative value,
+ the threshold defined by auto_data_switch_availability_stability_time_threshold_millis
+ will be used instead. -->
+ <integer name="auto_data_switch_availability_switchback_stability_time_threshold_millis">150000</integer>
+ <java-symbol type="integer" name="auto_data_switch_availability_switchback_stability_time_threshold_millis" />
+
<!-- Define the maximum retry times when a validation for switching failed.-->
<integer name="auto_data_switch_validation_max_retry">7</integer>
<java-symbol type="integer" name="auto_data_switch_validation_max_retry" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 26f0ab3..b013ffd 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3264,6 +3264,7 @@
<java-symbol type="dimen" name="notification_content_margin" />
<java-symbol type="dimen" name="notification_2025_margin" />
<java-symbol type="dimen" name="notification_2025_content_margin_top" />
+ <java-symbol type="dimen" name="notification_2025_content_margin_start" />
<java-symbol type="dimen" name="notification_2025_expand_button_horizontal_icon_padding" />
<java-symbol type="dimen" name="notification_2025_expand_button_reduced_end_padding" />
<java-symbol type="dimen" name="notification_progress_margin_horizontal" />
@@ -4705,7 +4706,8 @@
<java-symbol type="dimen" name="conversation_icon_container_top_padding" />
<java-symbol type="dimen" name="conversation_icon_container_top_padding_small_avatar" />
<java-symbol type="layout" name="notification_template_material_conversation" />
- <java-symbol type="layout" name="notification_2025_template_conversation" />
+ <java-symbol type="layout" name="notification_2025_template_collapsed_conversation" />
+ <java-symbol type="layout" name="notification_2025_template_expanded_conversation" />
<java-symbol type="dimen" name="button_padding_horizontal_material" />
<java-symbol type="dimen" name="button_inset_horizontal_material" />
<java-symbol type="layout" name="conversation_face_pile_layout" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 4c49ff8..05fb5735 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -249,8 +249,14 @@
android_ravenwood_test {
name: "FrameworksCoreTestsRavenwood",
libs: [
- "android.test.base.stubs.system",
- "android.test.runner.stubs.system",
+ "android.test.base.stubs",
+ "android.test.mock.stubs",
+ "android.test.runner.stubs",
+ "android.view.flags-aconfig-java",
+ "ext",
+ "framework",
+ "framework-res",
+ "org.apache.http.legacy.stubs",
],
static_libs: [
"androidx.annotation_annotation",
@@ -264,6 +270,7 @@
"flag-junit",
"flag-junit",
"perfetto_trace_java_protos",
+ "platform-compat-test-rules",
"platform-test-annotations",
"testng",
],
@@ -278,8 +285,12 @@
"src/android/content/res/*.java",
"src/android/content/res/*.kt",
"src/android/database/CursorWindowTest.java",
+ "src/android/graphics/*.java",
+ "src/android/graphics/*.kt",
"src/android/os/**/*.java",
"src/android/telephony/PinResultTest.java",
+ "src/android/text/**/*.java",
+ "src/android/text/**/*.kt",
"src/android/util/**/*.java",
"src/android/view/DisplayAdjustmentsTests.java",
"src/android/view/DisplayInfoTest.java",
@@ -288,20 +299,21 @@
"src/com/android/internal/os/**/*.java",
"src/com/android/internal/power/EnergyConsumerStatsTest.java",
"src/com/android/internal/ravenwood/**/*.java",
-
- // Pull in R.java from FrameworksCoreTests-resonly, not from FrameworksCoreTests,
- // to avoid having a dependency to FrameworksCoreTests.
- // This way, when updating source files and running this test, we don't need to
- // rebuild the entire FrameworksCoreTests, which would be slow.
"src/com/android/internal/util/**/*.java",
":FrameworksCoreTestDoubles-sources",
":FrameworksCoreTests-aidl",
":FrameworksCoreTests-helpers",
+
+ // Pull in R.java from FrameworksCoreTests-resonly, not from FrameworksCoreTests,
+ // to avoid having a dependency to FrameworksCoreTests.
+ // This way, when updating source files and running this test, we don't need to
+ // rebuild the entire FrameworksCoreTests, which would be slow.
":FrameworksCoreTests-resonly{.aapt.srcjar}",
],
exclude_srcs: [
"src/android/content/res/FontScaleConverterActivityTest.java",
+ "src/android/graphics/GraphicsPerformanceTests.java",
],
resource_apk: "FrameworksCoreTests-resonly",
aidl: {
@@ -313,6 +325,7 @@
"res/xml/power_profile_test_cpu_legacy.xml",
"res/xml/power_profile_test_modem.xml",
],
+ sdk_version: "core_platform",
auto_gen_config: true,
team: "trendy_team_ravenwood",
}
diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterTest.kt b/core/tests/coretests/src/android/content/res/FontScaleConverterTest.kt
index 0e5d926..2c61442 100644
--- a/core/tests/coretests/src/android/content/res/FontScaleConverterTest.kt
+++ b/core/tests/coretests/src/android/content/res/FontScaleConverterTest.kt
@@ -17,10 +17,8 @@
package android.content.res
import android.platform.test.annotations.Presubmit
-import android.platform.test.ravenwood.RavenwoodRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertWithMessage
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -28,9 +26,6 @@
@RunWith(AndroidJUnit4::class)
class FontScaleConverterTest {
- @get:Rule
- val ravenwoodRule: RavenwoodRule = RavenwoodRule.Builder().build()
-
@Test
fun straightInterpolation() {
val table = createTable(8f to 8f, 10f to 10f, 20f to 20f)
diff --git a/core/tests/coretests/src/android/graphics/BitmapFactoryTest.java b/core/tests/coretests/src/android/graphics/BitmapFactoryTest.java
index 84bdbe0..263307e 100644
--- a/core/tests/coretests/src/android/graphics/BitmapFactoryTest.java
+++ b/core/tests/coretests/src/android/graphics/BitmapFactoryTest.java
@@ -20,7 +20,9 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import android.os.MemoryFile;
import android.os.ParcelFileDescriptor;
+import android.platform.test.annotations.DisabledOnRavenwood;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -37,6 +39,7 @@
// tests that we can decode bitmaps from MemoryFiles
@SmallTest
@Test
+ @DisabledOnRavenwood(blockedBy = MemoryFile.class)
public void testBitmapParcelFileDescriptor() throws Exception {
Bitmap bitmap1 = Bitmap.createBitmap(
new int[] { Color.BLUE }, 1, 1, Bitmap.Config.RGB_565);
diff --git a/core/tests/coretests/src/android/graphics/BitmapTest.java b/core/tests/coretests/src/android/graphics/BitmapTest.java
index 0126d36..61c3d78 100644
--- a/core/tests/coretests/src/android/graphics/BitmapTest.java
+++ b/core/tests/coretests/src/android/graphics/BitmapTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.fail;
import android.hardware.HardwareBuffer;
+import android.platform.test.annotations.DisabledOnRavenwood;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -252,6 +253,7 @@
| GraphicBuffer.USAGE_SW_WRITE_OFTEN;
@Test
+ @DisabledOnRavenwood(blockedBy = HardwareBuffer.class)
public void testWrapHardwareBufferWithSrgbColorSpace() {
GraphicBuffer buffer = GraphicBuffer.create(10, 10, PixelFormat.RGBA_8888, GRAPHICS_USAGE);
Canvas canvas = buffer.lockCanvas();
@@ -265,6 +267,7 @@
}
@Test
+ @DisabledOnRavenwood(blockedBy = HardwareBuffer.class)
public void testWrapHardwareBufferWithDisplayP3ColorSpace() {
GraphicBuffer buffer = GraphicBuffer.create(10, 10, PixelFormat.RGBA_8888, GRAPHICS_USAGE);
Canvas canvas = buffer.lockCanvas();
diff --git a/core/tests/coretests/src/android/graphics/PaintTest.java b/core/tests/coretests/src/android/graphics/PaintTest.java
index 56760d7..deb5157 100644
--- a/core/tests/coretests/src/android/graphics/PaintTest.java
+++ b/core/tests/coretests/src/android/graphics/PaintTest.java
@@ -24,6 +24,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
@@ -97,6 +98,7 @@
@SmallTest
@Test
+ @DisabledOnRavenwood(bug = 391381043)
public void testHintingWidth() {
final Typeface fontTypeface = Typeface.createFromAsset(
InstrumentationRegistry.getInstrumentation().getContext().getAssets(), FONT_PATH);
@@ -143,6 +145,7 @@
}
@Test
+ @DisabledOnRavenwood(bug = 391381043)
public void testHasGlyph_variationSelectors() {
final Typeface fontTypeface = Typeface.createFromAsset(
InstrumentationRegistry.getInstrumentation().getContext().getAssets(),
diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
index 2b6eda8f..dc3376e 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
@@ -35,9 +35,9 @@
import android.text.FontConfig;
import android.util.ArrayMap;
-import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.text.flags.Flags;
@@ -63,9 +63,6 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TypefaceSystemFallbackTest {
- private static final String SYSTEM_FONT_DIR = "/system/fonts/";
- private static final String SYSTEM_FONTS_XML = "/system/etc/fonts.xml";
-
private static final String[] TEST_FONT_FILES = {
"a3em.ttf", // Supports "a","b","c". The width of "a" is 3em, others are 1em.
"b3em.ttf", // Supports "a","b","c". The width of "b" is 3em, others are 1em.
@@ -118,8 +115,6 @@
@Before
public void setUp() {
- final AssetManager am =
- InstrumentationRegistry.getInstrumentation().getContext().getAssets();
for (final String fontFile : TEST_FONT_FILES) {
final String sourceInAsset = "fonts/" + fontFile;
copyAssetToFile(sourceInAsset, new File(TEST_FONT_DIR, fontFile));
@@ -216,7 +211,8 @@
FontConfig fontConfig;
try {
fontConfig = FontListParser.parse(
- SYSTEM_FONTS_XML, SYSTEM_FONT_DIR, null, TEST_OEM_DIR, null, 0, 0);
+ SystemFonts.LEGACY_FONTS_XML, SystemFonts.SYSTEM_FONT_DIR,
+ null, TEST_OEM_DIR, null, 0, 0);
} catch (IOException | XmlPullParserException e) {
throw new RuntimeException(e);
}
diff --git a/core/tests/coretests/src/android/graphics/TypefaceTest.java b/core/tests/coretests/src/android/graphics/TypefaceTest.java
index 80efa51..0c8b5ab 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceTest.java
@@ -26,6 +26,7 @@
import android.graphics.fonts.FontFamily;
import android.graphics.fonts.SystemFonts;
import android.os.SharedMemory;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.text.FontConfig;
import android.util.ArrayMap;
@@ -196,6 +197,7 @@
@SmallTest
@Test
+ @DisabledOnRavenwood(blockedBy = SharedMemory.class)
public void testSerialize() throws Exception {
FontConfig fontConfig = SystemFonts.getSystemPreinstalledFontConfig();
Map<String, FontFamily[]> fallbackMap = SystemFonts.buildSystemFallback(fontConfig);
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
index 9383807..8ef105f 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
+++ b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
@@ -56,6 +56,7 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -116,7 +117,8 @@
@Test
public void testDisplayListenerIsCalled_WhenDisplayEventOccurs() throws RemoteException {
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
- ALL_DISPLAY_EVENTS, /* packageName= */ null);
+ ALL_DISPLAY_EVENTS, /* packageName= */ null,
+ /* isEventFilterExplicit */ true);
Mockito.verify(mDisplayManager)
.registerCallbackWithEventMask(mCallbackCaptor.capture(), anyLong());
IDisplayManagerCallback callback = mCallbackCaptor.getValue();
@@ -151,7 +153,7 @@
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE
| INTERNAL_EVENT_FLAG_DISPLAY_STATE,
- null);
+ null, /* isEventFilterExplicit */ true);
Mockito.verify(mDisplayManager)
.registerCallbackWithEventMask(mCallbackCaptor.capture(), anyLong());
IDisplayManagerCallback callback = mCallbackCaptor.getValue();
@@ -172,11 +174,80 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_DELAY_IMPLICIT_RR_REGISTRATION_UNTIL_RR_ACCESSED)
+ public void test_refreshRateRegistration_implicitRRCallbacksEnabled()
+ throws RemoteException {
+ // Subscription without supplied events doesn't subscribe to RR events
+ mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
+ ALL_DISPLAY_EVENTS, /* packageName= */ null,
+ /* isEventFilterExplicit */ false);
+ Mockito.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(), eq(ALL_DISPLAY_EVENTS));
+
+ // After registering to refresh rate changes, subscription without supplied events subscribe
+ // to RR events
+ mDisplayManagerGlobal.registerForRefreshRateChanges();
+ mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
+ ALL_DISPLAY_EVENTS, /* packageName= */ null,
+ /* isEventFilterExplicit */ false);
+ Mockito.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(), eq(ALL_DISPLAY_EVENTS
+ | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE));
+
+ // Assert all the existing listeners are also subscribed to RR events
+ CopyOnWriteArrayList<DisplayManagerGlobal.DisplayListenerDelegate> delegates =
+ mDisplayManagerGlobal.getDisplayListeners();
+ for (DisplayManagerGlobal.DisplayListenerDelegate delegate: delegates) {
+ assertEquals(ALL_DISPLAY_EVENTS | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE,
+ delegate.mInternalEventFlagsMask);
+ }
+
+ // Subscription to RR when events are supplied doesn't happen
+ mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
+ ALL_DISPLAY_EVENTS, /* packageName= */ null,
+ /* isEventFilterExplicit */ true);
+ Mockito.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(), eq(ALL_DISPLAY_EVENTS));
+
+ // Assert one listeners are not subscribed to RR events
+ delegates = mDisplayManagerGlobal.getDisplayListeners();
+ int subscribedListenersCount = 0;
+ int nonSubscribedListenersCount = 0;
+ for (DisplayManagerGlobal.DisplayListenerDelegate delegate: delegates) {
+
+ if (delegate.isEventFilterExplicit()) {
+ assertEquals(ALL_DISPLAY_EVENTS, delegate.mInternalEventFlagsMask);
+ nonSubscribedListenersCount++;
+ } else {
+ assertEquals(ALL_DISPLAY_EVENTS | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE,
+ delegate.mInternalEventFlagsMask);
+ subscribedListenersCount++;
+ }
+ }
+
+ assertEquals(2, subscribedListenersCount);
+ assertEquals(1, nonSubscribedListenersCount);
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_DELAY_IMPLICIT_RR_REGISTRATION_UNTIL_RR_ACCESSED)
+ public void test_refreshRateRegistration_implicitRRCallbacksDisabled()
+ throws RemoteException {
+ mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
+ ALL_DISPLAY_EVENTS, /* packageName= */ null,
+ /* isEventFilterExplicit */ false);
+ Mockito.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(), eq(ALL_DISPLAY_EVENTS
+ | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE));
+ }
+
+ @Test
public void testDisplayListenerIsNotCalled_WhenClientIsNotSubscribed() throws RemoteException {
// First we subscribe to all events in order to test that the subsequent calls to
// registerDisplayListener will update the event mask.
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
- ALL_DISPLAY_EVENTS, /* packageName= */ null);
+ ALL_DISPLAY_EVENTS, /* packageName= */ null,
+ /* isEventFilterExplicit */ true);
Mockito.verify(mDisplayManager)
.registerCallbackWithEventMask(mCallbackCaptor.capture(), anyLong());
IDisplayManagerCallback callback = mCallbackCaptor.getValue();
@@ -184,21 +255,24 @@
int displayId = 1;
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
ALL_DISPLAY_EVENTS
- & ~DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED, null);
+ & ~DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED, null,
+ /* isEventFilterExplicit */ true);
callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
waitForHandler();
Mockito.verifyZeroInteractions(mDisplayListener);
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
ALL_DISPLAY_EVENTS
- & ~DISPLAY_CHANGE_EVENTS, null);
+ & ~DISPLAY_CHANGE_EVENTS, null,
+ /* isEventFilterExplicit */ true);
callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED);
waitForHandler();
Mockito.verifyZeroInteractions(mDisplayListener);
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
ALL_DISPLAY_EVENTS
- & ~DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED, null);
+ & ~DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED, null,
+ /* isEventFilterExplicit */ true);
callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
waitForHandler();
Mockito.verifyZeroInteractions(mDisplayListener);
@@ -218,11 +292,34 @@
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_DELAY_IMPLICIT_RR_REGISTRATION_UNTIL_RR_ACCESSED)
+ public void test_registerNativeRefreshRateCallbacks_enablesRRImplicitRegistrations()
+ throws RemoteException {
+ // Registering the display listener without supplied events doesn't subscribe to RR events
+ mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
+ ALL_DISPLAY_EVENTS, /* packageName= */ null,
+ /* isEventFilterExplicit */ false);
+ Mockito.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(), eq(ALL_DISPLAY_EVENTS));
+
+ // Native subscription for refresh rates is done
+ mDisplayManagerGlobal.registerNativeChoreographerForRefreshRateCallbacks();
+
+ // Registering the display listener without supplied events subscribe to RR events
+ mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
+ ALL_DISPLAY_EVENTS, /* packageName= */ null,
+ /* isEventFilterExplicit */ false);
+ Mockito.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(), eq(ALL_DISPLAY_EVENTS
+ | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE));
+ }
+
+ @Test
public void testDisplayManagerGlobalRegistersWithDisplayManager_WhenThereAreListeners()
throws RemoteException {
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED,
- null);
+ null, /* isEventFilterExplicit */ true);
InOrder inOrder = Mockito.inOrder(mDisplayManager);
inOrder.verify(mDisplayManager)
@@ -260,9 +357,10 @@
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
| DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED,
- null /* packageName */);
+ null /* packageName */, /* isEventFilterExplicit */ true);
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener2, mHandler,
- DISPLAY_CHANGE_EVENTS, null /* packageName */);
+ DISPLAY_CHANGE_EVENTS, null /* packageName */,
+ /* isEventFilterExplicit */ true);
mDisplayManagerGlobal.handleDisplayChangeFromWindowManager(321);
waitForHandler();
diff --git a/core/tests/coretests/src/android/os/BundleMergerTest.java b/core/tests/coretests/src/android/os/BundleMergerTest.java
index 43ed821..5379993 100644
--- a/core/tests/coretests/src/android/os/BundleMergerTest.java
+++ b/core/tests/coretests/src/android/os/BundleMergerTest.java
@@ -18,6 +18,7 @@
import static android.os.BundleMerger.STRATEGY_ARRAY_APPEND;
import static android.os.BundleMerger.STRATEGY_ARRAY_LIST_APPEND;
+import static android.os.BundleMerger.STRATEGY_ARRAY_UNION;
import static android.os.BundleMerger.STRATEGY_BOOLEAN_AND;
import static android.os.BundleMerger.STRATEGY_BOOLEAN_OR;
import static android.os.BundleMerger.STRATEGY_COMPARABLE_MAX;
@@ -28,6 +29,7 @@
import static android.os.BundleMerger.STRATEGY_NUMBER_INCREMENT_FIRST;
import static android.os.BundleMerger.STRATEGY_NUMBER_INCREMENT_FIRST_AND_ADD;
import static android.os.BundleMerger.STRATEGY_REJECT;
+import static android.os.BundleMerger.STRATEGY_STRING_APPEND;
import static android.os.BundleMerger.merge;
import static org.junit.Assert.assertArrayEquals;
@@ -204,6 +206,33 @@
}
@Test
+ public void testStrategyArrayUnion() throws Exception {
+ assertArrayEquals(new int[] {},
+ (int[]) merge(STRATEGY_ARRAY_UNION, new int[] {}, new int[] {}));
+ assertArrayEquals(new int[] {10},
+ (int[]) merge(STRATEGY_ARRAY_UNION, new int[] {10}, new int[] {}));
+ assertArrayEquals(new int[] {20},
+ (int[]) merge(STRATEGY_ARRAY_UNION, new int[] {}, new int[] {20}));
+ assertArrayEquals(new int[] {10, 20},
+ (int[]) merge(STRATEGY_ARRAY_UNION, new int[] {10}, new int[] {20}));
+ assertArrayEquals(new int[] {10, 20, 30, 40},
+ (int[]) merge(STRATEGY_ARRAY_UNION, new int[] {10, 30}, new int[] {20, 40}));
+ assertArrayEquals(new int[] {10, 20, 30},
+ (int[]) merge(STRATEGY_ARRAY_UNION, new int[] {10, 30}, new int[] {10, 20}));
+ assertArrayEquals(new int[] {10, 20},
+ (int[]) merge(STRATEGY_ARRAY_UNION, new int[] {10, 20}, new int[] {20}));
+ assertArrayEquals(new String[] {"a", "b"},
+ (String[]) merge(STRATEGY_ARRAY_UNION, new String[] {"a"}, new String[] {"b"}));
+ assertArrayEquals(new String[] {"a", "b", "c"},
+ (String[]) merge(STRATEGY_ARRAY_UNION, new String[] {"a", "b"},
+ new String[] {"b", "c"}));
+
+ assertThrows(Exception.class, () -> {
+ merge(STRATEGY_ARRAY_UNION, 10, 20);
+ });
+ }
+
+ @Test
public void testStrategyArrayListAppend() throws Exception {
assertEquals(arrayListOf(),
merge(STRATEGY_ARRAY_LIST_APPEND, arrayListOf(), arrayListOf()));
@@ -224,6 +253,18 @@
}
@Test
+ public void testStrategyStringAppend() throws Exception {
+ assertEquals("ab", merge(STRATEGY_STRING_APPEND, "a", "b"));
+ assertEquals("abc", merge(STRATEGY_STRING_APPEND, "a", "bc"));
+ assertEquals("abc", merge(STRATEGY_STRING_APPEND, "ab", "c"));
+ assertEquals("a,b,c,", merge(STRATEGY_STRING_APPEND, "a,", "b,c,"));
+
+ assertThrows(Exception.class, () -> {
+ merge(STRATEGY_STRING_APPEND, 10, 20);
+ });
+ }
+
+ @Test
public void testSetDefaultMergeStrategy() throws Exception {
final BundleMerger merger = new BundleMerger();
merger.setDefaultMergeStrategy(STRATEGY_FIRST);
diff --git a/core/tests/coretests/src/android/os/PerfettoTraceTest.java b/core/tests/coretests/src/android/os/PerfettoTraceTest.java
index fb743d2..6915015 100644
--- a/core/tests/coretests/src/android/os/PerfettoTraceTest.java
+++ b/core/tests/coretests/src/android/os/PerfettoTraceTest.java
@@ -108,8 +108,8 @@
PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
PerfettoTrace.instant(FOO_CATEGORY, "event")
- .addFlow(2)
- .addTerminatingFlow(3)
+ .setFlow(2)
+ .setTerminatingFlow(3)
.addArg("long_val", 10000000000L)
.addArg("bool_val", true)
.addArg("double_val", 3.14)
diff --git a/core/tests/coretests/src/android/text/AndroidCharacterTest.java b/core/tests/coretests/src/android/text/AndroidCharacterTest.java
index 1c5986a..819a5fb 100644
--- a/core/tests/coretests/src/android/text/AndroidCharacterTest.java
+++ b/core/tests/coretests/src/android/text/AndroidCharacterTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertArrayEquals;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -26,6 +27,7 @@
@Presubmit
@SmallTest
+@DisabledOnRavenwood(reason = "No need to make j.l.Character match behavior of AndroidCharacter")
public class AndroidCharacterTest {
@Test
diff --git a/core/tests/coretests/src/android/text/SpanColorsTest.java b/core/tests/coretests/src/android/text/SpanColorsTest.java
index d2cb8c1..4cdbd08 100644
--- a/core/tests/coretests/src/android/text/SpanColorsTest.java
+++ b/core/tests/coretests/src/android/text/SpanColorsTest.java
@@ -20,6 +20,7 @@
import android.graphics.Color;
import android.graphics.drawable.ShapeDrawable;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.Presubmit;
import android.text.style.ForegroundColorSpan;
import android.text.style.ImageSpan;
@@ -35,6 +36,7 @@
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
+@DisabledOnRavenwood(blockedBy = ShapeDrawable.class)
public class SpanColorsTest {
private final TextPaint mWorkPaint = new TextPaint();
private SpanColors mSpanColors;
diff --git a/core/tests/coretests/src/android/text/SpannableTest.java b/core/tests/coretests/src/android/text/SpannableTest.java
index a3e6a78..710d1e2 100644
--- a/core/tests/coretests/src/android/text/SpannableTest.java
+++ b/core/tests/coretests/src/android/text/SpannableTest.java
@@ -16,10 +16,10 @@
package android.text;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import android.platform.test.annotations.Presubmit;
-import android.test.MoreAsserts;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -50,13 +50,13 @@
// but other spans are not, unless the query region is empty, in
// in which case any abutting spans are returned.
spans = spannable.getSpans(0, 1, Object.class);
- MoreAsserts.assertEquals(new Object[]{emptySpan}, spans);
+ assertArrayEquals(new Object[]{emptySpan}, spans);
spans = spannable.getSpans(0, 2, Object.class);
- MoreAsserts.assertEquals(new Object[]{emptySpan, unemptySpan}, spans);
+ assertArrayEquals(new Object[]{emptySpan, unemptySpan}, spans);
spans = spannable.getSpans(1, 2, Object.class);
- MoreAsserts.assertEquals(new Object[]{emptySpan, unemptySpan}, spans);
+ assertArrayEquals(new Object[]{emptySpan, unemptySpan}, spans);
spans = spannable.getSpans(2, 2, Object.class);
- MoreAsserts.assertEquals(new Object[]{unemptySpan}, spans);
+ assertArrayEquals(new Object[]{unemptySpan}, spans);
}
@Test
diff --git a/core/tests/coretests/src/android/text/StaticLayoutTest.java b/core/tests/coretests/src/android/text/StaticLayoutTest.java
index 3541900..55f38b2 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutTest.java
@@ -25,6 +25,7 @@
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
import android.os.LocaleList;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.Presubmit;
import android.text.Layout.Alignment;
import android.text.method.EditorState;
@@ -726,6 +727,7 @@
}
@Test
+ @DisabledOnRavenwood(bug = 391342883)
public void testLocaleSpanAffectsHyphenation() {
TextPaint paint = new TextPaint();
paint.setTextLocale(Locale.US);
diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java
index f552265..e38c880 100644
--- a/core/tests/coretests/src/android/text/TextUtilsTest.java
+++ b/core/tests/coretests/src/android/text/TextUtilsTest.java
@@ -18,6 +18,7 @@
import static android.text.TextUtils.formatSimple;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -28,7 +29,6 @@
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
-import android.test.MoreAsserts;
import android.text.style.StyleSpan;
import android.text.util.Rfc822Token;
import android.text.util.Rfc822Tokenizer;
@@ -237,7 +237,7 @@
for (String s : splitter) {
strings.add(s);
}
- MoreAsserts.assertEquals(expectedStrings, strings.toArray(new String[]{}));
+ assertArrayEquals(expectedStrings, strings.toArray(new String[]{}));
}
@Test
diff --git a/core/tests/coretests/src/android/text/format/DateFormatTest.java b/core/tests/coretests/src/android/text/format/DateFormatTest.java
index 59af6dd..c16393c 100644
--- a/core/tests/coretests/src/android/text/format/DateFormatTest.java
+++ b/core/tests/coretests/src/android/text/format/DateFormatTest.java
@@ -74,8 +74,9 @@
DateFormatSymbols dfs = DateFormat.getIcuDateFormatSymbols(Locale.US);
assertEquals("AM", dfs.getAmPmStrings()[0]);
assertEquals("PM", dfs.getAmPmStrings()[1]);
- assertEquals("a", dfs.getAmpmNarrowStrings()[0]);
- assertEquals("p", dfs.getAmpmNarrowStrings()[1]);
+ // getAmpmNarrowStrings() is a @CorePlatformApi that we should stop using in framework
+ // assertEquals("a", dfs.getAmpmNarrowStrings()[0]);
+ // assertEquals("p", dfs.getAmpmNarrowStrings()[1]);
}
@Test
diff --git a/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java b/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
index a07d399..e542734 100644
--- a/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
+++ b/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
@@ -40,6 +40,7 @@
import android.icu.util.Calendar;
import android.icu.util.TimeZone;
import android.icu.util.ULocale;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.Presubmit;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -686,6 +687,7 @@
}
@Test
+ @DisabledOnRavenwood(bug = 391381043)
public void testIsLibcoreVFlagEnabled() {
// This flag has been fully ramped. It should never be false.
assertTrue(DateIntervalFormat.isLibcoreVFlagEnabled());
diff --git a/core/tests/coretests/src/android/text/format/DateUtilsTest.java b/core/tests/coretests/src/android/text/format/DateUtilsTest.java
index 47be893..a853d4a 100644
--- a/core/tests/coretests/src/android/text/format/DateUtilsTest.java
+++ b/core/tests/coretests/src/android/text/format/DateUtilsTest.java
@@ -21,6 +21,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.LocaleList;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.Presubmit;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -116,6 +117,7 @@
}
@Test
+ @DisabledOnRavenwood(reason = "DateFormat.set24HourTimePref is not available on host JVM")
public void testFormatSameDayTime() {
// This test assumes a default DateFormat.is24Hour setting.
DateFormat.set24HourTimePref(null);
diff --git a/core/tests/coretests/src/android/text/format/TimeMigrationUtilsTest.java b/core/tests/coretests/src/android/text/format/TimeMigrationUtilsTest.java
index c8cb5f3..49f3373 100644
--- a/core/tests/coretests/src/android/text/format/TimeMigrationUtilsTest.java
+++ b/core/tests/coretests/src/android/text/format/TimeMigrationUtilsTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.Presubmit;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -72,6 +73,7 @@
* Compares TimeMigrationUtils.formatSimpleDateTime() with the code it is replacing.
*/
@Test
+ @DisabledOnRavenwood(blockedBy = Time.class)
public void formatMillisAsDateTime_matchesOldBehavior() {
// A selection of interesting locales.
Locale[] locales = new Locale[] {
diff --git a/core/tests/coretests/src/android/text/format/TimeTest.java b/core/tests/coretests/src/android/text/format/TimeTest.java
index 6138ea1..29c5899 100644
--- a/core/tests/coretests/src/android/text/format/TimeTest.java
+++ b/core/tests/coretests/src/android/text/format/TimeTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.Presubmit;
import android.util.Log;
import android.util.TimeFormatException;
@@ -34,6 +35,7 @@
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
+@DisabledOnRavenwood(blockedBy = Time.class)
public class TimeTest {
@Test
diff --git a/core/tests/coretests/src/android/text/method/BackspaceTest.java b/core/tests/coretests/src/android/text/method/BackspaceTest.java
index a7ff244..646e8f9 100644
--- a/core/tests/coretests/src/android/text/method/BackspaceTest.java
+++ b/core/tests/coretests/src/android/text/method/BackspaceTest.java
@@ -16,6 +16,7 @@
package android.text.method;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.Presubmit;
import android.text.InputType;
import android.util.KeyUtils;
@@ -41,6 +42,7 @@
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
+@DisabledOnRavenwood(blockedBy = EditText.class)
public class BackspaceTest {
private EditText mTextView;
diff --git a/core/tests/coretests/src/android/text/method/EditorState.java b/core/tests/coretests/src/android/text/method/EditorState.java
index 4eff7a4..633fa11 100644
--- a/core/tests/coretests/src/android/text/method/EditorState.java
+++ b/core/tests/coretests/src/android/text/method/EditorState.java
@@ -16,7 +16,7 @@
package android.text.method;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -181,4 +181,3 @@
Assert.assertEquals(expected.mSelectionEnd, mSelectionEnd);
}
}
-
diff --git a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
index 1e4024d..8044fd7 100644
--- a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
+++ b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
@@ -16,6 +16,7 @@
package android.text.method;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.Presubmit;
import android.text.InputType;
import android.util.KeyUtils;
@@ -40,6 +41,7 @@
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
+@DisabledOnRavenwood(blockedBy = EditText.class)
public class ForwardDeleteTest {
private EditText mTextView;
diff --git a/core/tests/coretests/src/android/text/method/InsertModeTransformationMethodTest.java b/core/tests/coretests/src/android/text/method/InsertModeTransformationMethodTest.java
index e2c1902..37ad204 100644
--- a/core/tests/coretests/src/android/text/method/InsertModeTransformationMethodTest.java
+++ b/core/tests/coretests/src/android/text/method/InsertModeTransformationMethodTest.java
@@ -19,6 +19,7 @@
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -44,6 +45,7 @@
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
+@DisabledOnRavenwood(blockedBy = View.class)
public class InsertModeTransformationMethodTest {
private static View sView;
private static final String TEXT = "abc def";
diff --git a/core/tests/coretests/src/android/text/util/LinkifyTest.java b/core/tests/coretests/src/android/text/util/LinkifyTest.java
index 52f3b2e..98bdb0b 100644
--- a/core/tests/coretests/src/android/text/util/LinkifyTest.java
+++ b/core/tests/coretests/src/android/text/util/LinkifyTest.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.content.res.Configuration;
import android.os.LocaleList;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.method.LinkMovementMethod;
@@ -46,6 +47,7 @@
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
+@DisabledOnRavenwood(blockedBy = Linkify.class)
public class LinkifyTest {
private static final LocaleList LOCALE_LIST_US = new LocaleList(Locale.US);
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index e6361e1..6adceb9 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -230,7 +230,9 @@
new InsetsSourceControl(ID_STATUS_BAR, statusBars(), mLeash,
false /* initialVisible */, new Point(), Insets.NONE),
new int[1], hideTypes, new int[1], new int[1]);
- assertTrue(mRemoveSurfaceCalled);
+ if (!android.view.inputmethod.Flags.refactorInsetsController()) {
+ assertTrue(mRemoveSurfaceCalled);
+ }
assertEquals(0, hideTypes[0]);
});
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index 8b0d315..9110898 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
@@ -1065,6 +1066,22 @@
assertEquals(b1Memory + b2Memory, rv.estimateTotalBitmapMemoryUsage());
}
+ @Test
+ public void remoteResponse_FillInIntentNestedIntentKeysCollected() {
+ Intent fillInIntent = new Intent();
+ fillInIntent.putExtra("extraIntent", new Intent());
+ RemoteViews.RemoteResponse.fromFillInIntent(fillInIntent);
+ assertNotEquals(0, fillInIntent.getExtendedFlags()
+ & Intent.EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED);
+
+ fillInIntent = new Intent();
+ fillInIntent.putExtra("extraIntent", new Intent());
+ RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test);
+ rv.setOnClickFillInIntent(R.id.view, fillInIntent);
+ assertNotEquals(0, fillInIntent.getExtendedFlags()
+ & Intent.EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED);
+ }
+
private static LayoutInflater.Factory2 createLayoutInflaterFactory(String viewTypeToReplace,
View replacementView) {
return new LayoutInflater.Factory2() {
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
index 3239598..0ba2d85 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
@@ -23,6 +23,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.annotation.SuppressLint;
import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.ravenwood.RavenwoodRule;
import android.util.SparseArray;
@@ -287,6 +288,7 @@
0, lastUidCpuTimes.size());
}
+ @SuppressLint("CheckResult")
@Test
public void testAddDeltaFromBpf() {
LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 5);
diff --git a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
index 7e5d0a4..959e121 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertThrows;
+import android.annotation.SuppressLint;
import android.os.BadParcelableException;
import android.os.Parcel;
import android.platform.test.ravenwood.RavenwoodRule;
@@ -176,6 +177,7 @@
assertCounts(newCounter, 0, new long[]{116, 232, 364, 528});
}
+ @SuppressLint("CheckResult")
private void assertCounts(LongArrayMultiStateCounter counter, int state, long[] expected) {
long[] counts = new long[expected.length];
counter.getCounts(counts, state);
diff --git a/graphics/java/android/graphics/AvoidXfermode.java b/graphics/java/android/graphics/AvoidXfermode.java
index 683c157..5296ee8 100644
--- a/graphics/java/android/graphics/AvoidXfermode.java
+++ b/graphics/java/android/graphics/AvoidXfermode.java
@@ -23,6 +23,7 @@
* @removed
*/
@Deprecated
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AvoidXfermode extends Xfermode {
// these need to match the enum in AvoidXfermode.h on the native side
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 9b9be244..9f60534 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -26,6 +26,7 @@
/**
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class BLASTBufferQueue {
// Note: This field is accessed by native code.
public long mNativeObject; // BLASTBufferQueue*
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index a2a0f49..0ca58cc 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -48,6 +48,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class BaseCanvas {
/**
* Should only be assigned in constructors (or setBitmap if software canvas),
diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
index 5b1fa7b..0511bd1 100644
--- a/graphics/java/android/graphics/BaseRecordingCanvas.java
+++ b/graphics/java/android/graphics/BaseRecordingCanvas.java
@@ -44,6 +44,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class BaseRecordingCanvas extends Canvas {
public BaseRecordingCanvas(long nativeCanvas) {
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 0c4ea79..cd5a54c 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -51,6 +51,7 @@
import java.util.ArrayList;
import java.util.WeakHashMap;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class Bitmap implements Parcelable {
private static final String TAG = "Bitmap";
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 1c20141..a5535c8 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -41,6 +41,7 @@
* Creates Bitmap objects from various sources, including files, streams,
* and byte-arrays.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class BitmapFactory {
private static final int DECODE_BUFFER_SIZE = 16 * 1024;
diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java
index 29112af..9b3f715 100644
--- a/graphics/java/android/graphics/BitmapRegionDecoder.java
+++ b/graphics/java/android/graphics/BitmapRegionDecoder.java
@@ -37,6 +37,7 @@
* to get a decoded Bitmap of the specified region.
*
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class BitmapRegionDecoder {
private long mNativeBitmapRegionDecoder;
private boolean mRecycled;
diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java
index dcfff62..ac3543a 100644
--- a/graphics/java/android/graphics/BitmapShader.java
+++ b/graphics/java/android/graphics/BitmapShader.java
@@ -31,6 +31,7 @@
* Shader used to draw a bitmap as a texture. The bitmap can be repeated or
* mirrored by setting the tiling mode.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class BitmapShader extends Shader {
/**
* Prevent garbage collection.
diff --git a/graphics/java/android/graphics/BlendMode.java b/graphics/java/android/graphics/BlendMode.java
index c6ae680..c07af4e 100644
--- a/graphics/java/android/graphics/BlendMode.java
+++ b/graphics/java/android/graphics/BlendMode.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public enum BlendMode {
/**
diff --git a/graphics/java/android/graphics/BlendModeColorFilter.java b/graphics/java/android/graphics/BlendModeColorFilter.java
index d4e2373..d5dd0d3c 100644
--- a/graphics/java/android/graphics/BlendModeColorFilter.java
+++ b/graphics/java/android/graphics/BlendModeColorFilter.java
@@ -23,6 +23,7 @@
* A color filter that can be used to tint the source pixels using a single
* color and a specific {@link BlendMode}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class BlendModeColorFilter extends ColorFilter {
@ColorInt final int mColor;
diff --git a/graphics/java/android/graphics/BlurMaskFilter.java b/graphics/java/android/graphics/BlurMaskFilter.java
index f3064f8..22ed524 100644
--- a/graphics/java/android/graphics/BlurMaskFilter.java
+++ b/graphics/java/android/graphics/BlurMaskFilter.java
@@ -22,6 +22,7 @@
* inside, or straddles, the original mask's border, is controlled by the
* Blur enum.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class BlurMaskFilter extends MaskFilter {
public enum Blur {
diff --git a/graphics/java/android/graphics/Camera.java b/graphics/java/android/graphics/Camera.java
index 46640d7..27b695c 100644
--- a/graphics/java/android/graphics/Camera.java
+++ b/graphics/java/android/graphics/Camera.java
@@ -21,6 +21,7 @@
* generate a matrix that can be applied, for instance, on a
* {@link Canvas}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Camera {
/**
* Creates a new camera, with empty transformations.
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 28c2ca3..9137150b 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -54,6 +54,7 @@
* <a href="{@docRoot}guide/topics/graphics/2d-graphics.html">
* Canvas and Drawables</a> developer guide.</p></div>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Canvas extends BaseCanvas {
private static int sCompatibilityVersion = 0;
private static boolean sCompatibilityRestore = false;
diff --git a/graphics/java/android/graphics/CanvasProperty.java b/graphics/java/android/graphics/CanvasProperty.java
index e949584..755161d 100644
--- a/graphics/java/android/graphics/CanvasProperty.java
+++ b/graphics/java/android/graphics/CanvasProperty.java
@@ -25,6 +25,7 @@
* TODO: Make public?
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CanvasProperty<T> {
private VirtualRefBasePtr mProperty;
diff --git a/graphics/java/android/graphics/ColorFilter.java b/graphics/java/android/graphics/ColorFilter.java
index 7050325..918f26d 100644
--- a/graphics/java/android/graphics/ColorFilter.java
+++ b/graphics/java/android/graphics/ColorFilter.java
@@ -23,6 +23,7 @@
* each pixel drawn with that paint. This is an abstract class that should
* never be used directly.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ColorFilter {
private static class NoImagePreloadHolder {
diff --git a/graphics/java/android/graphics/ColorMatrixColorFilter.java b/graphics/java/android/graphics/ColorMatrixColorFilter.java
index bfdf318..cb78a83 100644
--- a/graphics/java/android/graphics/ColorMatrixColorFilter.java
+++ b/graphics/java/android/graphics/ColorMatrixColorFilter.java
@@ -27,6 +27,7 @@
*
* @see ColorMatrix
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ColorMatrixColorFilter extends ColorFilter {
@UnsupportedAppUsage
private final ColorMatrix mMatrix = new ColorMatrix();
diff --git a/graphics/java/android/graphics/Compatibility.java b/graphics/java/android/graphics/Compatibility.java
index 747fbf1..f89a4f7 100644
--- a/graphics/java/android/graphics/Compatibility.java
+++ b/graphics/java/android/graphics/Compatibility.java
@@ -21,6 +21,7 @@
* specified by the app.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class Compatibility {
private Compatibility() {}
diff --git a/graphics/java/android/graphics/ComposePathEffect.java b/graphics/java/android/graphics/ComposePathEffect.java
index 7d59ece..b380d2e 100644
--- a/graphics/java/android/graphics/ComposePathEffect.java
+++ b/graphics/java/android/graphics/ComposePathEffect.java
@@ -16,6 +16,7 @@
package android.graphics;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ComposePathEffect extends PathEffect {
/**
diff --git a/graphics/java/android/graphics/ComposeShader.java b/graphics/java/android/graphics/ComposeShader.java
index e714568..57a11d2 100644
--- a/graphics/java/android/graphics/ComposeShader.java
+++ b/graphics/java/android/graphics/ComposeShader.java
@@ -22,6 +22,7 @@
/** A subclass of shader that returns the composition of two other shaders, combined by
an {@link android.graphics.Xfermode} subclass.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ComposeShader extends Shader {
Shader mShaderA;
diff --git a/graphics/java/android/graphics/CornerPathEffect.java b/graphics/java/android/graphics/CornerPathEffect.java
index 8f4d7d9..37f0d29 100644
--- a/graphics/java/android/graphics/CornerPathEffect.java
+++ b/graphics/java/android/graphics/CornerPathEffect.java
@@ -16,6 +16,7 @@
package android.graphics;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class CornerPathEffect extends PathEffect {
/**
diff --git a/graphics/java/android/graphics/DashPathEffect.java b/graphics/java/android/graphics/DashPathEffect.java
index ef3ebe8..ff3c376 100644
--- a/graphics/java/android/graphics/DashPathEffect.java
+++ b/graphics/java/android/graphics/DashPathEffect.java
@@ -16,6 +16,7 @@
package android.graphics;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class DashPathEffect extends PathEffect {
/**
diff --git a/graphics/java/android/graphics/DiscretePathEffect.java b/graphics/java/android/graphics/DiscretePathEffect.java
index 3b3c9c9..77f9845 100644
--- a/graphics/java/android/graphics/DiscretePathEffect.java
+++ b/graphics/java/android/graphics/DiscretePathEffect.java
@@ -16,6 +16,7 @@
package android.graphics;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class DiscretePathEffect extends PathEffect {
/**
diff --git a/graphics/java/android/graphics/DrawFilter.java b/graphics/java/android/graphics/DrawFilter.java
index c7fdcb2..505a830 100644
--- a/graphics/java/android/graphics/DrawFilter.java
+++ b/graphics/java/android/graphics/DrawFilter.java
@@ -22,6 +22,7 @@
* can disable/enable antialiasing, or change the color for everything this is
* drawn.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class DrawFilter {
/**
diff --git a/graphics/java/android/graphics/EmbossMaskFilter.java b/graphics/java/android/graphics/EmbossMaskFilter.java
index 003678a..f0a661f 100644
--- a/graphics/java/android/graphics/EmbossMaskFilter.java
+++ b/graphics/java/android/graphics/EmbossMaskFilter.java
@@ -16,6 +16,7 @@
package android.graphics;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class EmbossMaskFilter extends MaskFilter {
/**
* Create an emboss maskfilter
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index 88f0e8e..09022ee 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -41,6 +41,7 @@
* @deprecated Use {@link android.graphics.fonts.FontFamily} instead.
*/
@Deprecated
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class FontFamily {
private static String TAG = "FontFamily";
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 13c4a94..8d0f128 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -48,6 +48,7 @@
* Parser for font config files.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class FontListParser {
private static final String TAG = "FontListParser";
diff --git a/graphics/java/android/graphics/ForceDarkType.java b/graphics/java/android/graphics/ForceDarkType.java
index 396b037..d21aef3 100644
--- a/graphics/java/android/graphics/ForceDarkType.java
+++ b/graphics/java/android/graphics/ForceDarkType.java
@@ -29,6 +29,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ForceDarkType {
/**
* Force dark disabled: normal, default operation.
diff --git a/graphics/java/android/graphics/FrameInfo.java b/graphics/java/android/graphics/FrameInfo.java
index 3b8f466..5202138 100644
--- a/graphics/java/android/graphics/FrameInfo.java
+++ b/graphics/java/android/graphics/FrameInfo.java
@@ -38,6 +38,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class FrameInfo {
public long[] frameInfo = new long[FRAME_INFO_SIZE];
diff --git a/graphics/java/android/graphics/Gainmap.java b/graphics/java/android/graphics/Gainmap.java
index 63ca3b8..7fc13db 100644
--- a/graphics/java/android/graphics/Gainmap.java
+++ b/graphics/java/android/graphics/Gainmap.java
@@ -86,6 +86,7 @@
* for these functions cancels out and does not affect the result, so other bases may be used
* if preferred.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class Gainmap implements Parcelable {
/** @hide */
diff --git a/graphics/java/android/graphics/GraphicBuffer.java b/graphics/java/android/graphics/GraphicBuffer.java
index 6705b25..4982851 100644
--- a/graphics/java/android/graphics/GraphicBuffer.java
+++ b/graphics/java/android/graphics/GraphicBuffer.java
@@ -28,6 +28,7 @@
* @hide
*/
@SuppressWarnings("UnusedDeclaration")
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class GraphicBuffer implements Parcelable {
// Note: keep usage flags in sync with GraphicBuffer.h and gralloc.h
public static final int USAGE_SW_READ_NEVER = 0x0;
diff --git a/graphics/java/android/graphics/GraphicsProtos.java b/graphics/java/android/graphics/GraphicsProtos.java
index 6bc41d3..fa7eaf9 100644
--- a/graphics/java/android/graphics/GraphicsProtos.java
+++ b/graphics/java/android/graphics/GraphicsProtos.java
@@ -24,6 +24,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class GraphicsProtos {
/** GraphicsProtos can never be an instance */
private GraphicsProtos() {}
diff --git a/graphics/java/android/graphics/GraphicsStatsService.java b/graphics/java/android/graphics/GraphicsStatsService.java
index 7a012bc..d0b9998 100644
--- a/graphics/java/android/graphics/GraphicsStatsService.java
+++ b/graphics/java/android/graphics/GraphicsStatsService.java
@@ -74,6 +74,7 @@
* for the process to use.
*
* @hide */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class GraphicsStatsService extends IGraphicsStats.Stub {
public static final String GRAPHICS_STATS_SERVICE = "graphicsstats";
diff --git a/graphics/java/android/graphics/HardwareBufferRenderer.java b/graphics/java/android/graphics/HardwareBufferRenderer.java
index e04f13c..8179870 100644
--- a/graphics/java/android/graphics/HardwareBufferRenderer.java
+++ b/graphics/java/android/graphics/HardwareBufferRenderer.java
@@ -55,6 +55,7 @@
* HardwareBufferRenderer will never clear contents before each draw invocation so previous contents
* in the {@link HardwareBuffer} target will be preserved across renders.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class HardwareBufferRenderer implements AutoCloseable {
private static final ColorSpace DEFAULT_COLORSPACE = ColorSpace.get(Named.SRGB);
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index ef6b728..3444f84 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -79,6 +79,7 @@
* Failure to do so will cause the render thread to stall on that surface, blocking all
* HardwareRenderer instances.</p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class HardwareRenderer {
private static final String LOG_TAG = "HardwareRenderer";
diff --git a/graphics/java/android/graphics/HardwareRendererObserver.java b/graphics/java/android/graphics/HardwareRendererObserver.java
index d5a6a2f..9926378 100644
--- a/graphics/java/android/graphics/HardwareRendererObserver.java
+++ b/graphics/java/android/graphics/HardwareRendererObserver.java
@@ -28,6 +28,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class HardwareRendererObserver {
private final long[] mFrameMetrics;
private final Handler mHandler;
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 6395179..419929a 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -44,6 +44,7 @@
import android.net.Uri;
import android.os.Build;
import android.os.Trace;
+import android.ravenwood.annotation.RavenwoodIgnore;
import android.system.ErrnoException;
import android.system.Os;
import android.util.DisplayMetrics;
@@ -173,6 +174,7 @@
* });
* </pre>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class ImageDecoder implements AutoCloseable {
/**
* Source of encoded image data.
@@ -1987,6 +1989,7 @@
* Check if HEVC decoder is supported by the device.
*/
@SuppressWarnings("AndroidFrameworkCompatChange")
+ @RavenwoodIgnore(blockedBy = MediaCodecList.class)
private static boolean isHevcDecoderSupported() {
synchronized (sIsHevcDecoderSupportedLock) {
if (sIsHevcDecoderSupportedInitialized) {
@@ -2010,6 +2013,7 @@
* Checks if the device supports decoding 10-bit AV1.
*/
@SuppressWarnings("AndroidFrameworkCompatChange") // This is not an app-visible API.
+ @RavenwoodIgnore(blockedBy = MediaCodecList.class)
private static boolean isP010SupportedForAV1() {
synchronized (sIsP010SupportedLock) {
if (sIsP010SupportedFlagsInitialized) {
@@ -2025,6 +2029,7 @@
* This method is called by JNI.
*/
@SuppressWarnings("unused")
+ @RavenwoodIgnore(blockedBy = MediaCodecList.class)
private static boolean isP010SupportedForHEVC() {
synchronized (sIsP010SupportedLock) {
if (sIsP010SupportedFlagsInitialized) {
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index b4899f9..4c9f5ac 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -24,6 +24,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ImageFormat {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
diff --git a/graphics/java/android/graphics/LayerRasterizer.java b/graphics/java/android/graphics/LayerRasterizer.java
index 25155ab..1a44248 100644
--- a/graphics/java/android/graphics/LayerRasterizer.java
+++ b/graphics/java/android/graphics/LayerRasterizer.java
@@ -20,6 +20,7 @@
* @removed feature is not supported by hw-accerlerated or PDF backends
*/
@Deprecated
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class LayerRasterizer extends Rasterizer {
public LayerRasterizer() { }
diff --git a/graphics/java/android/graphics/LeakyTypefaceStorage.java b/graphics/java/android/graphics/LeakyTypefaceStorage.java
index 618e60d..25a84369 100644
--- a/graphics/java/android/graphics/LeakyTypefaceStorage.java
+++ b/graphics/java/android/graphics/LeakyTypefaceStorage.java
@@ -32,6 +32,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class LeakyTypefaceStorage {
private static final Object sLock = new Object();
diff --git a/graphics/java/android/graphics/LightingColorFilter.java b/graphics/java/android/graphics/LightingColorFilter.java
index fe73a1a..1afdc77 100644
--- a/graphics/java/android/graphics/LightingColorFilter.java
+++ b/graphics/java/android/graphics/LightingColorFilter.java
@@ -40,6 +40,7 @@
* </pre>
* The result is pinned to the <code>[0..255]</code> range for each channel.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class LightingColorFilter extends ColorFilter {
@ColorInt
private int mMul;
diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java
index 0879371..c6566c9 100644
--- a/graphics/java/android/graphics/LinearGradient.java
+++ b/graphics/java/android/graphics/LinearGradient.java
@@ -24,6 +24,7 @@
import android.os.Build;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class LinearGradient extends Shader {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private float mX0;
diff --git a/graphics/java/android/graphics/MaskFilter.java b/graphics/java/android/graphics/MaskFilter.java
index d474315..b490650 100644
--- a/graphics/java/android/graphics/MaskFilter.java
+++ b/graphics/java/android/graphics/MaskFilter.java
@@ -21,6 +21,7 @@
* an alpha-channel mask before drawing it. A subclass of MaskFilter may be
* installed into a Paint. Blur and emboss are implemented as subclasses of MaskFilter.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class MaskFilter {
protected void finalize() throws Throwable {
diff --git a/graphics/java/android/graphics/Mesh.java b/graphics/java/android/graphics/Mesh.java
index 6be8332..f4d841b 100644
--- a/graphics/java/android/graphics/Mesh.java
+++ b/graphics/java/android/graphics/Mesh.java
@@ -37,6 +37,7 @@
* for the mesh. Once generated, a mesh object can be drawn through
* {@link Canvas#drawMesh(Mesh, BlendMode, Paint)}
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Mesh {
private long mNativeMeshWrapper;
private boolean mIsIndexed;
diff --git a/graphics/java/android/graphics/MeshSpecification.java b/graphics/java/android/graphics/MeshSpecification.java
index b1aae7f..9c7e948 100644
--- a/graphics/java/android/graphics/MeshSpecification.java
+++ b/graphics/java/android/graphics/MeshSpecification.java
@@ -72,6 +72,7 @@
* These should be kept in mind when generating a mesh specification, as exceeding them will
* lead to errors.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class MeshSpecification {
long mNativeMeshSpec;
diff --git a/graphics/java/android/graphics/Movie.java b/graphics/java/android/graphics/Movie.java
index 9c9535d..cefe391 100644
--- a/graphics/java/android/graphics/Movie.java
+++ b/graphics/java/android/graphics/Movie.java
@@ -27,6 +27,7 @@
* @deprecated Prefer {@link android.graphics.drawable.AnimatedImageDrawable}.
*/
@Deprecated
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Movie {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private long mNativeMovie;
diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java
index 382269f..00df23f 100644
--- a/graphics/java/android/graphics/NinePatch.java
+++ b/graphics/java/android/graphics/NinePatch.java
@@ -32,6 +32,7 @@
* using a WYSIWYG graphics editor.
* </p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class NinePatch {
/**
* Struct of inset information attached to a 9 patch bitmap.
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 3d4dccf..a0ca098 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -66,6 +66,7 @@
* The Paint class holds the style and color information about how to draw
* geometries, text and bitmaps.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Paint {
private static final String TAG = "Paint";
diff --git a/graphics/java/android/graphics/PaintFlagsDrawFilter.java b/graphics/java/android/graphics/PaintFlagsDrawFilter.java
index 2326611..f4c49b1 100644
--- a/graphics/java/android/graphics/PaintFlagsDrawFilter.java
+++ b/graphics/java/android/graphics/PaintFlagsDrawFilter.java
@@ -16,6 +16,7 @@
package android.graphics;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PaintFlagsDrawFilter extends DrawFilter {
/**
* Subclass of DrawFilter that affects every paint by first clearing
diff --git a/graphics/java/android/graphics/PathDashPathEffect.java b/graphics/java/android/graphics/PathDashPathEffect.java
index 2b6a6ed..dc92e6c 100644
--- a/graphics/java/android/graphics/PathDashPathEffect.java
+++ b/graphics/java/android/graphics/PathDashPathEffect.java
@@ -16,6 +16,7 @@
package android.graphics;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PathDashPathEffect extends PathEffect {
public enum Style {
diff --git a/graphics/java/android/graphics/PathEffect.java b/graphics/java/android/graphics/PathEffect.java
index 3292501..9bb7193 100644
--- a/graphics/java/android/graphics/PathEffect.java
+++ b/graphics/java/android/graphics/PathEffect.java
@@ -21,6 +21,7 @@
* the geometry of a drawing primitive before it is transformed by the
* canvas' matrix and drawn.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PathEffect {
protected void finalize() throws Throwable {
diff --git a/graphics/java/android/graphics/PathIterator.java b/graphics/java/android/graphics/PathIterator.java
index d7caabf..1ed70d0 100644
--- a/graphics/java/android/graphics/PathIterator.java
+++ b/graphics/java/android/graphics/PathIterator.java
@@ -34,6 +34,7 @@
* <code>PathIterator</code> can be used to query a given {@link Path} object, to discover its
* operations and point values.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PathIterator implements Iterator<PathIterator.Segment> {
private final float[] mPointsArray;
@@ -47,9 +48,11 @@
private static final boolean IS_DALVIK = "dalvik".equalsIgnoreCase(
System.getProperty("java.vm.name"));
- private static final NativeAllocationRegistry sRegistry =
- NativeAllocationRegistry.createMalloced(
- PathIterator.class.getClassLoader(), nGetFinalizer());
+ private static class NoImagePreloadHolder {
+ private static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ PathIterator.class.getClassLoader(), nGetFinalizer());
+ }
/**
* The <code>Verb</code> indicates the operation for a given segment of a path. These
@@ -69,6 +72,11 @@
public static final int VERB_CLOSE = 5;
public static final int VERB_DONE = 6;
+
+ static {
+ // Keep <cinit> exist in bytecode
+ }
+
/**
* Returns a {@link PathIterator} object for this path, which can be used to query the
* data (operations and points) in the path. Iterators can only be used on Path objects
@@ -90,7 +98,7 @@
mPointsArray = new float[POINT_ARRAY_SIZE];
mPointsAddress = 0;
}
- sRegistry.registerNativeAllocation(this, mNativeIterator);
+ NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativeIterator);
}
/**
diff --git a/graphics/java/android/graphics/PathMeasure.java b/graphics/java/android/graphics/PathMeasure.java
index 2c6cfa5..4d123db 100644
--- a/graphics/java/android/graphics/PathMeasure.java
+++ b/graphics/java/android/graphics/PathMeasure.java
@@ -16,6 +16,7 @@
package android.graphics;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PathMeasure {
private Path mPath;
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index ee4165b..54eb2bc 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -33,6 +33,7 @@
* <p class="note"><strong>Note:</strong> Prior to API level 23 a picture cannot
* be replayed on a hardware accelerated canvas.</p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Picture {
private PictureCanvas mRecordingCanvas;
// TODO: Figure out if this was a false-positive
diff --git a/graphics/java/android/graphics/PixelXorXfermode.java b/graphics/java/android/graphics/PixelXorXfermode.java
index 27884e0..6427852 100644
--- a/graphics/java/android/graphics/PixelXorXfermode.java
+++ b/graphics/java/android/graphics/PixelXorXfermode.java
@@ -20,6 +20,7 @@
* @removed
*/
@Deprecated
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PixelXorXfermode extends Xfermode {
public PixelXorXfermode(int opColor) {
diff --git a/graphics/java/android/graphics/PorterDuff.java b/graphics/java/android/graphics/PorterDuff.java
index eb940e2..730a804 100644
--- a/graphics/java/android/graphics/PorterDuff.java
+++ b/graphics/java/android/graphics/PorterDuff.java
@@ -26,6 +26,7 @@
*
* Consider using {@link BlendMode} instead as it provides a wider variety of tinting options
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PorterDuff {
/**
* {@usesMathJax}
diff --git a/graphics/java/android/graphics/PorterDuffColorFilter.java b/graphics/java/android/graphics/PorterDuffColorFilter.java
index 0700f21..777ef6c 100644
--- a/graphics/java/android/graphics/PorterDuffColorFilter.java
+++ b/graphics/java/android/graphics/PorterDuffColorFilter.java
@@ -25,6 +25,7 @@
* A color filter that can be used to tint the source pixels using a single
* color and a specific {@link PorterDuff Porter-Duff composite mode}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PorterDuffColorFilter extends ColorFilter {
@ColorInt
private int mColor;
diff --git a/graphics/java/android/graphics/PorterDuffXfermode.java b/graphics/java/android/graphics/PorterDuffXfermode.java
index 83d0507..e10d7370 100644
--- a/graphics/java/android/graphics/PorterDuffXfermode.java
+++ b/graphics/java/android/graphics/PorterDuffXfermode.java
@@ -23,6 +23,7 @@
* information on the available alpha compositing and blending modes.</p>
*
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PorterDuffXfermode extends Xfermode {
/**
* Create an xfermode that uses the specified porter-duff mode.
diff --git a/graphics/java/android/graphics/PostProcessor.java b/graphics/java/android/graphics/PostProcessor.java
index 6fed39b..066214a 100644
--- a/graphics/java/android/graphics/PostProcessor.java
+++ b/graphics/java/android/graphics/PostProcessor.java
@@ -37,6 +37,7 @@
*
* <p>Supplied to ImageDecoder via {@link ImageDecoder#setPostProcessor setPostProcessor}.</p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface PostProcessor {
/**
* Do any processing after (for example) decoding.
diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java
index e582e66..06e92ea 100644
--- a/graphics/java/android/graphics/RadialGradient.java
+++ b/graphics/java/android/graphics/RadialGradient.java
@@ -24,6 +24,7 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class RadialGradient extends Shader {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private float mX;
diff --git a/graphics/java/android/graphics/Rasterizer.java b/graphics/java/android/graphics/Rasterizer.java
index 5750954..5e67da5 100644
--- a/graphics/java/android/graphics/Rasterizer.java
+++ b/graphics/java/android/graphics/Rasterizer.java
@@ -24,6 +24,7 @@
/**
* @removed feature is not supported by hw-accerlerated or PDF backends
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Rasterizer {
protected void finalize() throws Throwable { }
diff --git a/graphics/java/android/graphics/RecordingCanvas.java b/graphics/java/android/graphics/RecordingCanvas.java
index cc5b3b9..a56f461 100644
--- a/graphics/java/android/graphics/RecordingCanvas.java
+++ b/graphics/java/android/graphics/RecordingCanvas.java
@@ -33,6 +33,7 @@
* {@link RenderNode#endRecording()} is called. It must not be retained beyond that as it is
* internally reused.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class RecordingCanvas extends BaseRecordingCanvas {
// The recording canvas pool should be large enough to handle a deeply nested
// view hierarchy because display lists are generated recursively.
diff --git a/graphics/java/android/graphics/Region.java b/graphics/java/android/graphics/Region.java
index 2970873..e2215d4 100644
--- a/graphics/java/android/graphics/Region.java
+++ b/graphics/java/android/graphics/Region.java
@@ -23,6 +23,7 @@
import android.os.Parcelable;
import android.util.Pools.SynchronizedPool;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Region implements Parcelable {
private static final int MAX_POOL_SIZE = 10;
diff --git a/graphics/java/android/graphics/RegionIterator.java b/graphics/java/android/graphics/RegionIterator.java
index 443b23c..5d74487 100644
--- a/graphics/java/android/graphics/RegionIterator.java
+++ b/graphics/java/android/graphics/RegionIterator.java
@@ -16,6 +16,7 @@
package android.graphics;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class RegionIterator {
/**
diff --git a/graphics/java/android/graphics/RenderEffect.java b/graphics/java/android/graphics/RenderEffect.java
index b8a4685..06bfb82 100644
--- a/graphics/java/android/graphics/RenderEffect.java
+++ b/graphics/java/android/graphics/RenderEffect.java
@@ -30,6 +30,7 @@
* Additionally a {@link RenderEffect} can be applied to a View's backing RenderNode through
* {@link android.view.View#setRenderEffect(RenderEffect)}
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class RenderEffect {
private static class RenderEffectHolder {
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 03a8b30..fa41876 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -192,6 +192,7 @@
* top-level content is desired, and finally calling {@link Surface#unlockCanvasAndPost(Canvas)}.
* </p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class RenderNode {
// Use a Holder to allow static initialization in the boot image.
diff --git a/graphics/java/android/graphics/RuntimeColorFilter.java b/graphics/java/android/graphics/RuntimeColorFilter.java
index a64acfe..06aecc3 100644
--- a/graphics/java/android/graphics/RuntimeColorFilter.java
+++ b/graphics/java/android/graphics/RuntimeColorFilter.java
@@ -37,6 +37,7 @@
* </pre>
*/
@FlaggedApi(Flags.FLAG_RUNTIME_COLOR_FILTERS_BLENDERS)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class RuntimeColorFilter extends ColorFilter {
private String mAgsl;
diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java
index db2376e..6464f72 100644
--- a/graphics/java/android/graphics/RuntimeShader.java
+++ b/graphics/java/android/graphics/RuntimeShader.java
@@ -248,6 +248,7 @@
* the bitmap), remember that the coordinates are local to the canvas.</p>
*
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class RuntimeShader extends Shader {
private static class NoImagePreloadHolder {
diff --git a/graphics/java/android/graphics/RuntimeXfermode.java b/graphics/java/android/graphics/RuntimeXfermode.java
index c8a0b1a..1e20bd3 100644
--- a/graphics/java/android/graphics/RuntimeXfermode.java
+++ b/graphics/java/android/graphics/RuntimeXfermode.java
@@ -39,6 +39,7 @@
* </pre>
*/
@FlaggedApi(Flags.FLAG_RUNTIME_COLOR_FILTERS_BLENDERS)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class RuntimeXfermode extends Xfermode {
private static class NoImagePreloadHolder {
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index 4d6bead..369fab4 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -29,6 +29,7 @@
* paint.setShader(shader). After that any object (other than a bitmap) that is
* drawn with that paint will get its color(s) from the shader.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Shader {
private static class NoImagePreloadHolder {
diff --git a/graphics/java/android/graphics/SumPathEffect.java b/graphics/java/android/graphics/SumPathEffect.java
index 8fedc31..3543e10 100644
--- a/graphics/java/android/graphics/SumPathEffect.java
+++ b/graphics/java/android/graphics/SumPathEffect.java
@@ -16,6 +16,7 @@
package android.graphics;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SumPathEffect extends PathEffect {
/**
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index 5caedba..df384ea 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -78,6 +78,7 @@
* frame-available callback is called on an arbitrary thread, so unless special care is taken {@link
* #updateTexImage} should not be called directly from the callback.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SurfaceTexture {
private final Looper mCreatorLooper;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
diff --git a/graphics/java/android/graphics/SweepGradient.java b/graphics/java/android/graphics/SweepGradient.java
index 3a29395..9421925 100644
--- a/graphics/java/android/graphics/SweepGradient.java
+++ b/graphics/java/android/graphics/SweepGradient.java
@@ -23,6 +23,7 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SweepGradient extends Shader {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private float mCx;
diff --git a/graphics/java/android/graphics/TableMaskFilter.java b/graphics/java/android/graphics/TableMaskFilter.java
index 204f970..ca7627c 100644
--- a/graphics/java/android/graphics/TableMaskFilter.java
+++ b/graphics/java/android/graphics/TableMaskFilter.java
@@ -21,6 +21,7 @@
/**
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TableMaskFilter extends MaskFilter {
public TableMaskFilter(byte[] table) {
diff --git a/graphics/java/android/graphics/TemporaryBuffer.java b/graphics/java/android/graphics/TemporaryBuffer.java
index ef3f7f7..681c48e 100644
--- a/graphics/java/android/graphics/TemporaryBuffer.java
+++ b/graphics/java/android/graphics/TemporaryBuffer.java
@@ -23,6 +23,7 @@
/**
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TemporaryBuffer {
@UnsupportedAppUsage
public static char[] obtain(int len) {
diff --git a/graphics/java/android/graphics/TextureLayer.java b/graphics/java/android/graphics/TextureLayer.java
index ac1bd69..981b78a 100644
--- a/graphics/java/android/graphics/TextureLayer.java
+++ b/graphics/java/android/graphics/TextureLayer.java
@@ -29,6 +29,7 @@
*
* @hide TODO: Make this a SystemApi for b/155905258
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class TextureLayer implements AutoCloseable {
private HardwareRenderer mRenderer;
private VirtualRefBasePtr mFinalizer;
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 874b847..d1aca34 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -42,6 +42,7 @@
import android.os.Trace;
import android.provider.FontRequest;
import android.provider.FontsContract;
+import android.ravenwood.annotation.RavenwoodReplace;
import android.system.ErrnoException;
import android.system.OsConstants;
import android.text.FontConfig;
@@ -86,6 +87,7 @@
* textSize, textSkewX, textScaleX to specify
* how text appears when drawn (and measured).
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Typeface {
private static String TAG = "Typeface";
@@ -93,9 +95,11 @@
/** @hide */
public static final boolean ENABLE_LAZY_TYPEFACE_INITIALIZATION = true;
- private static final NativeAllocationRegistry sRegistry =
- NativeAllocationRegistry.createMalloced(
- Typeface.class.getClassLoader(), nativeGetReleaseFunc());
+ private static class NoImagePreloadHolder {
+ static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ Typeface.class.getClassLoader(), nativeGetReleaseFunc());
+ }
/** The default NORMAL typeface object */
public static final Typeface DEFAULT = null;
@@ -1284,7 +1288,7 @@
}
native_instance = ni;
- mCleaner = sRegistry.registerNativeAllocation(this, native_instance);
+ mCleaner = NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, native_instance);
mStyle = nativeGetStyle(ni);
mWeight = nativeGetWeight(ni);
mIsVariationInstance = nativeIsVariationInstance(ni);
@@ -1560,9 +1564,23 @@
}
static {
+ staticInitializer();
+ }
+
+ @RavenwoodReplace(reason = "Prevent circular reference on host side JVM", bug = 337329128)
+ private static void staticInitializer() {
+ init();
+ }
+
+ private static void staticInitializer$ravenwood() {
+ /* no-op */
+ }
+
+ /** @hide */
+ public static void init() {
// Preload Roboto-Regular.ttf in Zygote for improving app launch performance.
- preloadFontFile("/system/fonts/Roboto-Regular.ttf");
- preloadFontFile("/system/fonts/RobotoStatic-Regular.ttf");
+ preloadFontFile(SystemFonts.SYSTEM_FONT_DIR + "Roboto-Regular.ttf");
+ preloadFontFile(SystemFonts.SYSTEM_FONT_DIR + "RobotoStatic-Regular.ttf");
String locale = SystemProperties.get("persist.sys.locale", "en-US");
String script = ULocale.addLikelySubtags(ULocale.forLanguageTag(locale)).getScript();
@@ -1642,6 +1660,21 @@
setSystemFontMap(typefaceMap);
}
+ /**
+ * {@link #loadPreinstalledSystemFontMap()} does not actually initialize the native
+ * system font APIs. Add a new method to actually load the font files without going
+ * through SharedMemory.
+ *
+ * @hide
+ */
+ public static void loadNativeSystemFonts() {
+ synchronized (SYSTEM_FONT_MAP_LOCK) {
+ for (var type : sSystemFontMap.values()) {
+ nativeAddFontCollections(type.native_instance);
+ }
+ }
+ }
+
static {
if (!ENABLE_LAZY_TYPEFACE_INITIALIZATION) {
loadPreinstalledSystemFontMap();
diff --git a/graphics/java/android/graphics/Xfermode.java b/graphics/java/android/graphics/Xfermode.java
index fb689e4..eda9e3c 100644
--- a/graphics/java/android/graphics/Xfermode.java
+++ b/graphics/java/android/graphics/Xfermode.java
@@ -28,4 +28,5 @@
* specified in the Modes enum. When an Xfermode is assigned to a Paint, then
* objects drawn with that paint have the xfermode applied.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Xfermode {}
diff --git a/graphics/java/android/graphics/YuvImage.java b/graphics/java/android/graphics/YuvImage.java
index b0c7f20..2b7f404 100644
--- a/graphics/java/android/graphics/YuvImage.java
+++ b/graphics/java/android/graphics/YuvImage.java
@@ -32,6 +32,7 @@
* To compress a rectangle region in the YUV data, users have to specify the
* region by left, top, width and height.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class YuvImage {
/**
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index 2893177..8be3400 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -44,6 +44,7 @@
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.Collections;
@@ -54,6 +55,7 @@
/**
* A font class can be used for creating FontFamily.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class Font {
private static final String TAG = "Font";
@@ -293,7 +295,14 @@
int capacity = assetStream.available();
ByteBuffer buffer = ByteBuffer.allocateDirect(capacity);
buffer.order(ByteOrder.nativeOrder());
- assetStream.read(buffer.array(), buffer.arrayOffset(), assetStream.available());
+ if (buffer.hasArray()) {
+ assetStream.read(buffer.array(), buffer.arrayOffset(), assetStream.available());
+ } else {
+ // Direct buffer does not have a backing array on Ravenwood,
+ // wrap it with a channel and read from it
+ var ch = Channels.newChannel(assetStream);
+ ch.read(buffer.duplicate());
+ }
if (assetStream.read() != -1) {
throw new IOException("Unable to access full contents of " + path);
diff --git a/graphics/java/android/graphics/fonts/FontCustomizationParser.java b/graphics/java/android/graphics/fonts/FontCustomizationParser.java
index b7bf055..732a5f3 100644
--- a/graphics/java/android/graphics/fonts/FontCustomizationParser.java
+++ b/graphics/java/android/graphics/fonts/FontCustomizationParser.java
@@ -43,6 +43,7 @@
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class FontCustomizationParser {
private static final String TAG = "FontCustomizationParser";
diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java
index 5a7b0bb..0ab4639 100644
--- a/graphics/java/android/graphics/fonts/FontFamily.java
+++ b/graphics/java/android/graphics/fonts/FontFamily.java
@@ -66,6 +66,7 @@
* </p>
*
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class FontFamily {
private static final String TAG = "FontFamily";
diff --git a/graphics/java/android/graphics/fonts/FontFileUtil.java b/graphics/java/android/graphics/fonts/FontFileUtil.java
index abcafb6..305ab3b 100644
--- a/graphics/java/android/graphics/fonts/FontFileUtil.java
+++ b/graphics/java/android/graphics/fonts/FontFileUtil.java
@@ -32,6 +32,7 @@
* Provides a utility for font file operations.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class FontFileUtil {
private FontFileUtil() {} // Do not instantiate
diff --git a/graphics/java/android/graphics/fonts/FontStyle.java b/graphics/java/android/graphics/fonts/FontStyle.java
index 48969aa..b3ddbed 100644
--- a/graphics/java/android/graphics/fonts/FontStyle.java
+++ b/graphics/java/android/graphics/fonts/FontStyle.java
@@ -44,6 +44,7 @@
* </p>
*
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class FontStyle {
private static final String TAG = "FontStyle";
diff --git a/graphics/java/android/graphics/fonts/FontVariationAxis.java b/graphics/java/android/graphics/fonts/FontVariationAxis.java
index 30a248b..1d71594 100644
--- a/graphics/java/android/graphics/fonts/FontVariationAxis.java
+++ b/graphics/java/android/graphics/fonts/FontVariationAxis.java
@@ -31,6 +31,7 @@
/**
* Class that holds information about single font variation axis.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class FontVariationAxis {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final int mTag;
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index 0e25c34..599c426 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -25,6 +25,7 @@
import android.graphics.FontListParser;
import android.graphics.Typeface;
import android.os.LocaleList;
+import android.ravenwood.annotation.RavenwoodReplace;
import android.text.FontConfig;
import android.util.ArrayMap;
import android.util.Log;
@@ -32,6 +33,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.ravenwood.RavenwoodEnvironment;
import org.xmlpull.v1.XmlPullParserException;
@@ -50,23 +52,46 @@
/**
* Provides the system font configurations.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class SystemFonts {
private static final String TAG = "SystemFonts";
- private static final String FONTS_XML = "/system/etc/font_fallback.xml";
- private static final String LEGACY_FONTS_XML = "/system/etc/fonts.xml";
+ private static final String FONTS_XML = getFontsXmlDir() + "font_fallback.xml";
+ /** @hide */
+ public static final String LEGACY_FONTS_XML = getFontsXmlDir() + "fonts.xml";
/** @hide */
- public static final String SYSTEM_FONT_DIR = "/system/fonts/";
+ public static final String SYSTEM_FONT_DIR = getSystemFontDir();
private static final String OEM_XML = "/product/etc/fonts_customization.xml";
/** @hide */
public static final String OEM_FONT_DIR = "/product/fonts/";
+ private static final String DEVICE_FONTS_XML_DIR = "/system/etc/";
+ private static final String DEVICE_FONT_DIR = "/system/fonts/";
+
private SystemFonts() {} // Do not instansiate.
private static final Object LOCK = new Object();
private static @GuardedBy("sLock") Set<Font> sAvailableFonts;
+ @RavenwoodReplace
+ private static String getFontsXmlDir() {
+ return DEVICE_FONTS_XML_DIR;
+ }
+
+ private static String getFontsXmlDir$ravenwood() {
+ return RavenwoodEnvironment.getInstance().getRavenwoodRuntimePath() + "fonts/";
+ }
+
+ @RavenwoodReplace
+ private static String getSystemFontDir() {
+ return DEVICE_FONT_DIR;
+ }
+
+ private static String getSystemFontDir$ravenwood() {
+ return RavenwoodEnvironment.getInstance().getRavenwoodRuntimePath() + "fonts/";
+ }
+
/**
* Returns all available font files in the system.
*
diff --git a/graphics/java/android/graphics/text/GraphemeBreak.java b/graphics/java/android/graphics/text/GraphemeBreak.java
index f82b2fd..0bc1e3b 100644
--- a/graphics/java/android/graphics/text/GraphemeBreak.java
+++ b/graphics/java/android/graphics/text/GraphemeBreak.java
@@ -17,6 +17,7 @@
package android.graphics.text;
/** @hide */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class GraphemeBreak {
private GraphemeBreak() { }
diff --git a/graphics/java/android/graphics/text/LineBreakConfig.java b/graphics/java/android/graphics/text/LineBreakConfig.java
index 5a1086c..fa3cfbd 100644
--- a/graphics/java/android/graphics/text/LineBreakConfig.java
+++ b/graphics/java/android/graphics/text/LineBreakConfig.java
@@ -24,12 +24,13 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
-import android.app.ActivityThread;
import android.os.Build;
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
+import dalvik.system.VMRuntime;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
@@ -41,6 +42,7 @@
* <a href="https://www.w3.org/TR/css-text-3/#line-break-property" class="external">
* line-break property</a> for more information.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class LineBreakConfig implements Parcelable {
/**
* No hyphenation preference is specified.
@@ -484,8 +486,7 @@
* @hide
*/
public static @LineBreakStyle int getResolvedLineBreakStyle(@Nullable LineBreakConfig config) {
- final int targetSdkVersion = ActivityThread.currentApplication().getApplicationInfo()
- .targetSdkVersion;
+ final int targetSdkVersion = VMRuntime.getRuntime().getTargetSdkVersion();
final int defaultStyle;
final int vicVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM;
if (targetSdkVersion >= vicVersion) {
@@ -519,8 +520,7 @@
*/
public static @LineBreakWordStyle int getResolvedLineBreakWordStyle(
@Nullable LineBreakConfig config) {
- final int targetSdkVersion = ActivityThread.currentApplication().getApplicationInfo()
- .targetSdkVersion;
+ final int targetSdkVersion = VMRuntime.getRuntime().getTargetSdkVersion();
final int defaultWordStyle;
final int vicVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM;
if (targetSdkVersion >= vicVersion) {
diff --git a/graphics/java/android/graphics/text/LineBreaker.java b/graphics/java/android/graphics/text/LineBreaker.java
index 94de066..29135b8 100644
--- a/graphics/java/android/graphics/text/LineBreaker.java
+++ b/graphics/java/android/graphics/text/LineBreaker.java
@@ -91,6 +91,7 @@
* </pre>
* </p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class LineBreaker {
/** @hide */
@IntDef(prefix = { "BREAK_STRATEGY_" }, value = {
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
index 884268a..f58d531 100644
--- a/graphics/java/android/graphics/text/MeasuredText.java
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -56,6 +56,7 @@
* </pre>
* </p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class MeasuredText {
private static final String TAG = "MeasuredText";
diff --git a/graphics/java/android/graphics/text/PositionedGlyphs.java b/graphics/java/android/graphics/text/PositionedGlyphs.java
index 43216ba..31125ff 100644
--- a/graphics/java/android/graphics/text/PositionedGlyphs.java
+++ b/graphics/java/android/graphics/text/PositionedGlyphs.java
@@ -46,6 +46,7 @@
* @see TextRunShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
* @see TextRunShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class PositionedGlyphs {
private static class NoImagePreloadHolder {
private static final NativeAllocationRegistry REGISTRY =
diff --git a/graphics/java/android/graphics/text/TextRunShaper.java b/graphics/java/android/graphics/text/TextRunShaper.java
index 19ea04a..f1e3d67 100644
--- a/graphics/java/android/graphics/text/TextRunShaper.java
+++ b/graphics/java/android/graphics/text/TextRunShaper.java
@@ -40,6 +40,7 @@
* @see android.text.TextShaper#shapeText(CharSequence, int, int, TextDirectionHeuristic, TextPaint,
* TextShaper.GlyphsConsumer)
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TextRunShaper {
private TextRunShaper() {} // Do not instantiate
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index eb59d6ef..7ab9e2e 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -705,8 +705,7 @@
}
private static boolean isOverlayTransitionSupported() {
- return Flags.moveAnimationOptionsToChange()
- && Flags.activityEmbeddingOverlayPresentationFlag();
+ return Flags.activityEmbeddingOverlayPresentationFlag();
}
@NonNull
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index a08f88a..1e72d64 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -184,3 +184,13 @@
description: "Try out bubble bar on phones"
bug: "394869612"
}
+
+flag {
+ name: "enable_bubble_task_view_listener"
+ namespace: "multitasking"
+ description: "Use the same taskview listener for bubble bar and floating"
+ bug: "272102927"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml b/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml
index 95cd1c7..800ea74 100644
--- a/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml
@@ -2,6 +2,7 @@
package="com.android.wm.shell.multivalenttests">
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS"/>
+ <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT"/>
<application android:debuggable="true" android:supportsRtl="true" >
<uses-library android:name="android.test.runner" />
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewListenerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewListenerTest.kt
index 9ebc3d7..3aefcd5 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewListenerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewListenerTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.bubbles
+import android.app.ActivityOptions
import android.app.Notification
import android.app.PendingIntent
import android.content.ComponentName
@@ -24,6 +25,8 @@
import android.content.pm.ShortcutInfo
import android.graphics.drawable.Icon
import android.os.UserHandle
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import android.service.notification.NotificationListenerService.Ranking
import android.service.notification.StatusBarNotification
import android.view.View
@@ -33,6 +36,7 @@
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.internal.protolog.ProtoLog
+import com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_ANYTHING
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.Bubbles.BubbleMetadataFlagListener
import com.android.wm.shell.common.TestShellExecutor
@@ -41,6 +45,7 @@
import com.android.wm.shell.taskview.TaskViewTaskController
import com.google.common.truth.Truth.assertThat
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
@@ -48,6 +53,7 @@
import org.mockito.Mockito.reset
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
@@ -61,6 +67,9 @@
@RunWith(AndroidJUnit4::class)
class BubbleTaskViewListenerTest {
+ @get:Rule
+ val setFlagsRule = SetFlagsRule()
+
private val context = ApplicationProvider.getApplicationContext<Context>()
private var taskViewController = mock<TaskViewController>()
@@ -155,9 +164,22 @@
}
getInstrumentation().waitForIdleSync()
- // ..so it's pending intent-based, and launches that
+ // ..so it's pending intent-based, so the pending intent should be active
assertThat(b.isPendingIntentActive).isTrue()
- verify(taskViewController).startActivity(any(), eq(pendingIntent), any(), any(), any())
+
+ val intentCaptor = argumentCaptor<Intent>()
+ val optionsCaptor = argumentCaptor<ActivityOptions>()
+
+ verify(taskViewController).startActivity(any(),
+ eq(pendingIntent),
+ intentCaptor.capture(),
+ optionsCaptor.capture(),
+ any())
+ val intentFlags = intentCaptor.lastValue.flags
+ assertThat((intentFlags and Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0).isTrue()
+ assertThat((intentFlags and Intent.FLAG_ACTIVITY_MULTIPLE_TASK) != 0).isTrue()
+ assertThat(optionsCaptor.lastValue.launchedFromBubble).isTrue()
+ assertThat(optionsCaptor.lastValue.taskAlwaysOnTop).isTrue()
}
@Test
@@ -178,12 +200,52 @@
}
getInstrumentation().waitForIdleSync()
- assertThat(b.isPendingIntentActive).isFalse()
- verify(taskViewController).startShortcutActivity(any(), eq(shortcutInfo), any(), any())
+ val optionsCaptor = argumentCaptor<ActivityOptions>()
+
+ assertThat(b.isPendingIntentActive).isFalse() // not triggered for shortcut chats
+ verify(taskViewController).startShortcutActivity(any(),
+ eq(shortcutInfo),
+ optionsCaptor.capture(),
+ any())
+ assertThat(optionsCaptor.lastValue.launchedFromBubble).isTrue()
+ assertThat(optionsCaptor.lastValue.isApplyActivityFlagsForBubbles).isTrue()
+ assertThat(optionsCaptor.lastValue.taskAlwaysOnTop).isTrue()
+ }
+
+ @EnableFlags(FLAG_ENABLE_BUBBLE_ANYTHING)
+ @Test
+ fun onInitialized_shortcutBubble() {
+ val shortcutInfo = ShortcutInfo.Builder(context)
+ .setId("mockShortcutId")
+ .build()
+
+ val b = createShortcutBubble(shortcutInfo)
+ bubbleTaskViewListener.setBubble(b)
+
+ assertThat(b.isChat).isFalse()
+ assertThat(b.isShortcut).isTrue()
+ assertThat(b.shortcutInfo).isNotNull()
+
+ getInstrumentation().runOnMainSync {
+ bubbleTaskViewListener.onInitialized()
+ }
+ getInstrumentation().waitForIdleSync()
+
+ val optionsCaptor = argumentCaptor<ActivityOptions>()
+
+ assertThat(b.isPendingIntentActive).isFalse() // chat only triggers setting it active
+ verify(taskViewController).startShortcutActivity(any(),
+ eq(shortcutInfo),
+ optionsCaptor.capture(),
+ any())
+ assertThat(optionsCaptor.lastValue.launchedFromBubble).isFalse() // chat only
+ assertThat(optionsCaptor.lastValue.isApplyActivityFlagsForBubbles).isFalse() // chat only
+ assertThat(optionsCaptor.lastValue.isApplyMultipleTaskFlagForShortcut).isTrue()
+ assertThat(optionsCaptor.lastValue.taskAlwaysOnTop).isTrue()
}
@Test
- fun onInitialized_appBubble() {
+ fun onInitialized_appBubble_intent() {
val b = createAppBubble()
bubbleTaskViewListener.setBubble(b)
@@ -194,11 +256,83 @@
}
getInstrumentation().waitForIdleSync()
- assertThat(b.isPendingIntentActive).isFalse()
- verify(taskViewController).startActivity(any(), any(), anyOrNull(), any(), any())
+ val intentCaptor = argumentCaptor<Intent>()
+ val optionsCaptor = argumentCaptor<ActivityOptions>()
+
+ assertThat(b.isPendingIntentActive).isFalse() // chat only triggers setting it active
+ verify(taskViewController).startActivity(any(),
+ any(),
+ intentCaptor.capture(),
+ optionsCaptor.capture(),
+ any())
+
+ assertThat((intentCaptor.lastValue.flags
+ and Intent.FLAG_ACTIVITY_MULTIPLE_TASK) != 0).isTrue()
+ assertThat(optionsCaptor.lastValue.launchedFromBubble).isFalse() // chat only
+ assertThat(optionsCaptor.lastValue.isApplyActivityFlagsForBubbles).isFalse() // chat only
+ assertThat(optionsCaptor.lastValue.taskAlwaysOnTop).isTrue()
}
@Test
+ fun onInitialized_appBubble_pendingIntent() {
+ val b = createAppBubble(usePendingIntent = true)
+ bubbleTaskViewListener.setBubble(b)
+
+ assertThat(b.isApp).isTrue()
+
+ getInstrumentation().runOnMainSync {
+ bubbleTaskViewListener.onInitialized()
+ }
+ getInstrumentation().waitForIdleSync()
+
+ val intentCaptor = argumentCaptor<Intent>()
+ val optionsCaptor = argumentCaptor<ActivityOptions>()
+
+ assertThat(b.isPendingIntentActive).isFalse() // chat only triggers setting it active
+ verify(taskViewController).startActivity(any(),
+ any(),
+ intentCaptor.capture(),
+ optionsCaptor.capture(),
+ any())
+
+ assertThat((intentCaptor.lastValue.flags
+ and Intent.FLAG_ACTIVITY_MULTIPLE_TASK) != 0).isTrue()
+ assertThat(optionsCaptor.lastValue.launchedFromBubble).isFalse() // chat only
+ assertThat(optionsCaptor.lastValue.isApplyActivityFlagsForBubbles).isFalse() // chat only
+ assertThat(optionsCaptor.lastValue.taskAlwaysOnTop).isTrue()
+ }
+
+ @Test
+ fun onInitialized_noteBubble() {
+ val b = createNoteBubble()
+ bubbleTaskViewListener.setBubble(b)
+
+ assertThat(b.isNote).isTrue()
+
+ getInstrumentation().runOnMainSync {
+ bubbleTaskViewListener.onInitialized()
+ }
+ getInstrumentation().waitForIdleSync()
+
+ val intentCaptor = argumentCaptor<Intent>()
+ val optionsCaptor = argumentCaptor<ActivityOptions>()
+
+ assertThat(b.isPendingIntentActive).isFalse() // chat only triggers setting it active
+ verify(taskViewController).startActivity(any(),
+ any(),
+ intentCaptor.capture(),
+ optionsCaptor.capture(),
+ any())
+
+ assertThat((intentCaptor.lastValue.flags
+ and Intent.FLAG_ACTIVITY_MULTIPLE_TASK) != 0).isTrue()
+ assertThat(optionsCaptor.lastValue.launchedFromBubble).isFalse() // chat only
+ assertThat(optionsCaptor.lastValue.isApplyActivityFlagsForBubbles).isFalse() // chat only
+ assertThat(optionsCaptor.lastValue.taskAlwaysOnTop).isTrue()
+ }
+
+
+ @Test
fun onInitialized_preparingTransition() {
val b = createAppBubble()
bubbleTaskViewListener.setBubble(b)
@@ -416,13 +550,24 @@
assertThat(isNew).isTrue()
}
- private fun createAppBubble(): Bubble {
+ private fun createAppBubble(usePendingIntent: Boolean = false): Bubble {
val target = Intent(context, TestActivity::class.java)
target.setPackage(context.packageName)
+ if (usePendingIntent) {
+ // Robolectric doesn't seem to play nice with PendingIntents, have to mock it.
+ val pendingIntent = mock<PendingIntent>()
+ whenever(pendingIntent.intent).thenReturn(target)
+ return Bubble.createAppBubble(pendingIntent, mock<UserHandle>(),
+ mainExecutor, bgExecutor)
+ }
return Bubble.createAppBubble(target, mock<UserHandle>(), mock<Icon>(),
mainExecutor, bgExecutor)
}
+ private fun createShortcutBubble(shortcutInfo: ShortcutInfo): Bubble {
+ return Bubble.createShortcutBubble(shortcutInfo, mainExecutor, bgExecutor)
+ }
+
private fun createNoteBubble(): Bubble {
val target = Intent(context, TestActivity::class.java)
target.setPackage(context.packageName)
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index f5f3f0f..a0c68ad 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -670,7 +670,4 @@
<dimen name="desktop_windowing_education_promo_height">352dp</dimen>
<!-- The corner radius of the desktop windowing education promo. -->
<dimen name="desktop_windowing_education_promo_corner_radius">28dp</dimen>
-
- <!-- The corner radius of freeform tasks in desktop windowing. -->
- <dimen name="desktop_windowing_freeform_rounded_corner_radius">16dp</dimen>
</resources>
diff --git a/libs/WindowManager/Shell/shared/res/values/dimen.xml b/libs/WindowManager/Shell/shared/res/values/dimen.xml
index 11a6f32..23c9caf 100644
--- a/libs/WindowManager/Shell/shared/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/shared/res/values/dimen.xml
@@ -46,4 +46,7 @@
<dimen name="drop_target_expanded_view_height">578</dimen>
<dimen name="drop_target_expanded_view_padding_bottom">108</dimen>
<dimen name="drop_target_expanded_view_padding_horizontal">24</dimen>
+
+ <!-- The corner radius of freeform tasks in desktop windowing. -->
+ <dimen name="desktop_windowing_freeform_rounded_corner_radius">16dp</dimen>
</resources>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/Interpolators.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/Interpolators.java
index e92c1eb..43dd9b7 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/Interpolators.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/Interpolators.java
@@ -74,6 +74,12 @@
0.05f, 0.7f, 0.1f, 1f);
/**
+ * The standard accelerating interpolator that should be used on every regular movement of
+ * content that is disappearing e.g. when moving off screen.
+ */
+ public static final Interpolator STANDARD_ACCELERATE = new PathInterpolator(0.3f, 0f, 1f, 1f);
+
+ /**
* The standard decelerating interpolator that should be used on every regular movement of
* content that is appearing e.g. when coming from off screen.
*/
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/MinimizeAnimator.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/MinimizeAnimator.kt
index 0586e26..4ecace0 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/MinimizeAnimator.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/MinimizeAnimator.kt
@@ -19,52 +19,76 @@
import android.animation.Animator
import android.animation.AnimatorSet
import android.animation.ValueAnimator
-import android.util.DisplayMetrics
+import android.content.Context
+import android.os.Handler
+import android.view.Choreographer
import android.view.SurfaceControl.Transaction
-import android.view.animation.LinearInterpolator
-import android.view.animation.PathInterpolator
import android.window.TransitionInfo.Change
+import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
+import com.android.internal.jank.InteractionJankMonitor
/** Creates minimization animation */
object MinimizeAnimator {
private const val MINIMIZE_ANIM_ALPHA_DURATION_MS = 100L
- private val STANDARD_ACCELERATE = PathInterpolator(0.3f, 0f, 1f, 1f)
-
private val minimizeBoundsAnimationDef =
WindowAnimator.BoundsAnimationParams(
durationMs = 200,
endOffsetYDp = 12f,
endScale = 0.97f,
- interpolator = STANDARD_ACCELERATE,
+ interpolator = Interpolators.STANDARD_ACCELERATE,
)
+ /**
+ * Creates a minimize animator for given task [Change].
+ *
+ * @param onAnimFinish finish-callback for the animation, note that this is called on the same
+ * thread as the animation itself.
+ * @param animationHandler the Handler that the animation is running on.
+ */
@JvmStatic
fun create(
- displayMetrics: DisplayMetrics,
+ context: Context,
change: Change,
transaction: Transaction,
onAnimFinish: (Animator) -> Unit,
+ interactionJankMonitor: InteractionJankMonitor,
+ animationHandler: Handler,
): Animator {
val boundsAnimator = WindowAnimator.createBoundsAnimator(
- displayMetrics,
+ context.resources.displayMetrics,
minimizeBoundsAnimationDef,
change,
transaction,
)
val alphaAnimator = ValueAnimator.ofFloat(1f, 0f).apply {
duration = MINIMIZE_ANIM_ALPHA_DURATION_MS
- interpolator = LinearInterpolator()
+ interpolator = Interpolators.LINEAR
addUpdateListener { animation ->
- transaction.setAlpha(change.leash, animation.animatedValue as Float).apply()
+ transaction
+ .setAlpha(change.leash, animation.animatedValue as Float)
+ .setFrameTimeline(Choreographer.getInstance().vsyncId)
+ .apply()
}
}
val listener = object : Animator.AnimatorListener {
- override fun onAnimationEnd(animator: Animator) = onAnimFinish(animator)
- override fun onAnimationCancel(animator: Animator) = Unit
+ override fun onAnimationStart(animator: Animator) {
+ interactionJankMonitor.begin(
+ change.leash,
+ context,
+ animationHandler,
+ CUJ_DESKTOP_MODE_MINIMIZE_WINDOW,
+ )
+ }
+ override fun onAnimationCancel(animator: Animator) {
+ interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
+ }
override fun onAnimationRepeat(animator: Animator) = Unit
- override fun onAnimationStart(animator: Animator) = Unit
+ override fun onAnimationEnd(animator: Animator) {
+ interactionJankMonitor.end(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
+ onAnimFinish(animator)
+ }
}
return AnimatorSet().apply {
playTogether(boundsAnimator, alphaAnimator)
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
index 14338a4..0e4a6b9 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
@@ -16,12 +16,14 @@
package com.android.wm.shell.shared.desktopmode
+import android.Manifest.permission.SYSTEM_ALERT_WINDOW
import android.app.TaskInfo
import android.content.Context
import android.content.pm.ActivityInfo
import android.content.pm.ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED
import android.content.pm.ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION
import android.content.pm.ActivityInfo.OVERRIDE_EXCLUDE_CAPTION_INSETS_FROM_APP_BOUNDS
+import android.content.pm.PackageManager
import android.window.DesktopModeFlags
import com.android.internal.R
@@ -32,8 +34,10 @@
class DesktopModeCompatPolicy(private val context: Context) {
private val systemUiPackage: String = context.resources.getString(R.string.config_systemUi)
+ private val pkgManager: PackageManager
+ get() = context.getPackageManager()
private val defaultHomePackage: String?
- get() = context.getPackageManager().getHomeActivities(ArrayList())?.packageName
+ get() = pkgManager.getHomeActivities(ArrayList())?.packageName
/**
* If the top activity should be exempt from desktop windowing and forced back to fullscreen.
@@ -47,11 +51,12 @@
fun isTopActivityExemptFromDesktopWindowing(packageName: String?,
numActivities: Int, isTopActivityNoDisplay: Boolean, isActivityStackTransparent: Boolean) =
- DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue
- && ((isSystemUiTask(packageName)
- || isPartOfDefaultHomePackageOrNoHomeAvailable(packageName)
- || isTransparentTask(isActivityStackTransparent, numActivities))
- && !isTopActivityNoDisplay)
+ DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue &&
+ ((isSystemUiTask(packageName) ||
+ isPartOfDefaultHomePackageOrNoHomeAvailable(packageName) ||
+ (isTransparentTask(isActivityStackTransparent, numActivities) &&
+ hasFullscreenTransparentPermission(packageName))) &&
+ !isTopActivityNoDisplay)
/**
* Whether the caption insets should be excluded from configuration for system to handle.
@@ -83,6 +88,26 @@
private fun isSystemUiTask(packageName: String?) = packageName == systemUiPackage
+ // Checks if the app for the given package has the SYSTEM_ALERT_WINDOW permission.
+ private fun hasFullscreenTransparentPermission(packageName: String?): Boolean {
+ if (DesktopModeFlags.ENABLE_MODALS_FULLSCREEN_WITH_PERMISSIONS.isTrue) {
+ if (packageName == null) {
+ return false
+ }
+ return try {
+ val packageInfo = pkgManager.getPackageInfo(
+ packageName,
+ PackageManager.GET_PERMISSIONS
+ )
+ packageInfo?.requestedPermissions?.contains(SYSTEM_ALERT_WINDOW) == true
+ } catch (e: PackageManager.NameNotFoundException) {
+ false // Package not found
+ }
+ }
+ // If the flag is disabled we make this condition neutral.
+ return true
+ }
+
/**
* Returns true if the tasks base activity is part of the default home package, or there is
* currently no default home package available.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
index f269b38..78f5154 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
@@ -364,7 +364,7 @@
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull List<ActivityEmbeddingAnimationAdapter> adapters) {
for (ActivityEmbeddingAnimationAdapter adapter : adapters) {
- final int backgroundColor = getTransitionBackgroundColorIfSet(info, adapter.mChange,
+ final int backgroundColor = getTransitionBackgroundColorIfSet(adapter.mChange,
adapter.mAnimation, 0 /* defaultColor */);
if (backgroundColor != 0) {
// We only need to show one color.
@@ -436,8 +436,8 @@
final TransitionInfo.AnimationOptions options = boundsAnimationChange
.getAnimationOptions();
if (options != null) {
- final Animation overrideAnimation = mAnimationSpec.loadCustomAnimationFromOptions(
- options, TRANSIT_CHANGE);
+ final Animation overrideAnimation =
+ mAnimationSpec.loadCustomAnimation(options, TRANSIT_CHANGE);
if (overrideAnimation != null) {
overrideShowBackdrop = overrideAnimation.getShowBackdrop();
}
@@ -447,7 +447,7 @@
// There are two animations in the array. The first one is for the start leash
// (snapshot), and the second one is for the end leash (TaskFragment).
final Animation[] animations =
- mAnimationSpec.createChangeBoundsChangeAnimations(info, change, parentBounds);
+ mAnimationSpec.createChangeBoundsChangeAnimations(change, parentBounds);
// Jump cut if either animation has zero for duration.
for (Animation animation : animations) {
if (shouldUseJumpCutForAnimation(animation)) {
@@ -500,12 +500,10 @@
// window without bounds change.
animation = ActivityEmbeddingAnimationSpec.createNoopAnimation(change);
} else if (TransitionUtil.isClosingType(change.getMode())) {
- animation =
- mAnimationSpec.createChangeBoundsCloseAnimation(info, change, parentBounds);
+ animation = mAnimationSpec.createChangeBoundsCloseAnimation(change, parentBounds);
shouldShowBackgroundColor = false;
} else {
- animation =
- mAnimationSpec.createChangeBoundsOpenAnimation(info, change, parentBounds);
+ animation = mAnimationSpec.createChangeBoundsOpenAnimation(change, parentBounds);
shouldShowBackgroundColor = false;
}
if (shouldUseJumpCutForAnimation(animation)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
index 77799e9..2b9eda4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
@@ -42,7 +42,6 @@
import android.window.TransitionInfo;
import com.android.internal.policy.TransitionAnimation;
-import com.android.window.flags.Flags;
import com.android.wm.shell.shared.TransitionUtil;
/** Animation spec for ActivityEmbedding transition. */
@@ -94,9 +93,10 @@
/** Animation for window that is opening in a change transition. */
@NonNull
- Animation createChangeBoundsOpenAnimation(@NonNull TransitionInfo info,
- @NonNull TransitionInfo.Change change, @NonNull Rect parentBounds) {
- final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE);
+ Animation createChangeBoundsOpenAnimation(@NonNull TransitionInfo.Change change,
+ @NonNull Rect parentBounds) {
+ final Animation customAnimation =
+ loadCustomAnimation(change.getAnimationOptions(), TRANSIT_CHANGE);
if (customAnimation != null) {
return customAnimation;
}
@@ -126,9 +126,10 @@
/** Animation for window that is closing in a change transition. */
@NonNull
- Animation createChangeBoundsCloseAnimation(@NonNull TransitionInfo info,
- @NonNull TransitionInfo.Change change, @NonNull Rect parentBounds) {
- final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE);
+ Animation createChangeBoundsCloseAnimation(@NonNull TransitionInfo.Change change,
+ @NonNull Rect parentBounds) {
+ final Animation customAnimation =
+ loadCustomAnimation(change.getAnimationOptions(), TRANSIT_CHANGE);
if (customAnimation != null) {
return customAnimation;
}
@@ -162,12 +163,13 @@
* the second one is for the end leash.
*/
@NonNull
- Animation[] createChangeBoundsChangeAnimations(@NonNull TransitionInfo info,
- @NonNull TransitionInfo.Change change, @NonNull Rect parentBounds) {
+ Animation[] createChangeBoundsChangeAnimations(@NonNull TransitionInfo.Change change,
+ @NonNull Rect parentBounds) {
// TODO(b/293658614): Support more complicated animations that may need more than a noop
// animation as the start leash.
final Animation noopAnimation = createNoopAnimation(change);
- final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE);
+ final Animation customAnimation =
+ loadCustomAnimation(change.getAnimationOptions(), TRANSIT_CHANGE);
if (customAnimation != null) {
return new Animation[]{noopAnimation, customAnimation};
}
@@ -221,7 +223,8 @@
Animation loadOpenAnimation(@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) {
final boolean isEnter = TransitionUtil.isOpeningType(change.getMode());
- final Animation customAnimation = loadCustomAnimation(info, change, change.getMode());
+ final Animation customAnimation =
+ loadCustomAnimation(change.getAnimationOptions(), change.getMode());
final Animation animation;
if (customAnimation != null) {
animation = customAnimation;
@@ -248,7 +251,8 @@
Animation loadCloseAnimation(@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) {
final boolean isEnter = TransitionUtil.isOpeningType(change.getMode());
- final Animation customAnimation = loadCustomAnimation(info, change, change.getMode());
+ final Animation customAnimation =
+ loadCustomAnimation(change.getAnimationOptions(), change.getMode());
final Animation animation;
if (customAnimation != null) {
animation = customAnimation;
@@ -280,20 +284,8 @@
}
@Nullable
- private Animation loadCustomAnimation(@NonNull TransitionInfo info,
- @NonNull TransitionInfo.Change change, @WindowManager.TransitionType int mode) {
- final TransitionInfo.AnimationOptions options;
- if (Flags.moveAnimationOptionsToChange()) {
- options = change.getAnimationOptions();
- } else {
- options = info.getAnimationOptions();
- }
- return loadCustomAnimationFromOptions(options, mode);
- }
-
- @Nullable
- Animation loadCustomAnimationFromOptions(@Nullable TransitionInfo.AnimationOptions options,
- @WindowManager.TransitionType int mode) {
+ Animation loadCustomAnimation(@Nullable TransitionInfo.AnimationOptions options,
+ @WindowManager.TransitionType int mode) {
if (options == null || options.getType() != ANIM_CUSTOM) {
return null;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
index 55ed5fa..3a95333 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
@@ -40,7 +40,6 @@
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.window.flags.Flags;
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
@@ -123,9 +122,6 @@
}
private boolean shouldAnimateAnimationOptions(@NonNull TransitionInfo info) {
- if (!Flags.moveAnimationOptionsToChange()) {
- return shouldAnimateAnimationOptions(info.getAnimationOptions());
- }
for (TransitionInfo.Change change : info.getChanges()) {
if (!shouldAnimateAnimationOptions(change.getAnimationOptions())) {
// If any of override animation is not supported, don't animate the transition.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index d948928..313d151 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -364,7 +364,7 @@
@ShellMainThread Executor mainExecutor, @ShellBackgroundThread Executor bgExecutor) {
return new Bubble(intent,
user,
- /* key= */ getAppBubbleKeyForApp(ComponentUtils.getPackageName(intent), user),
+ /* key= */ getAppBubbleKeyForApp(intent.getIntent().getPackage(), user),
mainExecutor, bgExecutor);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 3f607a9..2c2451c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -197,6 +197,8 @@
*/
private final FrameLayout mExpandedViewContainer = new FrameLayout(getContext());
+ private TaskView.Listener mCurrentTaskViewListener;
+
private final TaskView.Listener mTaskViewListener = new TaskView.Listener() {
private boolean mInitialized = false;
private boolean mDestroyed = false;
@@ -235,18 +237,24 @@
Context context =
mContext.createContextAsUser(
mBubble.getUser(), Context.CONTEXT_RESTRICTED);
+ Intent fillInIntent = new Intent();
+ fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
PendingIntent pi = PendingIntent.getActivity(
context,
/* requestCode= */ 0,
- mBubble.getIntent().addFlags(FLAG_ACTIVITY_MULTIPLE_TASK),
- PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT,
+ mBubble.getIntent(),
+ // Needs to be mutable for the fillInIntent
+ PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT,
/* options= */ null);
- mTaskView.startActivity(pi, /* fillInIntent= */ null, options,
- launchBounds);
+ mTaskView.startActivity(pi, fillInIntent, options, launchBounds);
} else if (!mIsOverflow && isShortcutBubble) {
ProtoLog.v(WM_SHELL_BUBBLES, "startingShortcutBubble=%s", getBubbleKey());
- options.setLaunchedFromBubble(true);
- options.setApplyActivityFlagsForBubbles(true);
+ if (mBubble.isChat()) {
+ options.setLaunchedFromBubble(true);
+ options.setApplyActivityFlagsForBubbles(true);
+ } else {
+ options.setApplyMultipleTaskFlagForShortcut(true);
+ }
mTaskView.startShortcutActivity(mBubble.getShortcutInfo(),
options, launchBounds);
} else {
@@ -453,7 +461,34 @@
mTaskView = bubbleTaskView.getTaskView();
// reset the insets that might left after TaskView is shown in BubbleBarExpandedView
mTaskView.setCaptionInsets(null);
- bubbleTaskView.setDelegateListener(mTaskViewListener);
+ if (Flags.enableBubbleTaskViewListener()) {
+ mCurrentTaskViewListener = new BubbleTaskViewListener(mContext, bubbleTaskView,
+ /* viewParent= */ this, expandedViewManager,
+ new BubbleTaskViewListener.Callback() {
+ @Override
+ public void onTaskCreated() {
+ setContentVisibility(true);
+ }
+
+ @Override
+ public void onContentVisibilityChanged(boolean visible) {
+ setContentVisibility(visible);
+ }
+
+ @Override
+ public void onBackPressed() {
+ mStackView.onBackPressed();
+ }
+
+ @Override
+ public void onTaskRemovalStarted() {
+ // nothing to do / handled in listener.
+ }
+ });
+ } else {
+ mCurrentTaskViewListener = mTaskViewListener;
+ bubbleTaskView.setDelegateListener(mCurrentTaskViewListener);
+ }
// set a fixed width so it is not recalculated as part of a rotation. the width will be
// updated manually after the rotation.
@@ -464,9 +499,12 @@
}
mExpandedViewContainer.addView(mTaskView, lp);
bringChildToFront(mTaskView);
- if (bubbleTaskView.isCreated()) {
- mTaskViewListener.onTaskCreated(
- bubbleTaskView.getTaskId(), bubbleTaskView.getComponentName());
+
+ if (!Flags.enableBubbleTaskViewListener()) {
+ if (bubbleTaskView.isCreated()) {
+ mCurrentTaskViewListener.onTaskCreated(
+ bubbleTaskView.getTaskId(), bubbleTaskView.getComponentName());
+ }
}
}
}
@@ -897,7 +935,12 @@
Log.w(TAG, "Stack is null for bubble: " + bubble);
return;
}
- boolean isNew = mBubble == null || didBackingContentChange(bubble);
+ boolean isNew;
+ if (mCurrentTaskViewListener instanceof BubbleTaskViewListener) {
+ isNew = ((BubbleTaskViewListener) mCurrentTaskViewListener).setBubble(bubble);
+ } else {
+ isNew = mBubble == null || didBackingContentChange(bubble);
+ }
boolean isUpdate = bubble != null && mBubble != null
&& bubble.getKey().equals(mBubble.getKey());
ProtoLog.d(WM_SHELL_BUBBLES, "BubbleExpandedView - update bubble=%s; isNew=%b; isUpdate=%b",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewListener.java
index a38debb..63d7134 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewListener.java
@@ -129,27 +129,28 @@
Context context =
mContext.createContextAsUser(
mBubble.getUser(), Context.CONTEXT_RESTRICTED);
- Intent fillInIntent = null;
+ Intent fillInIntent = new Intent();
+ fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
// First try get pending intent from the bubble
PendingIntent pi = mBubble.getPendingIntent();
if (pi == null) {
- // If null - create new one
+ // If null - create new one based on the bubble intent
pi = PendingIntent.getActivity(
context,
/* requestCode= */ 0,
- mBubble.getIntent()
- .addFlags(FLAG_ACTIVITY_MULTIPLE_TASK),
- PendingIntent.FLAG_IMMUTABLE
- | PendingIntent.FLAG_UPDATE_CURRENT,
+ mBubble.getIntent(),
+ // Needs to be mutable for the fillInIntent
+ PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT,
/* options= */ null);
- } else {
- fillInIntent = new Intent(pi.getIntent());
- fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
}
mTaskView.startActivity(pi, fillInIntent, options, launchBounds);
} else if (isShortcutBubble) {
- options.setLaunchedFromBubble(true);
- options.setApplyActivityFlagsForBubbles(true);
+ if (mBubble.isChat()) {
+ options.setLaunchedFromBubble(true);
+ options.setApplyActivityFlagsForBubbles(true);
+ } else {
+ options.setApplyMultipleTaskFlagForShortcut(true);
+ }
mTaskView.startShortcutActivity(mBubble.getShortcutInfo(),
options, launchBounds);
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
index a676f41..338ffe7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
@@ -21,6 +21,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.View.INVISIBLE;
import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.wm.shell.transition.Transitions.TRANSIT_CONVERT_TO_BUBBLE;
@@ -325,7 +326,7 @@
for (int i = 0; i < info.getChanges().size(); ++i) {
final TransitionInfo.Change chg = info.getChanges().get(i);
if (chg.getTaskInfo() == null) continue;
- if (chg.getMode() != TRANSIT_CHANGE) continue;
+ if (chg.getMode() != TRANSIT_CHANGE && chg.getMode() != TRANSIT_TO_FRONT) continue;
if (!mTaskInfo.token.equals(chg.getTaskInfo().token)) continue;
mStartBounds.set(chg.getStartAbsBounds());
// Converting a task into taskview, so treat as "new"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index 8377a35..df82091 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -227,7 +227,13 @@
/** Hides the IME for Bubbles when the device is locked. */
public void hideImeForBubblesWhenLocked(int displayId) {
PerDisplay pd = mImePerDisplay.get(displayId);
- pd.setImeInputTargetRequestedVisibility(false, pd.getImeSourceControl().getImeStatsToken());
+ InsetsSourceControl imeSourceControl = pd.getImeSourceControl();
+ if (imeSourceControl != null) {
+ ImeTracker.Token imeStatsToken = imeSourceControl.getImeStatsToken();
+ if (imeStatsToken != null) {
+ pd.setImeInputTargetRequestedVisibility(false, imeStatsToken);
+ }
+ }
}
/** An implementation of {@link IDisplayWindowInsetsController} for a given display id. */
@@ -324,8 +330,10 @@
}
applyVisibilityToLeash(imeSourceControl);
}
- if (!mImeShowing) {
- removeImeSurface(mDisplayId);
+ if (!android.view.inputmethod.Flags.refactorInsetsController()) {
+ if (!mImeShowing) {
+ removeImeSurface(mDisplayId);
+ }
}
}
} else {
@@ -663,7 +671,9 @@
ImeTracker.forLogging().onProgress(mStatsToken,
ImeTracker.PHASE_WM_ANIMATION_RUNNING);
t.hide(animatingLeash);
- removeImeSurface(mDisplayId);
+ if (!android.view.inputmethod.Flags.refactorInsetsController()) {
+ removeImeSurface(mDisplayId);
+ }
if (android.view.inputmethod.Flags.refactorInsetsController()) {
setVisibleDirectly(false /* visible */, statsToken);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorController.kt
new file mode 100644
index 0000000..7a5bc13
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorController.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.common
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.graphics.RectF
+import android.view.SurfaceControl
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.shared.annotations.ShellDesktopThread
+
+/**
+ * Controller to manage the indicators that show users the current position of the dragged window on
+ * the new display when performing drag move across displays.
+ */
+class MultiDisplayDragMoveIndicatorController(
+ private val displayController: DisplayController,
+ private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+ private val indicatorSurfaceFactory: MultiDisplayDragMoveIndicatorSurface.Factory,
+ @ShellDesktopThread private val desktopExecutor: ShellExecutor,
+) {
+ @ShellDesktopThread
+ private val dragIndicators =
+ mutableMapOf<Int, MutableMap<Int, MultiDisplayDragMoveIndicatorSurface>>()
+
+ /**
+ * Called during drag move, which started at [startDisplayId]. Updates the position and
+ * visibility of the drag move indicators for the [taskInfo] based on [boundsDp] on the
+ * destination displays ([displayIds]) as the dragged window moves. [transactionSupplier]
+ * provides a [SurfaceControl.Transaction] for applying changes to the indicator surfaces.
+ *
+ * It is executed on the [desktopExecutor] to prevent blocking the main thread and avoid jank,
+ * as creating and manipulating surfaces can be expensive.
+ */
+ fun onDragMove(
+ boundsDp: RectF,
+ startDisplayId: Int,
+ taskInfo: RunningTaskInfo,
+ displayIds: Set<Int>,
+ transactionSupplier: () -> SurfaceControl.Transaction,
+ ) {
+ desktopExecutor.execute {
+ for (displayId in displayIds) {
+ if (displayId == startDisplayId) {
+ // No need to render indicators on the original display where the drag started.
+ continue
+ }
+ val displayLayout = displayController.getDisplayLayout(displayId) ?: continue
+ val shouldBeVisible =
+ RectF.intersects(RectF(boundsDp), displayLayout.globalBoundsDp())
+ if (
+ dragIndicators[taskInfo.taskId]?.containsKey(displayId) != true &&
+ !shouldBeVisible
+ ) {
+ // Skip this display if:
+ // - It doesn't have an existing indicator that needs to be updated, AND
+ // - The latest dragged window bounds don't intersect with this display.
+ continue
+ }
+
+ val boundsPx =
+ MultiDisplayDragMoveBoundsCalculator.convertGlobalDpToLocalPxForRect(
+ boundsDp,
+ displayLayout,
+ )
+
+ // Get or create the inner map for the current task.
+ val dragIndicatorsForTask =
+ dragIndicators.getOrPut(taskInfo.taskId) { mutableMapOf() }
+ dragIndicatorsForTask[displayId]?.also { existingIndicator ->
+ val transaction = transactionSupplier()
+ existingIndicator.relayout(boundsPx, transaction, shouldBeVisible)
+ transaction.apply()
+ } ?: run {
+ val newIndicator =
+ indicatorSurfaceFactory.create(
+ taskInfo,
+ displayController.getDisplay(displayId),
+ )
+ newIndicator.show(
+ transactionSupplier(),
+ taskInfo,
+ rootTaskDisplayAreaOrganizer,
+ displayId,
+ boundsPx,
+ )
+ dragIndicatorsForTask[displayId] = newIndicator
+ }
+ }
+ }
+ }
+
+ /**
+ * Called when the drag ends. Disposes of the drag move indicator surfaces associated with the
+ * given [taskId]. [transactionSupplier] provides a [SurfaceControl.Transaction] for applying
+ * changes to the indicator surfaces.
+ *
+ * It is executed on the [desktopExecutor] to ensure that any pending `onDragMove` operations
+ * have completed before disposing of the surfaces.
+ */
+ fun onDragEnd(taskId: Int, transactionSupplier: () -> SurfaceControl.Transaction) {
+ desktopExecutor.execute {
+ dragIndicators.remove(taskId)?.values?.takeIf { it.isNotEmpty() }?.let { indicators ->
+ val transaction = transactionSupplier()
+ indicators.forEach { indicator ->
+ indicator.disposeSurface(transaction)
+ }
+ transaction.apply()
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorSurface.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorSurface.kt
new file mode 100644
index 0000000..d05d3b0
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorSurface.kt
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.common
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.Context
+import android.graphics.Color
+import android.graphics.Rect
+import android.os.Trace
+import android.view.Display
+import android.view.SurfaceControl
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.ui.graphics.toArgb
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.windowdecor.common.DecorThemeUtil
+import com.android.wm.shell.windowdecor.common.Theme
+
+/**
+ * Represents the indicator surface that visualizes the current position of a dragged window during
+ * a multi-display drag operation.
+ *
+ * This class manages the creation, display, and manipulation of the [SurfaceControl]s that act as a
+ * visual indicator, providing feedback to the user about the dragged window's location.
+ */
+class MultiDisplayDragMoveIndicatorSurface(
+ context: Context,
+ taskInfo: RunningTaskInfo,
+ display: Display,
+ surfaceControlBuilderFactory: Factory.SurfaceControlBuilderFactory,
+) {
+ private var isVisible = false
+
+ // A container surface to host the veil background
+ private var veilSurface: SurfaceControl? = null
+
+ private val decorThemeUtil = DecorThemeUtil(context)
+ private val lightColors = dynamicLightColorScheme(context)
+ private val darkColors = dynamicDarkColorScheme(context)
+
+ init {
+ Trace.beginSection("DragIndicatorSurface#init")
+
+ val displayId = display.displayId
+ veilSurface =
+ surfaceControlBuilderFactory
+ .create("Drag indicator veil of Task=${taskInfo.taskId} Display=$displayId")
+ .setColorLayer()
+ .setCallsite("DragIndicatorSurface#init")
+ .setHidden(true)
+ .build()
+
+ // TODO: b/383069173 - Add icon for the surface.
+
+ Trace.endSection()
+ }
+
+ /**
+ * Disposes the indicator surface using the provided [transaction].
+ */
+ fun disposeSurface(transaction: SurfaceControl.Transaction) {
+ veilSurface?.let { veil -> transaction.remove(veil) }
+ veilSurface = null
+ }
+
+ /**
+ * Shows the indicator surface at [bounds] on the specified display ([displayId]),
+ * visualizing the drag of the [taskInfo]. The indicator surface is shown using [transaction],
+ * and the [rootTaskDisplayAreaOrganizer] is used to reparent the surfaces.
+ */
+ fun show(
+ transaction: SurfaceControl.Transaction,
+ taskInfo: RunningTaskInfo,
+ rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+ displayId: Int,
+ bounds: Rect,
+ ) {
+ val backgroundColor =
+ when (decorThemeUtil.getAppTheme(taskInfo)) {
+ Theme.LIGHT -> lightColors.surfaceContainer
+ Theme.DARK -> darkColors.surfaceContainer
+ }
+ val veil = veilSurface ?: return
+ isVisible = true
+
+ rootTaskDisplayAreaOrganizer.reparentToDisplayArea(displayId, veil, transaction)
+ relayout(bounds, transaction, shouldBeVisible = true)
+ transaction.show(veil).setColor(veil, Color.valueOf(backgroundColor.toArgb()).components)
+ transaction.apply()
+ }
+
+ /**
+ * Repositions and resizes the indicator surface based on [bounds] using [transaction]. The
+ * [shouldBeVisible] flag indicates whether the indicator is within the display after relayout.
+ */
+ fun relayout(bounds: Rect, transaction: SurfaceControl.Transaction, shouldBeVisible: Boolean) {
+ if (!isVisible && !shouldBeVisible) {
+ // No need to relayout if the surface is already invisible and should not be visible.
+ return
+ }
+ isVisible = shouldBeVisible
+ val veil = veilSurface ?: return
+ transaction.setCrop(veil, bounds)
+ }
+
+ /**
+ * Factory for creating [MultiDisplayDragMoveIndicatorSurface] instances with the [context].
+ */
+ class Factory(private val context: Context) {
+ private val surfaceControlBuilderFactory: SurfaceControlBuilderFactory =
+ object : SurfaceControlBuilderFactory {}
+
+ /**
+ * Creates a new [MultiDisplayDragMoveIndicatorSurface] instance to visualize the drag
+ * operation of the [taskInfo] on the given [display].
+ */
+ fun create(
+ taskInfo: RunningTaskInfo,
+ display: Display,
+ ) = MultiDisplayDragMoveIndicatorSurface(
+ context,
+ taskInfo,
+ display,
+ surfaceControlBuilderFactory,
+ )
+
+ /**
+ * Interface for creating [SurfaceControl.Builder] instances.
+ *
+ * This provides an abstraction over [SurfaceControl.Builder] creation for testing purposes.
+ */
+ interface SurfaceControlBuilderFactory {
+ fun create(name: String): SurfaceControl.Builder {
+ return SurfaceControl.Builder().setName(name)
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
index 8e026f0..04e8d8d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
@@ -24,6 +24,7 @@
import android.content.res.Resources;
import android.graphics.Rect;
import android.util.DisplayMetrics;
+import android.util.Rational;
import android.util.Size;
import android.view.Gravity;
@@ -41,9 +42,6 @@
private static final String TAG = PipBoundsAlgorithm.class.getSimpleName();
private static final float INVALID_SNAP_FRACTION = -1f;
- // The same value (with the same name) is used in Launcher.
- private static final float PIP_ASPECT_RATIO_MISMATCH_THRESHOLD = 0.01f;
-
@NonNull private final PipBoundsState mPipBoundsState;
@NonNull protected final PipDisplayLayoutState mPipDisplayLayoutState;
@NonNull protected final SizeSpecSource mSizeSpecSource;
@@ -223,9 +221,8 @@
+ " than destination(%s)", sourceRectHint, destinationBounds);
return false;
}
- final float reportedRatio = destinationBounds.width() / (float) destinationBounds.height();
- final float inferredRatio = sourceRectHint.width() / (float) sourceRectHint.height();
- if (Math.abs(reportedRatio - inferredRatio) > PIP_ASPECT_RATIO_MISMATCH_THRESHOLD) {
+ if (!PictureInPictureParams.isSameAspectRatio(sourceRectHint,
+ new Rational(destinationBounds.width(), destinationBounds.height()))) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"isSourceRectHintValidForEnterPip=false, hint(%s) does not match"
+ " destination(%s) aspect ratio", sourceRectHint, destinationBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
index ee3e39e..e9dc613 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
@@ -162,22 +162,31 @@
}
}
+ /** Provide a Shell animation-thread Handler. */
+ @WMSingleton
+ @Provides
+ @ShellAnimationThread
+ public static Handler provideShellAnimationHandler() {
+ HandlerThread animThread = new HandlerThread("wmshell.anim", THREAD_PRIORITY_DISPLAY);
+ animThread.start();
+ if (Build.IS_DEBUGGABLE) {
+ animThread.getLooper().setTraceTag(Trace.TRACE_TAG_WINDOW_MANAGER);
+ animThread.getLooper().setSlowLogThresholdMs(MSGQ_SLOW_DISPATCH_THRESHOLD_MS,
+ MSGQ_SLOW_DELIVERY_THRESHOLD_MS);
+ }
+ return Handler.createAsync(animThread.getLooper());
+ }
+
/**
* Provide a Shell animation-thread Executor.
*/
@WMSingleton
@Provides
@ShellAnimationThread
- public static ShellExecutor provideShellAnimationExecutor() {
- HandlerThread shellAnimationThread = new HandlerThread("wmshell.anim",
- THREAD_PRIORITY_DISPLAY);
- shellAnimationThread.start();
- if (Build.IS_DEBUGGABLE) {
- shellAnimationThread.getLooper().setTraceTag(Trace.TRACE_TAG_WINDOW_MANAGER);
- shellAnimationThread.getLooper().setSlowLogThresholdMs(MSGQ_SLOW_DISPATCH_THRESHOLD_MS,
- MSGQ_SLOW_DELIVERY_THRESHOLD_MS);
- }
- return new HandlerExecutor(Handler.createAsync(shellAnimationThread.getLooper()));
+ public static ShellExecutor provideShellAnimationExecutor(
+ @ShellAnimationThread Handler animHandler
+ ) {
+ return new HandlerExecutor(animHandler);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 2fd8c27..5d5e4d3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -68,6 +68,8 @@
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.LaunchAdjacentController;
+import com.android.wm.shell.common.MultiDisplayDragMoveIndicatorController;
+import com.android.wm.shell.common.MultiDisplayDragMoveIndicatorSurface;
import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -82,6 +84,7 @@
import com.android.wm.shell.desktopmode.DefaultDragToDesktopTransitionHandler;
import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler;
import com.android.wm.shell.desktopmode.DesktopDisplayEventHandler;
+import com.android.wm.shell.desktopmode.DesktopDisplayModeController;
import com.android.wm.shell.desktopmode.DesktopImmersiveController;
import com.android.wm.shell.desktopmode.DesktopMinimizationTransitionHandler;
import com.android.wm.shell.desktopmode.DesktopMixedTransitionHandler;
@@ -428,9 +431,10 @@
Transitions transitions,
DisplayController displayController,
@ShellMainThread ShellExecutor mainExecutor,
- @ShellAnimationThread ShellExecutor animExecutor) {
+ @ShellAnimationThread ShellExecutor animExecutor,
+ @ShellAnimationThread Handler animHandler) {
return new FreeformTaskTransitionHandler(
- transitions, displayController, mainExecutor, animExecutor);
+ transitions, displayController, mainExecutor, animExecutor, animHandler);
}
@WMSingleton
@@ -989,7 +993,8 @@
WindowDecorTaskResourceLoader taskResourceLoader,
RecentsTransitionHandler recentsTransitionHandler,
DesktopModeCompatPolicy desktopModeCompatPolicy,
- DesktopTilingDecorViewModel desktopTilingDecorViewModel
+ DesktopTilingDecorViewModel desktopTilingDecorViewModel,
+ MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController
) {
if (!DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context)) {
return Optional.empty();
@@ -1006,7 +1011,30 @@
windowDecorCaptionHandleRepository, activityOrientationChangeHandler,
focusTransitionObserver, desktopModeEventLogger, desktopModeUiEventLogger,
taskResourceLoader, recentsTransitionHandler, desktopModeCompatPolicy,
- desktopTilingDecorViewModel));
+ desktopTilingDecorViewModel,
+ multiDisplayDragMoveIndicatorController));
+ }
+
+ @WMSingleton
+ @Provides
+ static MultiDisplayDragMoveIndicatorController
+ providesMultiDisplayDragMoveIndicatorController(
+ DisplayController displayController,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ MultiDisplayDragMoveIndicatorSurface.Factory
+ multiDisplayDragMoveIndicatorSurfaceFactory,
+ @ShellDesktopThread ShellExecutor desktopExecutor
+ ) {
+ return new MultiDisplayDragMoveIndicatorController(
+ displayController, rootTaskDisplayAreaOrganizer,
+ multiDisplayDragMoveIndicatorSurfaceFactory, desktopExecutor);
+ }
+
+ @WMSingleton
+ @Provides
+ static MultiDisplayDragMoveIndicatorSurface.Factory
+ providesMultiDisplayDragMoveIndicatorSurfaceFactory(Context context) {
+ return new MultiDisplayDragMoveIndicatorSurface.Factory(context);
}
@WMSingleton
@@ -1074,8 +1102,9 @@
Context context,
@ShellMainThread ShellExecutor mainExecutor,
@ShellAnimationThread ShellExecutor animExecutor,
- @ShellMainThread Handler handler) {
- return new CloseDesktopTaskTransitionHandler(context, mainExecutor, animExecutor, handler);
+ @ShellAnimationThread Handler animHandler) {
+ return new CloseDesktopTaskTransitionHandler(context, mainExecutor, animExecutor,
+ animHandler);
}
@WMSingleton
@@ -1083,9 +1112,10 @@
static DesktopMinimizationTransitionHandler provideDesktopMinimizationTransitionHandler(
@ShellMainThread ShellExecutor mainExecutor,
@ShellAnimationThread ShellExecutor animExecutor,
- DisplayController displayController) {
+ DisplayController displayController,
+ @ShellAnimationThread Handler mainHandler) {
return new DesktopMinimizationTransitionHandler(mainExecutor, animExecutor,
- displayController);
+ displayController, mainHandler);
}
@WMSingleton
@@ -1230,13 +1260,10 @@
static Optional<DesktopDisplayEventHandler> provideDesktopDisplayEventHandler(
Context context,
ShellInit shellInit,
- Transitions transitions,
DisplayController displayController,
- RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
- IWindowManager windowManager,
Optional<DesktopUserRepositories> desktopUserRepositories,
Optional<DesktopTasksController> desktopTasksController,
- ShellTaskOrganizer shellTaskOrganizer
+ Optional<DesktopDisplayModeController> desktopDisplayModeController
) {
if (!DesktopModeStatus.canEnterDesktopMode(context)) {
return Optional.empty();
@@ -1245,13 +1272,10 @@
new DesktopDisplayEventHandler(
context,
shellInit,
- transitions,
displayController,
- rootTaskDisplayAreaOrganizer,
- windowManager,
desktopUserRepositories.get(),
desktopTasksController.get(),
- shellTaskOrganizer));
+ desktopDisplayModeController.get()));
}
@WMSingleton
@@ -1381,6 +1405,29 @@
return new DesktopModeUiEventLogger(uiEventLogger, packageManager);
}
+ @WMSingleton
+ @Provides
+ static Optional<DesktopDisplayModeController> provideDesktopDisplayModeController(
+ Context context,
+ Transitions transitions,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ IWindowManager windowManager,
+ ShellTaskOrganizer shellTaskOrganizer,
+ DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider
+ ) {
+ if (!DesktopModeStatus.canEnterDesktopMode(context)) {
+ return Optional.empty();
+ }
+ return Optional.of(
+ new DesktopDisplayModeController(
+ context,
+ transitions,
+ rootTaskDisplayAreaOrganizer,
+ windowManager,
+ shellTaskOrganizer,
+ desktopWallpaperActivityTokenProvider));
+ }
+
//
// App zoom out
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandler.kt
index 1ce093e..b22a46e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandler.kt
@@ -37,7 +37,6 @@
import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_CLOSE_TASK
import com.android.internal.jank.InteractionJankMonitor
import com.android.wm.shell.common.ShellExecutor
-import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.transition.Transitions
import java.util.function.Supplier
@@ -49,7 +48,7 @@
private val mainExecutor: ShellExecutor,
private val animExecutor: ShellExecutor,
private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() },
- @ShellMainThread private val handler: Handler,
+ private val animHandler: Handler,
) : Transitions.TransitionHandler {
private val runningAnimations = mutableMapOf<IBinder, List<Animator>>()
@@ -95,7 +94,7 @@
interactionJankMonitor.begin(
lastChangeLeash,
context,
- handler,
+ animHandler,
CUJ_DESKTOP_MODE_CLOSE_TASK,
)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
index c38558d..946e795 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
@@ -16,41 +16,25 @@
package com.android.wm.shell.desktopmode
-import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
-import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
-import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
-import android.app.WindowConfiguration.windowingModeToString
import android.content.Context
-import android.provider.Settings
-import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
import android.view.Display.DEFAULT_DISPLAY
-import android.view.IWindowManager
-import android.view.WindowManager.TRANSIT_CHANGE
import android.window.DesktopExperienceFlags
-import android.window.WindowContainerTransaction
import com.android.internal.protolog.ProtoLog
-import com.android.window.flags.Flags
-import com.android.wm.shell.RootTaskDisplayAreaOrganizer
-import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
-import com.android.wm.shell.transition.Transitions
/** Handles display events in desktop mode */
class DesktopDisplayEventHandler(
private val context: Context,
shellInit: ShellInit,
- private val transitions: Transitions,
private val displayController: DisplayController,
- private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
- private val windowManager: IWindowManager,
private val desktopUserRepositories: DesktopUserRepositories,
private val desktopTasksController: DesktopTasksController,
- private val shellTaskOrganizer: ShellTaskOrganizer,
+ private val desktopDisplayModeController: DesktopDisplayModeController,
) : OnDisplaysChangedListener, OnDeskRemovedListener {
private val desktopRepository: DesktopRepository
@@ -70,7 +54,7 @@
override fun onDisplayAdded(displayId: Int) {
if (displayId != DEFAULT_DISPLAY) {
- refreshDisplayWindowingMode()
+ desktopDisplayModeController.refreshDisplayWindowingMode()
}
if (!supportsDesks(displayId)) {
@@ -88,7 +72,7 @@
override fun onDisplayRemoved(displayId: Int) {
if (displayId != DEFAULT_DISPLAY) {
- refreshDisplayWindowingMode()
+ desktopDisplayModeController.refreshDisplayWindowingMode()
}
// TODO: b/362720497 - move desks in closing display to the remaining desk.
@@ -102,65 +86,6 @@
}
}
- private fun refreshDisplayWindowingMode() {
- if (!Flags.enableDisplayWindowingModeSwitching()) return
- // TODO: b/375319538 - Replace the check with a DisplayManager API once it's available.
- val isExtendedDisplayEnabled =
- 0 !=
- Settings.Global.getInt(
- context.contentResolver,
- DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
- 0,
- )
- if (!isExtendedDisplayEnabled) {
- // No action needed in mirror or projected mode.
- return
- }
-
- val hasNonDefaultDisplay =
- rootTaskDisplayAreaOrganizer.getDisplayIds().any { displayId ->
- displayId != DEFAULT_DISPLAY
- }
- val targetDisplayWindowingMode =
- if (hasNonDefaultDisplay) {
- WINDOWING_MODE_FREEFORM
- } else {
- // Use the default display windowing mode when no non-default display.
- windowManager.getWindowingMode(DEFAULT_DISPLAY)
- }
- val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)
- requireNotNull(tdaInfo) { "DisplayAreaInfo of DEFAULT_DISPLAY must be non-null." }
- val currentDisplayWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
- if (currentDisplayWindowingMode == targetDisplayWindowingMode) {
- // Already in the target mode.
- return
- }
-
- logV(
- "As an external display is connected, changing default display's windowing mode from" +
- " ${windowingModeToString(currentDisplayWindowingMode)}" +
- " to ${windowingModeToString(targetDisplayWindowingMode)}"
- )
-
- val wct = WindowContainerTransaction()
- wct.setWindowingMode(tdaInfo.token, targetDisplayWindowingMode)
- shellTaskOrganizer
- .getRunningTasks(DEFAULT_DISPLAY)
- .filter { it.activityType == ACTIVITY_TYPE_STANDARD }
- .forEach {
- // TODO: b/391965153 - Reconsider the logic under multi-desk window hierarchy
- when (it.windowingMode) {
- currentDisplayWindowingMode -> {
- wct.setWindowingMode(it.token, currentDisplayWindowingMode)
- }
- targetDisplayWindowingMode -> {
- wct.setWindowingMode(it.token, WINDOWING_MODE_UNDEFINED)
- }
- }
- }
- transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null)
- }
-
// TODO: b/362720497 - connected/projected display considerations.
private fun supportsDesks(displayId: Int): Boolean =
DesktopModeStatus.canEnterDesktopMode(context)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
new file mode 100644
index 0000000..c9a63ff
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
+import android.app.WindowConfiguration.windowingModeToString
+import android.content.Context
+import android.provider.Settings
+import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
+import android.view.Display.DEFAULT_DISPLAY
+import android.view.IWindowManager
+import android.view.WindowManager.TRANSIT_CHANGE
+import android.window.WindowContainerTransaction
+import com.android.internal.protolog.ProtoLog
+import com.android.window.flags.Flags
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.transition.Transitions
+
+/** Controls the display windowing mode in desktop mode */
+class DesktopDisplayModeController(
+ private val context: Context,
+ private val transitions: Transitions,
+ private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+ private val windowManager: IWindowManager,
+ private val shellTaskOrganizer: ShellTaskOrganizer,
+ private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider,
+) {
+
+ fun refreshDisplayWindowingMode() {
+ if (!Flags.enableDisplayWindowingModeSwitching()) return
+ // TODO: b/375319538 - Replace the check with a DisplayManager API once it's available.
+ val isExtendedDisplayEnabled =
+ 0 !=
+ Settings.Global.getInt(
+ context.contentResolver,
+ DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
+ 0,
+ )
+ if (!isExtendedDisplayEnabled) {
+ // No action needed in mirror or projected mode.
+ return
+ }
+
+ val hasNonDefaultDisplay =
+ rootTaskDisplayAreaOrganizer.getDisplayIds().any { displayId ->
+ displayId != DEFAULT_DISPLAY
+ }
+ val targetDisplayWindowingMode =
+ if (hasNonDefaultDisplay) {
+ WINDOWING_MODE_FREEFORM
+ } else {
+ // Use the default display windowing mode when no non-default display.
+ windowManager.getWindowingMode(DEFAULT_DISPLAY)
+ }
+ val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)
+ requireNotNull(tdaInfo) { "DisplayAreaInfo of DEFAULT_DISPLAY must be non-null." }
+ val currentDisplayWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
+ if (currentDisplayWindowingMode == targetDisplayWindowingMode) {
+ // Already in the target mode.
+ return
+ }
+
+ logV(
+ "As an external display is connected, changing default display's windowing mode from" +
+ " ${windowingModeToString(currentDisplayWindowingMode)}" +
+ " to ${windowingModeToString(targetDisplayWindowingMode)}"
+ )
+
+ val wct = WindowContainerTransaction()
+ wct.setWindowingMode(tdaInfo.token, targetDisplayWindowingMode)
+ shellTaskOrganizer
+ .getRunningTasks(DEFAULT_DISPLAY)
+ .filter { it.activityType == ACTIVITY_TYPE_STANDARD }
+ .forEach {
+ // TODO: b/391965153 - Reconsider the logic under multi-desk window hierarchy
+ when (it.windowingMode) {
+ currentDisplayWindowingMode -> {
+ wct.setWindowingMode(it.token, currentDisplayWindowingMode)
+ }
+ targetDisplayWindowingMode -> {
+ wct.setWindowingMode(it.token, WINDOWING_MODE_UNDEFINED)
+ }
+ }
+ }
+ // The override windowing mode of DesktopWallpaper can be UNDEFINED on fullscreen-display
+ // right after the first launch while its resolved windowing mode is FULLSCREEN. We here
+ // it has the FULLSCREEN override windowing mode.
+ desktopWallpaperActivityTokenProvider.getToken(DEFAULT_DISPLAY)?.let { token ->
+ wct.setWindowingMode(token, WINDOWING_MODE_FULLSCREEN)
+ }
+ transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null)
+ }
+
+ private fun logV(msg: String, vararg arguments: Any?) {
+ ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
+ companion object {
+ private const val TAG = "DesktopDisplayModeController"
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandler.kt
index 728638d..7074e8b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandler.kt
@@ -18,14 +18,17 @@
import android.animation.Animator
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.os.Handler
import android.os.IBinder
-import android.util.DisplayMetrics
import android.view.SurfaceControl.Transaction
import android.window.TransitionInfo
import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.TransitionUtil
import com.android.wm.shell.shared.animation.MinimizeAnimator.create
import com.android.wm.shell.transition.Transitions
@@ -41,6 +44,7 @@
private val mainExecutor: ShellExecutor,
private val animExecutor: ShellExecutor,
private val displayController: DisplayController,
+ private val animHandler: Handler,
) : Transitions.TransitionHandler {
/** Shouldn't handle anything */
@@ -90,10 +94,30 @@
val t = Transaction()
val sc = change.leash
finishTransaction.hide(sc)
- val displayMetrics: DisplayMetrics? =
- change.taskInfo?.let {
- displayController.getDisplayContext(it.displayId)?.getResources()?.displayMetrics
- }
- return displayMetrics?.let { create(it, change, t, onAnimFinish) }
+ val displayContext =
+ change.taskInfo?.let { displayController.getDisplayContext(it.displayId) }
+ if (displayContext == null) {
+ logW(
+ "displayContext is null for taskId=${change.taskInfo?.taskId}, " +
+ "displayId=${change.taskInfo?.displayId}"
+ )
+ return null
+ }
+ return create(
+ displayContext,
+ change,
+ t,
+ onAnimFinish,
+ InteractionJankMonitor.getInstance(),
+ animHandler,
+ )
+ }
+
+ private companion object {
+ private fun logW(msg: String, vararg arguments: Any?) {
+ ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
+ const val TAG = "DesktopMinimizationTransitionHandler"
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
index 031925b..7c6cf4a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
@@ -332,29 +332,22 @@
return false
}
- /**
- * Adds given task to the closing task list for [displayId]'s active desk.
- *
- * TODO: b/389960283 - add explicit [deskId] argument.
- */
- fun addClosingTask(displayId: Int, taskId: Int) {
- val activeDesk =
- desktopData.getActiveDesk(displayId)
- ?: error("Expected active desk in display: $displayId")
- if (activeDesk.closingTasks.add(taskId)) {
- logD(
- "Added closing task=%d displayId=%d deskId=%d",
- taskId,
- displayId,
- activeDesk.deskId,
- )
+ /** Adds given task to the closing task list of its desk. */
+ fun addClosingTask(displayId: Int, deskId: Int?, taskId: Int) {
+ val desk =
+ deskId?.let { desktopData.getDesk(it) }
+ ?: checkNotNull(desktopData.getActiveDesk(displayId)) {
+ "Expected active desk in display: $displayId"
+ }
+ if (desk.closingTasks.add(taskId)) {
+ logD("Added closing task=%d displayId=%d deskId=%d", taskId, displayId, desk.deskId)
} else {
// If the task hasn't been removed from closing list after it disappeared.
logW(
"Task with taskId=%d displayId=%d deskId=%d is already closing",
taskId,
displayId,
- activeDesk.deskId,
+ desk.deskId,
)
}
}
@@ -392,7 +385,8 @@
* Checks if a task is the only visible, non-closing, non-minimized task on the active desk of
* the given display, or any display's active desk if [displayId] is [INVALID_DISPLAY].
*
- * TODO: b/389960283 - add explicit [deskId] argument.
+ * TODO: b/389960283 - consider forcing callers to use [isOnlyVisibleNonClosingTaskInDesk] with
+ * an explicit desk id instead of using this function and defaulting to the active one.
*/
fun isOnlyVisibleNonClosingTask(taskId: Int, displayId: Int = INVALID_DISPLAY): Boolean {
val activeDesks =
@@ -402,14 +396,27 @@
desktopData.getAllActiveDesks()
}
return activeDesks.any { desk ->
- desk.visibleTasks
- .subtract(desk.closingTasks)
- .subtract(desk.minimizedTasks)
- .singleOrNull() == taskId
+ isOnlyVisibleNonClosingTaskInDesk(
+ taskId = taskId,
+ deskId = desk.deskId,
+ displayId = desk.displayId,
+ )
}
}
/**
+ * Checks if a task is the only visible, non-closing, non-minimized task on the given desk of
+ * the given display.
+ */
+ fun isOnlyVisibleNonClosingTaskInDesk(taskId: Int, deskId: Int, displayId: Int): Boolean {
+ val desk = desktopData.getDesk(deskId) ?: return false
+ return desk.visibleTasks
+ .subtract(desk.closingTasks)
+ .subtract(desk.minimizedTasks)
+ .singleOrNull() == taskId
+ }
+
+ /**
* Returns the active tasks in the given display's active desk.
*
* TODO: b/389960283 - migrate callers to [getActiveTaskIdsInDesk].
@@ -686,6 +693,11 @@
* TODO: b/389960283 - add explicit [deskId] argument.
*/
fun setTopTransparentFullscreenTaskId(displayId: Int, taskId: Int) {
+ logD(
+ "Top transparent fullscreen task set for display: taskId=%d, displayId=%d",
+ taskId,
+ displayId,
+ )
desktopData.getActiveDesk(displayId)?.topTransparentFullscreenTaskId = taskId
}
@@ -703,6 +715,11 @@
* TODO: b/389960283 - add explicit [deskId] argument.
*/
fun clearTopTransparentFullscreenTaskId(displayId: Int) {
+ logD(
+ "Top transparent fullscreen task cleared for display: taskId=%d, displayId=%d",
+ desktopData.getActiveDesk(displayId)?.topTransparentFullscreenTaskId,
+ displayId,
+ )
desktopData.getActiveDesk(displayId)?.topTransparentFullscreenTaskId = null
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 93058db..87d9674 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -117,6 +117,7 @@
import com.android.wm.shell.recents.RecentsTransitionStateListener
import com.android.wm.shell.recents.RecentsTransitionStateListener.RecentsTransitionState
import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_NOT_RUNNING
+import com.android.wm.shell.shared.R as SharedR
import com.android.wm.shell.shared.TransitionUtil
import com.android.wm.shell.shared.annotations.ExternalThread
import com.android.wm.shell.shared.annotations.ShellDesktopThread
@@ -801,6 +802,9 @@
): ((IBinder) -> Unit) {
val taskId = taskInfo.taskId
val deskId = taskRepository.getDeskIdForTask(taskInfo.taskId)
+ if (deskId == null && DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ error("Did not find desk for task: $taskId")
+ }
snapEventHandler.removeTaskIfTiled(displayId, taskId)
val shouldExitDesktop =
willExitDesktop(
@@ -818,7 +822,7 @@
shouldEndUpAtHome = true,
)
- taskRepository.addClosingTask(displayId, taskId)
+ taskRepository.addClosingTask(displayId = displayId, deskId = deskId, taskId = taskId)
taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
doesAnyTaskRequireTaskbarRounding(displayId, taskId)
)
@@ -870,6 +874,10 @@
private fun minimizeTaskInner(taskInfo: RunningTaskInfo, minimizeReason: MinimizeReason) {
val taskId = taskInfo.taskId
val deskId = taskRepository.getDeskIdForTask(taskInfo.taskId)
+ if (deskId == null && DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ logW("minimizeTaskInner: desk not found for task: ${taskInfo.taskId}")
+ return
+ }
val displayId = taskInfo.displayId
val wct = WindowContainerTransaction()
@@ -890,10 +898,26 @@
taskInfo = taskInfo,
reason = DesktopImmersiveController.ExitReason.MINIMIZED,
)
-
- wct.reorder(taskInfo.token, false)
- val isLastTask = taskRepository.isOnlyVisibleNonClosingTask(taskId, displayId)
- val transition: IBinder =
+ if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ desksOrganizer.minimizeTask(
+ wct = wct,
+ deskId = checkNotNull(deskId) { "Expected non-null deskId" },
+ task = taskInfo,
+ )
+ } else {
+ wct.reorder(taskInfo.token, /* onTop= */ false)
+ }
+ val isLastTask =
+ if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ taskRepository.isOnlyVisibleNonClosingTaskInDesk(
+ taskId = taskId,
+ deskId = checkNotNull(deskId) { "Expected non-null deskId" },
+ displayId = displayId,
+ )
+ } else {
+ taskRepository.isOnlyVisibleNonClosingTask(taskId = taskId, displayId = displayId)
+ }
+ val transition =
freeformTaskTransitionStarter.startMinimizedModeTransition(wct, taskId, isLastTask)
desktopTasksLimiter.ifPresent {
it.addPendingMinimizeChange(
@@ -1055,7 +1079,8 @@
)
}
- private fun startLaunchTransition(
+ @VisibleForTesting
+ fun startLaunchTransition(
transitionType: Int,
wct: WindowContainerTransaction,
launchingTaskId: Int?,
@@ -1063,34 +1088,52 @@
displayId: Int = DEFAULT_DISPLAY,
unminimizeReason: UnminimizeReason = UnminimizeReason.UNKNOWN,
): IBinder {
+ // TODO: b/397619806 - Consolidate sharable logic with [handleFreeformTaskLaunch].
+ var launchTransaction = wct
val taskIdToMinimize =
addAndGetMinimizeChanges(
displayId,
- wct,
+ launchTransaction,
newTaskId = launchingTaskId,
launchingNewIntent = launchingTaskId == null,
)
val exitImmersiveResult =
desktopImmersiveController.exitImmersiveIfApplicable(
- wct = wct,
+ wct = launchTransaction,
displayId = displayId,
excludeTaskId = launchingTaskId,
reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH,
)
+ var deskIdToActivate: Int? = null
+ if (
+ DesktopExperienceFlags.ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING.isTrue &&
+ !isDesktopModeShowing(displayId)
+ ) {
+ deskIdToActivate =
+ checkNotNull(
+ launchingTaskId?.let { taskRepository.getDeskIdForTask(it) }
+ ?: getDefaultDeskId(displayId)
+ )
+ val activateDeskWct = WindowContainerTransaction()
+ addDeskActivationChanges(deskIdToActivate, activateDeskWct)
+ // Desk activation must be handled before app launch-related transactions.
+ activateDeskWct.merge(launchTransaction, /* transfer= */ true)
+ launchTransaction = activateDeskWct
+ }
val t =
if (remoteTransition == null) {
desktopMixedTransitionHandler.startLaunchTransition(
transitionType = transitionType,
- wct = wct,
+ wct = launchTransaction,
taskId = launchingTaskId,
minimizingTaskId = taskIdToMinimize,
exitingImmersiveTask = exitImmersiveResult.asExit()?.exitingTask,
)
} else if (taskIdToMinimize == null) {
val remoteTransitionHandler = OneShotRemoteHandler(mainExecutor, remoteTransition)
- transitions.startTransition(transitionType, wct, remoteTransitionHandler).also {
- remoteTransitionHandler.setTransition(it)
- }
+ transitions
+ .startTransition(transitionType, launchTransaction, remoteTransitionHandler)
+ .also { remoteTransitionHandler.setTransition(it) }
} else {
val remoteTransitionHandler =
DesktopWindowLimitRemoteHandler(
@@ -1099,9 +1142,9 @@
remoteTransition,
taskIdToMinimize,
)
- transitions.startTransition(transitionType, wct, remoteTransitionHandler).also {
- remoteTransitionHandler.setTransition(it)
- }
+ transitions
+ .startTransition(transitionType, launchTransaction, remoteTransitionHandler)
+ .also { remoteTransitionHandler.setTransition(it) }
}
if (taskIdToMinimize != null) {
addPendingMinimizeTransition(t, taskIdToMinimize, MinimizeReason.TASK_LIMIT)
@@ -1109,6 +1152,24 @@
if (launchingTaskId != null && taskRepository.isMinimizedTask(launchingTaskId)) {
addPendingUnminimizeTransition(t, displayId, launchingTaskId, unminimizeReason)
}
+ if (
+ DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue &&
+ deskIdToActivate != null
+ ) {
+ if (DesktopExperienceFlags.ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING.isTrue) {
+ desksTransitionObserver.addPendingTransition(
+ DeskTransition.ActivateDesk(
+ token = t,
+ displayId = displayId,
+ deskId = deskIdToActivate,
+ )
+ )
+ }
+
+ desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted(
+ FREEFORM_ANIMATION_DURATION
+ )
+ }
exitImmersiveResult.asExit()?.runOnTransitionStart?.invoke(t)
return t
}
@@ -1231,9 +1292,9 @@
// home.
if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
performDesktopExitCleanupIfNeeded(
- task.taskId,
- task.displayId,
- wct,
+ taskId = task.taskId,
+ displayId = task.displayId,
+ wct = wct,
forceToFullscreen = false,
// TODO: b/371096166 - Temporary turing home relaunch off to prevent home stealing
// display focus. Remove shouldEndUpAtHome = false when home focus handling
@@ -1684,6 +1745,9 @@
launchWindowingMode = WINDOWING_MODE_FULLSCREEN
pendingIntentBackgroundActivityStartMode =
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
+ if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
+ launchDisplayId = displayId
+ }
}
val pendingIntent =
PendingIntent.getActivityAsUser(
@@ -1800,6 +1864,7 @@
private fun performDesktopExitCleanupIfNeeded(
taskId: Int,
+ deskId: Int? = null,
displayId: Int,
wct: WindowContainerTransaction,
forceToFullscreen: Boolean,
@@ -1813,13 +1878,14 @@
// |RunOnTransitStart| when the transition is started.
return performDesktopExitCleanUp(
wct = wct,
- deskId = null,
+ deskId = deskId,
displayId = displayId,
willExitDesktop = true,
shouldEndUpAtHome = shouldEndUpAtHome,
)
}
+ /** TODO: b/394268248 - update [deskId] to be non-null. */
private fun performDesktopExitCleanUp(
wct: WindowContainerTransaction,
deskId: Int?,
@@ -2015,7 +2081,9 @@
}
val cornerRadius =
context.resources
- .getDimensionPixelSize(R.dimen.desktop_windowing_freeform_rounded_corner_radius)
+ .getDimensionPixelSize(
+ SharedR.dimen.desktop_windowing_freeform_rounded_corner_radius
+ )
.toFloat()
info.changes
.filter { it.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM }
@@ -2370,17 +2438,28 @@
): WindowContainerTransaction? {
logV("handleTaskClosing")
if (!isDesktopModeShowing(task.displayId)) return null
+ val deskId = taskRepository.getDeskIdForTask(task.taskId)
+ if (deskId == null && DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ return null
+ }
val wct = WindowContainerTransaction()
- performDesktopExitCleanupIfNeeded(
- task.taskId,
- task.displayId,
- wct,
- forceToFullscreen = false,
- )
+ val deactivationRunnable =
+ performDesktopExitCleanupIfNeeded(
+ taskId = task.taskId,
+ deskId = deskId,
+ displayId = task.displayId,
+ wct = wct,
+ forceToFullscreen = false,
+ )
+ deactivationRunnable?.invoke(transition)
if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) {
- taskRepository.addClosingTask(task.displayId, task.taskId)
+ taskRepository.addClosingTask(
+ displayId = task.displayId,
+ deskId = deskId,
+ taskId = task.taskId,
+ )
snapEventHandler.removeTaskIfTiled(task.displayId, task.taskId)
}
@@ -2584,9 +2663,9 @@
wct.setDensityDpi(taskInfo.token, getDefaultDensityDpi())
performDesktopExitCleanupIfNeeded(
- taskInfo.taskId,
- taskInfo.displayId,
- wct,
+ taskId = taskInfo.taskId,
+ displayId = taskInfo.displayId,
+ wct = wct,
forceToFullscreen = false,
shouldEndUpAtHome = false,
)
@@ -2665,10 +2744,9 @@
activateDesk(deskId, remoteTransition)
}
- /** Activates the given desk. */
- fun activateDesk(deskId: Int, remoteTransition: RemoteTransition? = null) {
+ /** Activates the given desk but without starting a transition. */
+ fun addDeskActivationChanges(deskId: Int, wct: WindowContainerTransaction) {
val displayId = taskRepository.getDisplayForDesk(deskId)
- val wct = WindowContainerTransaction()
if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
prepareForDeskActivation(displayId, wct)
desksOrganizer.activateDesk(wct, deskId)
@@ -2681,6 +2759,13 @@
} else {
bringDesktopAppsToFront(displayId, wct)
}
+ }
+
+ /** Activates the given desk. */
+ fun activateDesk(deskId: Int, remoteTransition: RemoteTransition? = null) {
+ val displayId = taskRepository.getDisplayForDesk(deskId)
+ val wct = WindowContainerTransaction()
+ addDeskActivationChanges(deskId, wct)
val transitionType = transitionType(remoteTransition)
val handler =
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
index 81b136d..f9ab359 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
@@ -26,7 +26,6 @@
import android.window.TransitionInfo
import android.window.WindowContainerTransaction
import androidx.annotation.VisibleForTesting
-import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.ShellTaskOrganizer
@@ -176,28 +175,13 @@
return taskChange.mode == TRANSIT_TO_BACK
}
- override fun onTransitionStarting(transition: IBinder) {
- val mActiveTaskDetails = activeTransitionTokensAndTasks[transition]
- val info = mActiveTaskDetails?.transitionInfo ?: return
- val minimizeChange = getMinimizeChange(info, mActiveTaskDetails.taskId) ?: return
- // Begin minimize window CUJ instrumentation.
- interactionJankMonitor.begin(
- minimizeChange.leash,
- context,
- handler,
- CUJ_DESKTOP_MODE_MINIMIZE_WINDOW,
- )
- }
-
private fun getMinimizeChange(info: TransitionInfo, taskId: Int): TransitionInfo.Change? =
info.changes.find { change ->
change.taskInfo?.taskId == taskId && change.mode == TRANSIT_TO_BACK
}
override fun onTransitionMerged(merged: IBinder, playing: IBinder) {
- if (activeTransitionTokensAndTasks.remove(merged) != null) {
- interactionJankMonitor.end(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
- }
+ activeTransitionTokensAndTasks.remove(merged)
pendingTransitionTokensAndTasks.remove(merged)?.let { taskToTransfer ->
pendingTransitionTokensAndTasks[playing] = taskToTransfer
}
@@ -209,13 +193,6 @@
}
override fun onTransitionFinished(transition: IBinder, aborted: Boolean) {
- if (activeTransitionTokensAndTasks.remove(transition) != null) {
- if (aborted) {
- interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
- } else {
- interactionJankMonitor.end(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
- }
- }
pendingTransitionTokensAndTasks.remove(transition)
activeUnminimizeTransitionTokensAndTasks.remove(transition)
pendingUnminimizeTransitionTokensAndTasks.remove(transition)
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/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt
index 0f2f371..fc359d7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt
@@ -40,9 +40,19 @@
task: ActivityManager.RunningTaskInfo,
)
+ /** Minimizes the given task of the given deskId. */
+ fun minimizeTask(
+ wct: WindowContainerTransaction,
+ deskId: Int,
+ task: ActivityManager.RunningTaskInfo,
+ )
+
/** Whether the change is for the given desk id. */
fun isDeskChange(change: TransitionInfo.Change, deskId: Int): Boolean
+ /** Whether the change is for a known desk. */
+ fun isDeskChange(change: TransitionInfo.Change): Boolean
+
/**
* Returns the desk id in which the task in the given change is located at the end of a
* transition, if any.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
index 339932c..f576258 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
@@ -15,7 +15,9 @@
*/
package com.android.wm.shell.desktopmode.multidesks
+import android.annotation.SuppressLint
import android.app.ActivityManager.RunningTaskInfo
+import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
@@ -25,6 +27,7 @@
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.window.DesktopExperienceFlags
import android.window.TransitionInfo
+import android.window.WindowContainerToken
import android.window.WindowContainerTransaction
import androidx.core.util.forEach
import com.android.internal.annotations.VisibleForTesting
@@ -43,8 +46,12 @@
private val shellTaskOrganizer: ShellTaskOrganizer,
) : DesksOrganizer, ShellTaskOrganizer.TaskListener {
- private val deskCreateRequests = mutableListOf<CreateRequest>()
- @VisibleForTesting val roots = SparseArray<DeskRoot>()
+ private val createDeskRootRequests = mutableListOf<CreateDeskRequest>()
+ @VisibleForTesting val deskRootsByDeskId = SparseArray<DeskRoot>()
+ private val createDeskMinimizationRootRequests =
+ mutableListOf<CreateDeskMinimizationRootRequest>()
+ @VisibleForTesting
+ val deskMinimizationRootsByDeskId: MutableMap<Int, DeskMinimizationRoot> = mutableMapOf()
init {
if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
@@ -57,7 +64,7 @@
override fun createDesk(displayId: Int, callback: OnCreateCallback) {
logV("createDesk in display: %d", displayId)
- deskCreateRequests += CreateRequest(displayId, callback)
+ createDeskRootRequests += CreateDeskRequest(displayId, callback)
shellTaskOrganizer.createRootTask(
displayId,
WINDOWING_MODE_FREEFORM,
@@ -68,14 +75,14 @@
override fun removeDesk(wct: WindowContainerTransaction, deskId: Int) {
logV("removeDesk %d", deskId)
- val desk = checkNotNull(roots[deskId]) { "Root not found for desk: $deskId" }
- wct.removeRootTask(desk.taskInfo.token)
+ deskRootsByDeskId[deskId]?.let { root -> wct.removeRootTask(root.token) }
+ deskMinimizationRootsByDeskId[deskId]?.let { root -> wct.removeRootTask(root.token) }
}
override fun activateDesk(wct: WindowContainerTransaction, deskId: Int) {
logV("activateDesk %d", deskId)
- val root = checkNotNull(roots[deskId]) { "Root not found for desk: $deskId" }
- wct.reorder(root.taskInfo.token, /* onTop= */ true)
+ val root = checkNotNull(deskRootsByDeskId[deskId]) { "Root not found for desk: $deskId" }
+ wct.reorder(root.token, /* onTop= */ true)
wct.setLaunchRoot(
/* container= */ root.taskInfo.token,
/* windowingModes= */ intArrayOf(WINDOWING_MODE_FREEFORM, WINDOWING_MODE_UNDEFINED),
@@ -85,7 +92,7 @@
override fun deactivateDesk(wct: WindowContainerTransaction, deskId: Int) {
logV("deactivateDesk %d", deskId)
- val root = checkNotNull(roots[deskId]) { "Root not found for desk: $deskId" }
+ val root = checkNotNull(deskRootsByDeskId[deskId]) { "Root not found for desk: $deskId" }
wct.setLaunchRoot(
/* container= */ root.taskInfo.token,
/* windowingModes= */ null,
@@ -98,16 +105,58 @@
deskId: Int,
task: RunningTaskInfo,
) {
- val root = roots[deskId] ?: error("Root not found for desk: $deskId")
+ val root = deskRootsByDeskId[deskId] ?: error("Root not found for desk: $deskId")
wct.setWindowingMode(task.token, WINDOWING_MODE_UNDEFINED)
wct.reparent(task.token, root.taskInfo.token, /* onTop= */ true)
}
- override fun isDeskChange(change: TransitionInfo.Change, deskId: Int): Boolean =
- roots.contains(deskId) && change.taskInfo?.taskId == deskId
+ override fun minimizeTask(wct: WindowContainerTransaction, deskId: Int, task: RunningTaskInfo) {
+ val deskRoot =
+ checkNotNull(deskRootsByDeskId[deskId]) { "Root not found for desk: $deskId" }
+ val minimizationRoot =
+ checkNotNull(deskMinimizationRootsByDeskId[deskId]) {
+ "Minimization root not found for desk: $deskId"
+ }
+ val taskId = task.taskId
+ if (taskId in minimizationRoot.children) {
+ logV("Task #$taskId is already minimized in desk #$deskId")
+ return
+ }
+ if (taskId !in deskRoot.children) {
+ logE("Attempted to minimize task=${task.taskId} in desk=$deskId but it was not a child")
+ return
+ }
+ wct.reparent(task.token, minimizationRoot.token, /* onTop= */ true)
+ }
- override fun getDeskAtEnd(change: TransitionInfo.Change): Int? =
- change.taskInfo?.parentTaskId?.takeIf { it in roots }
+ override fun isDeskChange(change: TransitionInfo.Change, deskId: Int): Boolean =
+ (isDeskRootChange(change) && change.taskId == deskId) ||
+ (getDeskMinimizationRootInChange(change)?.deskId == deskId)
+
+ override fun isDeskChange(change: TransitionInfo.Change): Boolean =
+ isDeskRootChange(change) || getDeskMinimizationRootInChange(change) != null
+
+ private fun isDeskRootChange(change: TransitionInfo.Change): Boolean =
+ change.taskId in deskRootsByDeskId
+
+ private fun getDeskMinimizationRootInChange(
+ change: TransitionInfo.Change
+ ): DeskMinimizationRoot? =
+ deskMinimizationRootsByDeskId.values.find { it.rootId == change.taskId }
+
+ private val TransitionInfo.Change.taskId: Int
+ get() = taskInfo?.taskId ?: INVALID_TASK_ID
+
+ override fun getDeskAtEnd(change: TransitionInfo.Change): Int? {
+ val parentTaskId = change.taskInfo?.parentTaskId ?: return null
+ if (parentTaskId in deskRootsByDeskId) {
+ return parentTaskId
+ }
+ val deskMinimizationRoot =
+ deskMinimizationRootsByDeskId.values.find { root -> root.rootId == parentTaskId }
+ ?: return null
+ return deskMinimizationRoot.deskId
+ }
override fun isDeskActiveAtEnd(change: TransitionInfo.Change, deskId: Int): Boolean =
change.taskInfo?.taskId == deskId &&
@@ -115,51 +164,176 @@
change.mode == TRANSIT_TO_FRONT
override fun onTaskAppeared(taskInfo: RunningTaskInfo, leash: SurfaceControl) {
- if (taskInfo.parentTaskId in roots) {
+ // Check whether this task is appearing inside a desk.
+ if (taskInfo.parentTaskId in deskRootsByDeskId) {
val deskId = taskInfo.parentTaskId
val taskId = taskInfo.taskId
logV("Task #$taskId appeared in desk #$deskId")
addChildToDesk(taskId = taskId, deskId = deskId)
return
}
- val deskId = taskInfo.taskId
- check(deskId !in roots) { "A root already exists for desk: $deskId" }
- val request =
- checkNotNull(deskCreateRequests.firstOrNull { it.displayId == taskInfo.displayId }) {
- "Task ${taskInfo.taskId} appeared without pending create request"
- }
- logV("Desk #$deskId appeared")
- roots[deskId] = DeskRoot(deskId, taskInfo, leash)
- deskCreateRequests.remove(request)
- request.onCreateCallback.onCreated(deskId)
+ // Check whether this task is appearing in a minimization root.
+ val minimizationRoot =
+ deskMinimizationRootsByDeskId.values.singleOrNull { it.rootId == taskInfo.parentTaskId }
+ if (minimizationRoot != null) {
+ val deskId = minimizationRoot.deskId
+ val taskId = taskInfo.taskId
+ logV("Task #$taskId was minimized in desk #$deskId ")
+ addChildToMinimizationRoot(taskId = taskId, deskId = deskId)
+ return
+ }
+ // The appearing task is a root (either a desk or a minimization root), it should not exist
+ // already.
+ check(taskInfo.taskId !in deskRootsByDeskId) {
+ "A root already exists for desk: ${taskInfo.taskId}"
+ }
+ check(deskMinimizationRootsByDeskId.values.none { it.rootId == taskInfo.taskId }) {
+ "A minimization root already exists with rootId: ${taskInfo.taskId}"
+ }
+
+ val appearingInDisplayId = taskInfo.displayId
+ // Check if there's any pending desk creation requests under this display.
+ val deskRequest =
+ createDeskRootRequests.firstOrNull { it.displayId == appearingInDisplayId }
+ if (deskRequest != null) {
+ // Appearing root matches desk request.
+ val deskId = taskInfo.taskId
+ logV("Desk #$deskId appeared")
+ deskRootsByDeskId[deskId] = DeskRoot(deskId, taskInfo, leash)
+ createDeskRootRequests.remove(deskRequest)
+ deskRequest.onCreateCallback.onCreated(deskId)
+ createDeskMinimizationRoot(displayId = appearingInDisplayId, deskId = deskId)
+ return
+ }
+ // Check if there's any pending minimization container creation requests under this display.
+ val deskMinimizationRootRequest =
+ createDeskMinimizationRootRequests.first { it.displayId == appearingInDisplayId }
+ val deskId = deskMinimizationRootRequest.deskId
+ logV("Minimization container for desk #$deskId appeared with id=${taskInfo.taskId}")
+ val deskMinimizationRoot = DeskMinimizationRoot(deskId, taskInfo, leash)
+ deskMinimizationRootsByDeskId[deskId] = deskMinimizationRoot
+ createDeskMinimizationRootRequests.remove(deskMinimizationRootRequest)
+ hideMinimizationRoot(deskMinimizationRoot)
}
override fun onTaskInfoChanged(taskInfo: RunningTaskInfo) {
- if (roots.contains(taskInfo.taskId)) {
+ if (deskRootsByDeskId.contains(taskInfo.taskId)) {
val deskId = taskInfo.taskId
- roots[deskId] = roots[deskId].copy(taskInfo = taskInfo)
+ deskRootsByDeskId[deskId] = deskRootsByDeskId[deskId].copy(taskInfo = taskInfo)
+ logV("Desk #$deskId's task info changed")
+ return
}
+ val minimizationRoot =
+ deskMinimizationRootsByDeskId.values.find { root -> root.rootId == taskInfo.taskId }
+ if (minimizationRoot != null) {
+ deskMinimizationRootsByDeskId.remove(minimizationRoot.deskId)
+ deskMinimizationRootsByDeskId[minimizationRoot.deskId] =
+ minimizationRoot.copy(taskInfo = taskInfo)
+ logV("Minimization root for desk#${minimizationRoot.deskId} task info changed")
+ return
+ }
+
+ val parentTaskId = taskInfo.parentTaskId
+ if (parentTaskId in deskRootsByDeskId) {
+ val deskId = taskInfo.parentTaskId
+ val taskId = taskInfo.taskId
+ logV("onTaskInfoChanged: Task #$taskId appeared in desk #$deskId")
+ addChildToDesk(taskId = taskId, deskId = deskId)
+ return
+ }
+ // Check whether this task is appearing in a minimization root.
+ val parentMinimizationRoot =
+ deskMinimizationRootsByDeskId.values.singleOrNull { it.rootId == parentTaskId }
+ if (parentMinimizationRoot != null) {
+ val deskId = parentMinimizationRoot.deskId
+ val taskId = taskInfo.taskId
+ logV("onTaskInfoChanged: Task #$taskId was minimized in desk #$deskId ")
+ addChildToMinimizationRoot(taskId = taskId, deskId = deskId)
+ return
+ }
+ logE("onTaskInfoChanged: unknown task: ${taskInfo.taskId}")
}
override fun onTaskVanished(taskInfo: RunningTaskInfo) {
- if (roots.contains(taskInfo.taskId)) {
+ if (deskRootsByDeskId.contains(taskInfo.taskId)) {
val deskId = taskInfo.taskId
- val deskRoot = roots[deskId]
+ val deskRoot = deskRootsByDeskId[deskId]
// Use the last saved taskInfo to obtain the displayId. Using the local one here will
// return -1 since the task is not unassociated with a display.
val displayId = deskRoot.taskInfo.displayId
logV("Desk #$deskId vanished from display #$displayId")
- roots.remove(deskId)
+ deskRootsByDeskId.remove(deskId)
return
}
+ val deskMinimizationRoot =
+ deskMinimizationRootsByDeskId.values.singleOrNull { it.rootId == taskInfo.taskId }
+ if (deskMinimizationRoot != null) {
+ logV("Minimization root for desk ${deskMinimizationRoot.deskId} vanished")
+ deskMinimizationRootsByDeskId.remove(deskMinimizationRoot.deskId)
+ return
+ }
+
+ // Check whether the vanishing task was a child of any desk.
// At this point, [parentTaskId] may be unset even if this is a task vanishing from a desk,
// so search through each root to remove this if it's a child.
- roots.forEach { deskId, deskRoot ->
+ deskRootsByDeskId.forEach { deskId, deskRoot ->
if (deskRoot.children.remove(taskInfo.taskId)) {
logV("Task #${taskInfo.taskId} vanished from desk #$deskId")
return
}
}
+ // Check whether the vanishing task was a child of the minimized root and remove it.
+ deskMinimizationRootsByDeskId.values.forEach { root ->
+ val taskId = taskInfo.taskId
+ if (root.children.remove(taskId)) {
+ logV("Task #$taskId vanished from minimization root of desk #${root.deskId}")
+ return
+ }
+ }
+ }
+
+ private fun createDeskMinimizationRoot(displayId: Int, deskId: Int) {
+ createDeskMinimizationRootRequests +=
+ CreateDeskMinimizationRootRequest(displayId = displayId, deskId = deskId)
+ shellTaskOrganizer.createRootTask(
+ displayId,
+ WINDOWING_MODE_FREEFORM,
+ /* listener = */ this,
+ /* removeWithTaskOrganizer = */ true,
+ )
+ }
+
+ @SuppressLint("MissingPermission")
+ private fun hideMinimizationRoot(root: DeskMinimizationRoot) {
+ shellTaskOrganizer.applyTransaction(
+ WindowContainerTransaction().apply { setHidden(root.token, /* hidden= */ true) }
+ )
+ }
+
+ private fun addChildToDesk(taskId: Int, deskId: Int) {
+ deskRootsByDeskId.forEach { _, deskRoot ->
+ if (deskRoot.deskId == deskId) {
+ deskRoot.children.add(taskId)
+ } else {
+ deskRoot.children.remove(taskId)
+ }
+ }
+ // A task cannot be in both a desk root and a minimization root at the same time, so make
+ // sure to remove them if needed.
+ deskMinimizationRootsByDeskId.values.forEach { root -> root.children.remove(taskId) }
+ }
+
+ private fun addChildToMinimizationRoot(taskId: Int, deskId: Int) {
+ deskMinimizationRootsByDeskId.forEach { _, minimizationRoot ->
+ if (minimizationRoot.deskId == deskId) {
+ minimizationRoot.children += taskId
+ } else {
+ minimizationRoot.children -= taskId
+ }
+ }
+ // A task cannot be in both a desk root and a minimization root at the same time, so make
+ // sure to remove them if needed.
+ deskRootsByDeskId.forEach { _, deskRoot -> deskRoot.children -= taskId }
}
@VisibleForTesting
@@ -168,34 +342,55 @@
val taskInfo: RunningTaskInfo,
val leash: SurfaceControl,
val children: MutableSet<Int> = mutableSetOf(),
+ ) {
+ val token: WindowContainerToken = taskInfo.token
+ }
+
+ @VisibleForTesting
+ data class DeskMinimizationRoot(
+ val deskId: Int,
+ val taskInfo: RunningTaskInfo,
+ val leash: SurfaceControl,
+ val children: MutableSet<Int> = mutableSetOf(),
+ ) {
+ val rootId: Int
+ get() = taskInfo.taskId
+
+ val token: WindowContainerToken = taskInfo.token
+ }
+
+ private data class CreateDeskRequest(
+ val displayId: Int,
+ val onCreateCallback: OnCreateCallback,
)
+ private data class CreateDeskMinimizationRootRequest(val displayId: Int, val deskId: Int)
+
+ private fun logV(msg: String, vararg arguments: Any?) {
+ ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
+ private fun logE(msg: String, vararg arguments: Any?) {
+ ProtoLog.e(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
override fun dump(pw: PrintWriter, prefix: String) {
val innerPrefix = "$prefix "
pw.println("$prefix$TAG")
pw.println("${innerPrefix}Desk Roots:")
- roots.forEach { deskId, root ->
+ deskRootsByDeskId.forEach { deskId, root ->
+ val minimizationRoot = deskMinimizationRootsByDeskId[deskId]
pw.println("$innerPrefix #$deskId visible=${root.taskInfo.isVisible}")
+ pw.println("$innerPrefix displayId=${root.taskInfo.displayId}")
pw.println("$innerPrefix children=${root.children}")
- }
- }
-
- private fun addChildToDesk(taskId: Int, deskId: Int) {
- roots.forEach { _, deskRoot ->
- if (deskRoot.deskId == deskId) {
- deskRoot.children.add(taskId)
- } else {
- deskRoot.children.remove(taskId)
+ pw.println("$innerPrefix minimization root:")
+ pw.println("$innerPrefix rootId=${minimizationRoot?.rootId}")
+ if (minimizationRoot != null) {
+ pw.println("$innerPrefix children=${minimizationRoot.children}")
}
}
}
- private data class CreateRequest(val displayId: Int, val onCreateCallback: OnCreateCallback)
-
- private fun logV(msg: String, vararg arguments: Any?) {
- ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
- }
-
companion object {
private const val TAG = "RootTaskDesksOrganizer"
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/README.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/README.md
index 3fad28a..a98ae55 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/README.md
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/README.md
@@ -9,6 +9,7 @@
4) [Threading model in the Shell](threading.md)
5) [Making changes in the Shell](changes.md)
6) [Extending the Shell for Products/OEMs](extending.md)
+6) [Shell transitions](transitions.md)
7) [Debugging in the Shell](debugging.md)
8) [Testing in the Shell](testing.md)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/transitions.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/transitions.md
new file mode 100644
index 0000000..dc23bb0
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/transitions.md
@@ -0,0 +1,118 @@
+# Shell transitions
+[Back to home](README.md)
+
+---
+
+## General
+
+General guides for using Shell Transitions can be found here:
+- [Shell transitions animation guide](http://go/shell-transit-anim)
+- [Hitchhiker's guide to transitions](http://go/transitions-book)
+
+## Transient-launch transitions
+<span style="color:orange">Use with care!</span>
+
+Transient-launch transitions are a way to handle non-atomic (ie. gestural) transitions by allowing
+WM Core to put participating activities into a transiently visible or hidden state for the duration
+of the animation and adding the ability to cancel the transition.
+
+For example, if you are launching an activity normally, WM Core will be updated
+at the start of the animation which includes pausing the previous activity and resuming the next
+activity (and subsequently the transition will reconcile that state via an animation).
+
+If you are transiently launching an activity though, WM Core will ensure that both the leaving
+activity and the incoming activity will be RESUMED for the duration of the transition duration. In
+addition, WM Core will track the position of the transient-launch activity in the window hierarchy
+prior to the launch, and allow Shell to restore it to that position if the transitions needs to be
+canceled.
+
+Starting a transient-launch transition can be done via the activity options (since the activity may
+not have been started yet):
+```kotlin
+val opts = ActivityOptions.makeBasic().setTransientLaunch()
+val wct = WindowContainerTransaction()
+wct.sendPendingIntent(pendingIntent, new Intent(), opts.toBundle())
+transitions.startTransition(TRANSIT_OPEN, wct, ...)
+```
+
+And restoring the transient order via a WCT:
+```kotlin
+val wct = WindowContainerTransaction()
+wct.restoreTransientOrder(transientLaunchContainerToken)
+transitions.startTransition(TRANSIT_RESTORE, wct, ...)
+```
+
+### <span style="color:orange">Considerations</span>
+
+Usage of transient-launch transitions should be done with consideration, there are a few gotchas
+that might result in subtle and hard-to-reproduce issues.
+
+#### Understanding the flow
+When starting a transient-launch transition, there are several possible outcomes:
+1) The transition finishes as normal: The user is committing the transition to the state requested
+ at the start of the transition. In such cases, you can simply finish the transition and the
+ states of the transiently shown/hidden activities will be updated to match the original state
+ that a non-transient transition would have (ie. closing activities will be stopped).
+
+2) The transition is interrupted: A change in the system results in the window hierarchy changing
+ in a way which may or may not affect the transient-launch activity. eg. We transiently-launch
+ home from app A, but then app B launches. In this case, WM attempts to create a new transition
+ reflecting the window hierarchy changes (ie. if B occludes Home in the above example, then the
+ transition will have Home TO_BACK, and B TO_FRONT).
+
+ At this point, the transition handler can choose to merge the incoming transition or not (to
+ queue it after this current transition). Take note of the next section for concerns re. bookend
+ transitions.
+
+3) The transition is canceled: The user is canceling the transition to the previous state. In such
+ cases, you need to store the `WindowContainerToken` for the task associated with the
+ transient-launch activity, and restore the transient order via the `WindowContainerTransaction`
+ API above. In some cases, if anything has been reordered since (ie. due to other merged
+ transitions), then you may also want to use `WindowContainerTransaction#reorder()` to place all
+ the relevant containers to their original order (provided via the change-order in the initial
+ launch transition).
+
+#### Finishing the transient-launch transition
+
+When restoring the transient order in the 3rd flow above, it is recommended to do it in a new
+transition and <span style="color:orange">**not**</span> via the WindowContainerTransaction in
+`TransitionFinishCallback#onTransitionFinished()` provided when starting the transition.
+
+Changes to the window hierarchy via the finish transaction are not applied in sync with other
+transitions that are collecting and aplying, and are also not observable in Shell in any way.
+Starting a new transition instead ensures both. (The finish transaction can still be used if there
+are non-transition affecting properties (ie. container properties) that need to be updated as a part
+of finishing the transient-launch transition).
+
+So the general idea is when restoring is:
+
+1) Start transient-launch transition START_T
+2) ...
+3) All done, start bookend transition END_T
+4) Handler receives END_T, merges it and then finishes START_T
+
+In practice it's not quite that simple, due to the ordering of transitions and extra care must be
+taken when using a new transition to prevent deadlocking when merging transitions.
+
+When a new transition arrives while a transient-launch transition is playing, the handler can
+choose to handle/merge the transition into the ongoing one, or skip merging to queue it up to be
+played after. In the above flow, we can see how this might result in a deadlock:
+
+Queueing END during merge:
+1) Start transient-launch transition START_T
+2) ...
+3) Incoming transition OTHER_T, choose to cancel START_T -> start bookend transition END_T, but don't merge OTHER_T
+3) Waiting for END_T... <span style="color:red">Deadlock!</span>
+
+Interrupt while pending END:
+1) Start transient-launch transition START_T
+2) ...
+3) All done, start bookend transition END_T
+3) Incoming transition OTHER_T occurs before END_T, but don't merge OTHER_T
+3) Waiting for END_T... <span style="color:red">Deadlock!</span>
+
+This means that when using transient-launch transitions with a bookend transition
+<span style="color:orange">requires</span> you to handle any incoming transitions if the bookend is
+ever queued (or already posted) after it. You can do so by preempting the bookend transition
+(finishing the transient-launch transition), or handling the merge of the new transition (so it
+doesn't queue).
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
index b60fb5e..16e411e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
@@ -25,10 +25,12 @@
import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
+import android.content.Context;
import android.graphics.Rect;
+import android.os.Handler;
import android.os.IBinder;
import android.util.ArrayMap;
-import android.util.DisplayMetrics;
+import android.util.Log;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.TransitionInfo;
@@ -38,6 +40,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.shared.animation.MinimizeAnimator;
@@ -52,11 +55,13 @@
*/
public class FreeformTaskTransitionHandler
implements Transitions.TransitionHandler, FreeformTaskTransitionStarter {
+ private static final String TAG = "FreeformTaskTransitionHandler";
private static final int CLOSE_ANIM_DURATION = 400;
private final Transitions mTransitions;
private final DisplayController mDisplayController;
private final ShellExecutor mMainExecutor;
private final ShellExecutor mAnimExecutor;
+ private final Handler mAnimHandler;
private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
@@ -66,11 +71,13 @@
Transitions transitions,
DisplayController displayController,
ShellExecutor mainExecutor,
- ShellExecutor animExecutor) {
+ ShellExecutor animExecutor,
+ Handler animHandler) {
mTransitions = transitions;
mDisplayController = displayController;
mMainExecutor = mainExecutor;
mAnimExecutor = animExecutor;
+ mAnimHandler = animHandler;
}
@Override
@@ -123,13 +130,11 @@
@NonNull Transitions.TransitionFinishCallback finishCallback) {
boolean transitionHandled = false;
final ArrayList<Animator> animations = new ArrayList<>();
- final Runnable onAnimFinish = () -> {
+ final Runnable onAnimFinish = () -> mMainExecutor.execute(() -> {
if (!animations.isEmpty()) return;
- mMainExecutor.execute(() -> {
- mAnimations.remove(transition);
- finishCallback.onTransitionFinished(null /* wct */);
- });
- };
+ mAnimations.remove(transition);
+ finishCallback.onTransitionFinished(null /* wct */);
+ });
for (TransitionInfo.Change change : info.getChanges()) {
if ((change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0) {
continue;
@@ -234,18 +239,25 @@
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
SurfaceControl sc = change.getLeash();
finishT.hide(sc);
- final DisplayMetrics displayMetrics =
- mDisplayController
- .getDisplayContext(taskInfo.displayId).getResources().getDisplayMetrics();
+ final Context displayContext =
+ mDisplayController.getDisplayContext(taskInfo.displayId);
+ if (displayContext == null) {
+ Log.w(TAG, "No displayContext for displayId=" + taskInfo.displayId);
+ return false;
+ }
final Animator animator = MinimizeAnimator.create(
- displayMetrics,
+ displayContext,
change,
t,
(anim) -> {
- animations.remove(anim);
- onAnimFinish.run();
+ mMainExecutor.execute(() -> {
+ animations.remove(anim);
+ onAnimFinish.run();
+ });
return null;
- });
+ },
+ InteractionJankMonitor.getInstance(),
+ mAnimHandler);
animations.add(animator);
return true;
}
@@ -277,8 +289,10 @@
new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- animations.remove(animator);
- onAnimFinish.run();
+ mMainExecutor.execute(() -> {
+ animations.remove(animator);
+ onAnimFinish.run();
+ });
}
});
animations.add(animator);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java
index c4d065f..e0cae81 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipSurfaceTransactionHelper.java
@@ -134,6 +134,18 @@
}
/**
+ * Operates the round corner radius on a given transaction and leash, scaled by bounds
+ * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
+ */
+ public PipSurfaceTransactionHelper round(SurfaceControl.Transaction tx, SurfaceControl leash,
+ Rect fromBounds, Rect toBounds) {
+ final float scale = (float) (Math.hypot(fromBounds.width(), fromBounds.height())
+ / Math.hypot(toBounds.width(), toBounds.height()));
+ tx.setCornerRadius(leash, mCornerRadius * scale);
+ return this;
+ }
+
+ /**
* Operates the shadow radius on a given transaction and leash
* @return same {@link PipSurfaceTransactionHelper} instance for method chaining
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
index 06e8349..eb5fe88 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
@@ -173,7 +173,7 @@
transformTensor.postRotate(degrees, targetBounds.centerX(), targetBounds.centerY());
tx.setMatrix(leash, transformTensor, mMatrixTmp)
- .setCornerRadius(leash, cornerRadius)
+ .setCornerRadius(leash, cornerRadius / scaleX)
.setShadowRadius(leash, shadowRadius);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index 99c9302..1ce24f7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -404,6 +404,10 @@
mPipBoundsState.setBoundsStateForEntry(componentName, activityInfo, pictureInPictureParams,
mPipBoundsAlgorithm);
+
+ // Update the size spec in case aspect ratio is invariant, but display has changed
+ // since the last PiP session, or this is the first PiP session altogether.
+ mPipBoundsState.updateMinMaxSize(mPipBoundsState.getAspectRatio());
return mPipBoundsAlgorithm.getEntryDestinationBounds();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
index df7a25a..7805ec3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
@@ -55,6 +55,7 @@
private PipTransitionController mPipTransitionController;
private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
+ private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
@Nullable private Runnable mUpdateMovementBoundsRunnable;
@@ -75,6 +76,7 @@
mSurfaceControlTransactionFactory =
new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
+ mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(mContext);
mPipAlphaAnimatorSupplier = PipAlphaAnimator::new;
}
@@ -214,6 +216,8 @@
transformTensor.postTranslate(toBounds.left, toBounds.top);
transformTensor.postRotate(degrees, toBounds.centerX(), toBounds.centerY());
+ mPipSurfaceTransactionHelper.round(tx, leash, mPipBoundsState.getBounds(), toBounds);
+
tx.setMatrix(leash, transformTensor, mMatrixTmp);
tx.apply();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 035c93d..0cfab11 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -50,6 +50,7 @@
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.SystemProperties;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.TransitionInfo;
@@ -105,7 +106,10 @@
* The fixed start delay in ms when fading out the content overlay from bounds animation.
* The fadeout animation is guaranteed to start after the client has drawn under the new config.
*/
- private static final int CONTENT_OVERLAY_FADE_OUT_DELAY_MS = 500;
+ private static final int EXTRA_CONTENT_OVERLAY_FADE_OUT_DELAY_MS =
+ SystemProperties.getInt(
+ "persist.wm.debug.extra_content_overlay_fade_out_delay_ms", 400);
+ private static final int CONTENT_OVERLAY_FADE_OUT_DURATION_MS = 500;
//
// Dependencies
@@ -551,7 +555,8 @@
@NonNull Runnable onAnimationEnd) {
PipAlphaAnimator animator = new PipAlphaAnimator(mContext, overlayLeash,
null /* startTx */, null /* finishTx */, PipAlphaAnimator.FADE_OUT);
- animator.setDuration(CONTENT_OVERLAY_FADE_OUT_DELAY_MS);
+ animator.setDuration(CONTENT_OVERLAY_FADE_OUT_DURATION_MS);
+ animator.setStartDelay(EXTRA_CONTENT_OVERLAY_FADE_OUT_DELAY_MS);
animator.setAnimationEndCallback(onAnimationEnd);
animator.start();
}
@@ -738,6 +743,13 @@
}
}
+ if (!mPipTransitionState.isInSwipePipToHomeTransition()) {
+ // Update the size spec in case aspect ratio is invariant, but display has changed
+ // since the last PiP session, or this is the first PiP session altogether.
+ // Skip the update if in swipe PiP to home, as this has already been done.
+ mPipBoundsState.updateMinMaxSize(mPipBoundsState.getAspectRatio());
+ }
+
// calculate the entry bounds and notify core to move task to pinned with final bounds
final Rect entryBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
mPipBoundsState.setBounds(entryBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index 847a038..3e03e00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -47,6 +47,7 @@
import android.app.ActivityTaskManager;
import android.app.IApplicationThread;
import android.app.PendingIntent;
+import android.app.WindowConfiguration;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
@@ -75,11 +76,11 @@
import com.android.internal.os.IResultReceiver;
import com.android.internal.protolog.ProtoLog;
import com.android.wm.shell.Flags;
-import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.R;
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.HomeTransitionObserver;
@@ -320,7 +321,7 @@
"RecentsTransitionHandler.mergeAnimation: no controller found");
return;
}
- controller.merge(info, startT, finishT, mergeTarget, finishCallback);
+ controller.merge(info, startT, finishT, finishCallback);
}
@Override
@@ -408,7 +409,8 @@
// next called.
private Pair<int[], TaskSnapshot[]> mPendingPauseSnapshotsForCancel;
- // Used to track a pending finish transition
+ // Used to track a pending finish transition, this is only non-null if
+ // enableRecentsBookendTransition() is enabled
private IBinder mPendingFinishTransition;
private IResultReceiver mPendingRunnerFinishCb;
@@ -917,7 +919,7 @@
*/
@SuppressLint("NewApi")
void merge(TransitionInfo info, SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT, IBinder mergeTarget,
+ SurfaceControl.Transaction finishT,
Transitions.TransitionFinishCallback finishCallback) {
if (mFinishCB == null) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
@@ -927,16 +929,25 @@
return;
}
- if (Flags.enableRecentsBookendTransition()
- && info.getType() == TRANSIT_END_RECENTS_TRANSITION
- && mergeTarget == mTransition) {
- // This is a pending finish, so merge the end transition to trigger completing the
- // cleanup of the recents transition
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- "[%d] RecentsController.merge: TRANSIT_END_RECENTS_TRANSITION",
- mInstanceId);
- finishCallback.onTransitionFinished(null /* wct */);
- return;
+ if (Flags.enableRecentsBookendTransition()) {
+ if (info.getType() == TRANSIT_END_RECENTS_TRANSITION) {
+ // This is a pending finish, so merge the end transition to trigger completing
+ // the cleanup of the recents transition
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.merge: TRANSIT_END_RECENTS_TRANSITION",
+ mInstanceId);
+ consumeMerge(info, startT, finishT, finishCallback);
+ return;
+ } else if (mPendingFinishTransition != null) {
+ // This transition is interrupting a pending finish that was already sent, so
+ // pre-empt the pending finish transition since the state has already changed
+ // in the core
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.merge: Awaiting TRANSIT_END_RECENTS_TRANSITION",
+ mInstanceId);
+ onFinishInner(null /* wct */);
+ return;
+ }
}
if (info.getType() == TRANSIT_SLEEP) {
@@ -1210,16 +1221,12 @@
}
return;
}
+
// At this point, we are accepting the merge.
- startT.apply();
- // Since we're accepting the merge, update the finish transaction so that changes via
- // that transaction will be applied on top of those of the merged transitions
- mFinishTransaction = finishT;
+ consumeMerge(info, startT, finishT, finishCallback);
+
+ // Notify Launcher of the new opening tasks if necessary
boolean passTransitionInfo = ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX.isTrue();
- if (!passTransitionInfo) {
- // not using the incoming anim-only surfaces
- info.releaseAnimSurfaces();
- }
if (appearedTargets != null) {
try {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
@@ -1229,6 +1236,27 @@
Slog.e(TAG, "Error sending appeared tasks to recents animation", e);
}
}
+ }
+
+ /**
+ * Consumes the merge of the other given transition.
+ */
+ private void consumeMerge(TransitionInfo info, SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT,
+ Transitions.TransitionFinishCallback finishCallback) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.merge: consuming merge",
+ mInstanceId);
+
+ startT.apply();
+ // Since we're accepting the merge, update the finish transaction so that changes via
+ // that transaction will be applied on top of those of the merged transitions
+ mFinishTransaction = finishT;
+ boolean passTransitionInfo = ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX.isTrue();
+ if (!passTransitionInfo) {
+ // not using the incoming anim-only surfaces
+ info.releaseAnimSurfaces();
+ }
finishCallback.onTransitionFinished(null /* wct */);
}
@@ -1346,9 +1374,16 @@
final SurfaceControl.Transaction t = mFinishTransaction;
final WindowContainerTransaction wct = new WindowContainerTransaction();
+ // The following code must set this if it is changing anything in core that might affect
+ // transitions as a part of finishing the recents transition
+ boolean requiresBookendTransition = false;
+
if (mKeyguardLocked && mRecentsTask != null) {
if (toHome) wct.reorder(mRecentsTask, true /* toTop */);
else wct.restoreTransientOrder(mRecentsTask);
+ // We are manipulating the window hierarchy, which should only be done with the
+ // bookend transition
+ requiresBookendTransition = true;
}
if (returningToApp) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, " returning to app");
@@ -1365,6 +1400,9 @@
if (!mKeyguardLocked && mRecentsTask != null) {
wct.restoreTransientOrder(mRecentsTask);
}
+ // We are manipulating the window hierarchy, which should only be done with the
+ // bookend transition
+ requiresBookendTransition = true;
} else if (toHome && mOpeningSeparateHome && mPausingTasks != null) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, " 3p launching home");
// Special situation where 3p launcher was changed during recents (this happens
@@ -1384,6 +1422,9 @@
if (!mKeyguardLocked && mRecentsTask != null) {
wct.restoreTransientOrder(mRecentsTask);
}
+ // We are manipulating the window hierarchy, which should only be done with the
+ // bookend transition
+ requiresBookendTransition = true;
} else {
if (mPausingSeparateHome) {
if (mOpeningTasks.isEmpty()) {
@@ -1484,13 +1525,21 @@
if (Flags.enableRecentsBookendTransition()) {
if (!wct.isEmpty()) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- "[%d] RecentsController.finishInner: "
- + "Queuing TRANSIT_END_RECENTS_TRANSITION", mInstanceId);
- mPendingRunnerFinishCb = runnerFinishCb;
- mPendingFinishTransition = mTransitions.startTransition(
- TRANSIT_END_RECENTS_TRANSITION, wct,
- new PendingFinishTransitionHandler());
+ if (requiresBookendTransition) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.finishInner: "
+ + "Queuing TRANSIT_END_RECENTS_TRANSITION", mInstanceId);
+ mPendingRunnerFinishCb = runnerFinishCb;
+ mPendingFinishTransition = mTransitions.startTransition(
+ TRANSIT_END_RECENTS_TRANSITION, wct,
+ new PendingFinishTransitionHandler());
+ } else {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.finishInner: Non-transition affecting wct",
+ mInstanceId);
+ mPendingRunnerFinishCb = runnerFinishCb;
+ onFinishInner(wct);
+ }
} else {
// If there's no work to do, just go ahead and clean up
mPendingRunnerFinishCb = runnerFinishCb;
@@ -1631,6 +1680,9 @@
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] PendingFinishTransitionHandler.startAnimation: "
+ + "Started pending finish transition", mInstanceId);
return false;
}
@@ -1644,10 +1696,15 @@
@Override
public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
@Nullable SurfaceControl.Transaction finishTransaction) {
+ if (mPendingFinishTransition == null) {
+ // The cleanup was pre-empted by an earlier transition, nothing there is nothing
+ // to do here
+ return;
+ }
// Once we have merged (or not if the WCT didn't result in any changes), then we can
// run the pending finish logic
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- "[%d] RecentsController.onTransitionConsumed: "
+ "[%d] PendingFinishTransitionHandler.onTransitionConsumed: "
+ "Consumed pending finish transition", mInstanceId);
onFinishInner(null /* wct */);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 743bd05..347dcff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -40,7 +40,6 @@
import android.window.WindowContainerTransaction;
import com.android.internal.protolog.ProtoLog;
-import com.android.window.flags.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
import com.android.wm.shell.common.ComponentUtils;
@@ -439,9 +438,6 @@
for (int i = 0; i < info.getRootCount(); ++i) {
out.addRoot(info.getRoot(i));
}
- if (!Flags.moveAnimationOptionsToChange()) {
- out.setAnimationOptions(info.getAnimationOptions());
- }
return out;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 01428e6..e9c6ade 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -539,7 +539,7 @@
cornerRadius = 0;
}
- backgroundColorForTransition = getTransitionBackgroundColorIfSet(info, change, a,
+ backgroundColorForTransition = getTransitionBackgroundColorIfSet(change, a,
backgroundColorForTransition);
if (!isTask && a.getExtensionEdges() != 0x0) {
@@ -606,12 +606,7 @@
mTransactionPool, mMainExecutor, animRelOffset, cornerRadius,
clipRect);
- final TransitionInfo.AnimationOptions options;
- if (Flags.moveAnimationOptionsToChange()) {
- options = change.getAnimationOptions();
- } else {
- options = info.getAnimationOptions();
- }
+ final TransitionInfo.AnimationOptions options = change.getAnimationOptions();
if (options != null) {
attachThumbnail(animations, onAnimFinish, change, options, cornerRadius);
}
@@ -834,12 +829,7 @@
final boolean isOpeningType = TransitionUtil.isOpeningType(type);
final boolean enter = TransitionUtil.isOpeningType(changeMode);
final boolean isTask = change.getTaskInfo() != null;
- final TransitionInfo.AnimationOptions options;
- if (Flags.moveAnimationOptionsToChange()) {
- options = change.getAnimationOptions();
- } else {
- options = info.getAnimationOptions();
- }
+ final TransitionInfo.AnimationOptions options = change.getAnimationOptions();
final int overrideType = options != null ? options.getType() : ANIM_NONE;
final int userId = options != null ? options.getUserId() : UserHandle.USER_CURRENT;
final Rect endBounds = TransitionUtil.isClosingType(changeMode)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
index 1917996..938885c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
@@ -21,6 +21,7 @@
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_CONVERT_TO_BUBBLE;
import static com.android.wm.shell.transition.Transitions.TransitionObserver;
import android.annotation.NonNull;
@@ -35,6 +36,7 @@
import com.android.wm.shell.common.SingleInstanceRemoteListener;
import com.android.wm.shell.shared.IHomeTransitionListener;
import com.android.wm.shell.shared.TransitionUtil;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
/**
* The {@link TransitionObserver} that observes for transitions involving the home
@@ -48,6 +50,8 @@
private @NonNull final Context mContext;
private @NonNull final ShellExecutor mMainExecutor;
+ private Boolean mPendingHomeVisibilityUpdate;
+
public HomeTransitionObserver(@NonNull Context context,
@NonNull ShellExecutor mainExecutor) {
mContext = context;
@@ -59,28 +63,78 @@
@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction) {
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+ handleTransitionReadyWithBubbleAnything(info);
+ } else {
+ handleTransitionReady(info);
+ }
+ }
+
+ private void handleTransitionReady(@NonNull TransitionInfo info) {
for (TransitionInfo.Change change : info.getChanges()) {
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
- if (info.getType() == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
- || taskInfo == null
+ if (taskInfo == null
+ || info.getType() == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
+ || taskInfo.displayId != DEFAULT_DISPLAY
+ || taskInfo.taskId == -1
+ || !taskInfo.isRunning) {
+ continue;
+ }
+ Boolean homeVisibilityUpdate = getHomeVisibilityUpdate(info, change, taskInfo);
+ if (homeVisibilityUpdate != null) {
+ notifyHomeVisibilityChanged(homeVisibilityUpdate);
+ }
+ }
+ }
+
+ private void handleTransitionReadyWithBubbleAnything(@NonNull TransitionInfo info) {
+ Boolean homeVisibilityUpdate = null;
+ for (TransitionInfo.Change change : info.getChanges()) {
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ if (taskInfo == null
|| taskInfo.displayId != DEFAULT_DISPLAY
|| taskInfo.taskId == -1
|| !taskInfo.isRunning) {
continue;
}
- final int mode = change.getMode();
- final boolean isBackGesture = change.hasFlags(FLAG_BACK_GESTURE_ANIMATED);
- if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME) {
- final boolean gestureToHomeTransition = isBackGesture
- && TransitionUtil.isClosingType(info.getType());
- if (gestureToHomeTransition || TransitionUtil.isClosingMode(mode)
- || (!isBackGesture && TransitionUtil.isOpeningMode(mode))) {
- notifyHomeVisibilityChanged(gestureToHomeTransition
- || TransitionUtil.isOpeningType(mode));
- }
+ Boolean update = getHomeVisibilityUpdate(info, change, taskInfo);
+ if (update != null) {
+ homeVisibilityUpdate = update;
}
}
+
+ if (info.getType() == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP) {
+ // Do not apply at the start of desktop drag as that updates launcher UI visibility.
+ // Store the value and apply with a next transition if needed.
+ mPendingHomeVisibilityUpdate = homeVisibilityUpdate;
+ return;
+ }
+
+ if (info.getType() == TRANSIT_CONVERT_TO_BUBBLE && homeVisibilityUpdate == null) {
+ // We are converting to bubble and we did not get a change to home visibility in this
+ // transition. Apply the value from start of drag.
+ homeVisibilityUpdate = mPendingHomeVisibilityUpdate;
+ }
+ if (homeVisibilityUpdate != null) {
+ mPendingHomeVisibilityUpdate = null;
+ notifyHomeVisibilityChanged(homeVisibilityUpdate);
+ }
+ }
+
+ private Boolean getHomeVisibilityUpdate(TransitionInfo info,
+ TransitionInfo.Change change, ActivityManager.RunningTaskInfo taskInfo) {
+ final int mode = change.getMode();
+ final boolean isBackGesture = change.hasFlags(FLAG_BACK_GESTURE_ANIMATED);
+ if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME) {
+ final boolean gestureToHomeTransition = isBackGesture
+ && TransitionUtil.isClosingType(info.getType());
+ if (gestureToHomeTransition || TransitionUtil.isClosingMode(mode)
+ || (!isBackGesture && TransitionUtil.isOpeningMode(mode))) {
+ return gestureToHomeTransition || TransitionUtil.isOpeningType(mode);
+ }
+ }
+ return null;
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
index 4feb475..7984bce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
@@ -58,7 +58,6 @@
import com.android.internal.R;
import com.android.internal.policy.TransitionAnimation;
import com.android.internal.protolog.ProtoLog;
-import com.android.window.flags.Flags;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.TransitionUtil;
@@ -78,12 +77,7 @@
final boolean isFreeform = isTask && change.getTaskInfo().isFreeform();
final boolean isCoveredByOpaqueFullscreenChange =
isCoveredByOpaqueFullscreenChange(info, change);
- final TransitionInfo.AnimationOptions options;
- if (Flags.moveAnimationOptionsToChange()) {
- options = change.getAnimationOptions();
- } else {
- options = info.getAnimationOptions();
- }
+ final TransitionInfo.AnimationOptions options = change.getAnimationOptions();
final int overrideType = options != null ? options.getType() : ANIM_NONE;
int animAttr = 0;
boolean translucent = false;
@@ -279,16 +273,10 @@
* the given transition animation.
*/
@ColorInt
- public static int getTransitionBackgroundColorIfSet(@NonNull TransitionInfo info,
- @NonNull TransitionInfo.Change change, @NonNull Animation a,
- @ColorInt int defaultColor) {
+ public static int getTransitionBackgroundColorIfSet(@NonNull TransitionInfo.Change change,
+ @NonNull Animation a, @ColorInt int defaultColor) {
if (!a.getShowBackdrop()) {
return defaultColor;
- }
- if (!Flags.moveAnimationOptionsToChange() && info.getAnimationOptions() != null
- && info.getAnimationOptions().getBackgroundColor() != 0) {
- // If available use the background color provided through AnimationOptions
- return info.getAnimationOptions().getBackgroundColor();
} else if (a.getBackdropColor() != 0) {
// Otherwise fallback on the background color provided through the animation
// definition.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 5a6ea21..cf139a0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -103,6 +103,7 @@
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.MultiDisplayDragMoveIndicatorController;
import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -258,6 +259,7 @@
private final RecentsTransitionHandler mRecentsTransitionHandler;
private final DesktopModeCompatPolicy mDesktopModeCompatPolicy;
private final DesktopTilingDecorViewModel mDesktopTilingDecorViewModel;
+ private final MultiDisplayDragMoveIndicatorController mMultiDisplayDragMoveIndicatorController;
public DesktopModeWindowDecorViewModel(
Context context,
@@ -296,7 +298,8 @@
WindowDecorTaskResourceLoader taskResourceLoader,
RecentsTransitionHandler recentsTransitionHandler,
DesktopModeCompatPolicy desktopModeCompatPolicy,
- DesktopTilingDecorViewModel desktopTilingDecorViewModel) {
+ DesktopTilingDecorViewModel desktopTilingDecorViewModel,
+ MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController) {
this(
context,
shellExecutor,
@@ -340,7 +343,8 @@
taskResourceLoader,
recentsTransitionHandler,
desktopModeCompatPolicy,
- desktopTilingDecorViewModel);
+ desktopTilingDecorViewModel,
+ multiDisplayDragMoveIndicatorController);
}
@VisibleForTesting
@@ -387,7 +391,8 @@
WindowDecorTaskResourceLoader taskResourceLoader,
RecentsTransitionHandler recentsTransitionHandler,
DesktopModeCompatPolicy desktopModeCompatPolicy,
- DesktopTilingDecorViewModel desktopTilingDecorViewModel) {
+ DesktopTilingDecorViewModel desktopTilingDecorViewModel,
+ MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController) {
mContext = context;
mMainExecutor = shellExecutor;
mMainHandler = mainHandler;
@@ -460,6 +465,7 @@
mDesktopModeCompatPolicy = desktopModeCompatPolicy;
mDesktopTilingDecorViewModel = desktopTilingDecorViewModel;
mDesktopTasksController.setSnapEventHandler(this);
+ mMultiDisplayDragMoveIndicatorController = multiDisplayDragMoveIndicatorController;
shellInit.addInitCallback(this::onInit, this);
}
@@ -1759,7 +1765,8 @@
mTransitions,
mInteractionJankMonitor,
mTransactionFactory,
- mMainHandler);
+ mMainHandler,
+ mMultiDisplayDragMoveIndicatorController);
windowDecoration.setTaskDragResizer(taskPositioner);
final DesktopModeTouchEventListener touchEventListener =
@@ -2056,7 +2063,8 @@
Transitions transitions,
InteractionJankMonitor interactionJankMonitor,
Supplier<SurfaceControl.Transaction> transactionFactory,
- Handler handler) {
+ Handler handler,
+ MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController) {
final TaskPositioner taskPositioner = DesktopModeStatus.isVeiledResizeEnabled()
// TODO(b/383632995): Update when the flag is launched.
? (Flags.enableConnectedDisplaysWindowDrag()
@@ -2067,7 +2075,8 @@
dragEventListener,
transitions,
interactionJankMonitor,
- handler)
+ handler,
+ multiDisplayDragMoveIndicatorController)
: new VeiledResizeTaskPositioner(
taskOrganizer,
windowDecoration,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index dca376f..6165dbf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -1069,7 +1069,8 @@
private static int getCornerRadius(@NonNull Context context, int layoutResId) {
if (layoutResId == R.layout.desktop_mode_app_header) {
return loadDimensionPixelSize(context.getResources(),
- R.dimen.desktop_windowing_freeform_rounded_corner_radius);
+ com.android.wm.shell.shared.R.dimen
+ .desktop_windowing_freeform_rounded_corner_radius);
}
return INVALID_CORNER_RADIUS;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
index bb20292..c6cb62d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
@@ -17,6 +17,7 @@
import android.graphics.PointF
import android.graphics.Rect
+import android.hardware.display.DisplayTopology
import android.os.Handler
import android.os.IBinder
import android.os.Looper
@@ -32,10 +33,10 @@
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.MultiDisplayDragMoveBoundsCalculator
+import com.android.wm.shell.common.MultiDisplayDragMoveIndicatorController
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.transition.Transitions
import java.util.concurrent.TimeUnit
-import java.util.function.Supplier
/**
* A task positioner that also takes into account resizing a
@@ -49,11 +50,12 @@
private val desktopWindowDecoration: DesktopModeWindowDecoration,
private val displayController: DisplayController,
dragEventListener: DragPositioningCallbackUtility.DragEventListener,
- private val transactionSupplier: Supplier<SurfaceControl.Transaction>,
+ private val transactionSupplier: () -> SurfaceControl.Transaction,
private val transitions: Transitions,
private val interactionJankMonitor: InteractionJankMonitor,
@ShellMainThread private val handler: Handler,
-) : TaskPositioner, Transitions.TransitionHandler {
+ private val multiDisplayDragMoveIndicatorController: MultiDisplayDragMoveIndicatorController,
+) : TaskPositioner, Transitions.TransitionHandler, DisplayController.OnDisplaysChangedListener {
private val dragEventListeners =
mutableListOf<DragPositioningCallbackUtility.DragEventListener>()
private val stableBounds = Rect()
@@ -71,6 +73,7 @@
private var isResizingOrAnimatingResize = false
@Surface.Rotation private var rotation = 0
private var startDisplayId = 0
+ private val displayIds = mutableSetOf<Int>()
constructor(
taskOrganizer: ShellTaskOrganizer,
@@ -80,19 +83,22 @@
transitions: Transitions,
interactionJankMonitor: InteractionJankMonitor,
@ShellMainThread handler: Handler,
+ multiDisplayDragMoveIndicatorController: MultiDisplayDragMoveIndicatorController,
) : this(
taskOrganizer,
windowDecoration,
displayController,
dragEventListener,
- Supplier<SurfaceControl.Transaction> { SurfaceControl.Transaction() },
+ { SurfaceControl.Transaction() },
transitions,
interactionJankMonitor,
handler,
+ multiDisplayDragMoveIndicatorController,
)
init {
dragEventListeners.add(dragEventListener)
+ displayController.addDisplayWindowListener(this)
}
override fun onDragPositioningStart(ctrlType: Int, displayId: Int, x: Float, y: Float): Rect {
@@ -164,7 +170,7 @@
createLongTimeoutJankConfigBuilder(Cuj.CUJ_DESKTOP_MODE_DRAG_WINDOW)
)
- val t = transactionSupplier.get()
+ val t = transactionSupplier()
val startDisplayLayout = displayController.getDisplayLayout(startDisplayId)
val currentDisplayLayout = displayController.getDisplayLayout(displayId)
@@ -196,7 +202,13 @@
)
)
- // TODO(b/383069173): Render drag indicator(s)
+ multiDisplayDragMoveIndicatorController.onDragMove(
+ boundsDp,
+ startDisplayId,
+ desktopWindowDecoration.mTaskInfo,
+ displayIds,
+ transactionSupplier,
+ )
t.setPosition(
desktopWindowDecoration.leash,
@@ -267,7 +279,10 @@
)
)
- // TODO(b/383069173): Clear drag indicator(s)
+ multiDisplayDragMoveIndicatorController.onDragEnd(
+ desktopWindowDecoration.mTaskInfo.taskId,
+ transactionSupplier,
+ )
}
interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_DRAG_WINDOW)
@@ -348,6 +363,14 @@
dragEventListeners.remove(dragEventListener)
}
+ override fun onTopologyChanged(topology: DisplayTopology) {
+ // TODO: b/383069173 - Cancel window drag when topology changes happen during drag.
+
+ displayIds.clear()
+ val displayBounds = topology.getAbsoluteBounds()
+ displayIds.addAll(List(displayBounds.size()) { displayBounds.keyAt(it) })
+ }
+
companion object {
// Timeout used for resize and drag CUJs, this is longer than the default timeout to avoid
// timing out in the middle of a resize or drag action.
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
index 94dc774..d4d8d93 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
@@ -39,7 +39,6 @@
import android.annotation.NonNull;
import android.graphics.Point;
import android.graphics.Rect;
-import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
@@ -77,7 +76,6 @@
doNothing().when(mController).onAnimationFinished(any());
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testStartAnimation() {
final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
@@ -103,7 +101,6 @@
verify(mController).onAnimationFinished(mTransition);
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testChangesBehindStartingWindow() {
final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
@@ -118,7 +115,6 @@
assertEquals(0, animator.getDuration());
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testTransitionTypeDragResize() {
final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_TASK_FRAGMENT_DRAG_RESIZE, 0)
@@ -133,25 +129,6 @@
assertEquals(0, animator.getDuration());
}
- @DisableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
- @Test
- public void testInvalidCustomAnimation_disableAnimationOptionsPerChange() {
- final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
- .addChange(createChange(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY, TRANSIT_OPEN))
- .build();
- info.setAnimationOptions(TransitionInfo.AnimationOptions
- .makeCustomAnimOptions("packageName", 0 /* enterResId */, 0 /* exitResId */,
- 0 /* backgroundColor */, false /* overrideTaskTransition */));
- final Animator animator = mAnimRunner.createAnimator(
- info, mStartTransaction, mFinishTransaction,
- () -> mFinishCallback.onTransitionFinished(null /* wct */),
- new ArrayList<>());
-
- // An invalid custom animation is equivalent to jump-cut.
- assertEquals(0, animator.getDuration());
- }
-
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testInvalidCustomAnimation_enableAnimationOptionsPerChange() {
final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
@@ -169,36 +146,6 @@
assertEquals(0, animator.getDuration());
}
- @DisableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG)
- @Test
- public void testCalculateParentBounds_flagDisabled() {
- final Rect parentBounds = new Rect(0, 0, 2000, 2000);
- final Rect primaryBounds = new Rect();
- final Rect secondaryBounds = new Rect();
- parentBounds.splitVertically(primaryBounds, secondaryBounds);
-
- final TransitionInfo.Change change = createChange(0 /* flags */);
- change.setStartAbsBounds(secondaryBounds);
-
- final TransitionInfo.Change boundsAnimationChange = createChange(0 /* flags */);
- boundsAnimationChange.setStartAbsBounds(primaryBounds);
- boundsAnimationChange.setEndAbsBounds(primaryBounds);
- final Rect actualParentBounds = new Rect();
-
- calculateParentBounds(change, boundsAnimationChange, actualParentBounds);
-
- assertEquals(parentBounds, actualParentBounds);
-
- actualParentBounds.setEmpty();
-
- boundsAnimationChange.setStartAbsBounds(secondaryBounds);
- boundsAnimationChange.setEndAbsBounds(primaryBounds);
-
- calculateParentBounds(boundsAnimationChange, boundsAnimationChange, actualParentBounds);
-
- assertEquals(parentBounds, actualParentBounds);
- }
-
// TODO(b/243518738): Rewrite with TestParameter
@EnableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG)
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
index 9f29ef7..53a13d0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
@@ -32,8 +32,6 @@
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Rect;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
@@ -41,7 +39,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.window.flags.Flags;
import com.android.wm.shell.transition.TransitionInfoBuilder;
import org.junit.Before;
@@ -69,13 +66,11 @@
any());
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testInstantiate() {
verify(mShellInit).addInitCallback(any(), any());
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testOnInit() {
mController.onInit();
@@ -83,7 +78,6 @@
verify(mTransitions).addHandler(mController);
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testSetAnimScaleSetting() {
mController.setAnimScaleSetting(1.0f);
@@ -92,7 +86,6 @@
verify(mAnimSpec).setAnimScaleSetting(1.0f);
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testStartAnimation_containsNonActivityEmbeddingChange() {
final TransitionInfo.Change nonEmbeddedOpen = createChange(0 /* flags */);
@@ -129,7 +122,6 @@
assertFalse(info2.getChanges().contains(nonEmbeddedClose));
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testStartAnimation_containsOnlyFillTaskActivityEmbeddingChange() {
final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
@@ -146,7 +138,6 @@
verifyNoMoreInteractions(mFinishCallback);
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testStartAnimation_containsActivityEmbeddingSplitChange() {
// Change that occupies only part of the Task.
@@ -164,7 +155,6 @@
verifyNoMoreInteractions(mFinishTransaction);
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testStartAnimation_containsChangeEnterActivityEmbeddingSplit() {
// Change that is entering ActivityEmbedding split.
@@ -181,7 +171,6 @@
verifyNoMoreInteractions(mFinishTransaction);
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testStartAnimation_containsChangeExitActivityEmbeddingSplit() {
// Change that is exiting ActivityEmbedding split.
@@ -198,27 +187,6 @@
verifyNoMoreInteractions(mFinishTransaction);
}
- @DisableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
- @Test
- public void testShouldAnimate_containsAnimationOptions_disableAnimOptionsPerChange() {
- final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_CLOSE, 0)
- .addChange(createEmbeddedChange(EMBEDDED_RIGHT_BOUNDS, TASK_BOUNDS, TASK_BOUNDS))
- .build();
-
- info.setAnimationOptions(TransitionInfo.AnimationOptions
- .makeCustomAnimOptions("packageName", 0 /* enterResId */, 0 /* exitResId */,
- 0 /* backgroundColor */, false /* overrideTaskTransition */));
- assertTrue(mController.shouldAnimate(info));
-
- info.setAnimationOptions(TransitionInfo.AnimationOptions
- .makeSceneTransitionAnimOptions());
- assertFalse(mController.shouldAnimate(info));
-
- info.setAnimationOptions(TransitionInfo.AnimationOptions.makeCrossProfileAnimOptions());
- assertFalse(mController.shouldAnimate(info));
- }
-
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testShouldAnimate_containsAnimationOptions_enableAnimOptionsPerChange() {
final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_CLOSE, 0)
@@ -239,7 +207,6 @@
assertFalse(mController.shouldAnimate(info));
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@UiThreadTest
@Test
public void testMergeAnimation() {
@@ -278,7 +245,6 @@
verify(mFinishCallback).onTransitionFinished(any());
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testOnAnimationFinished() {
// Should not call finish when there is no transition.
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorControllerTest.kt
new file mode 100644
index 0000000..abd2388
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorControllerTest.kt
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.common
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.res.Configuration
+import android.graphics.Rect
+import android.graphics.RectF
+import android.testing.TestableResources
+import android.view.Display
+import android.view.SurfaceControl
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestShellExecutor
+import java.util.function.Supplier
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.whenever
+
+/**
+ * Tests for [MultiDisplayDragMoveIndicatorController].
+ *
+ * Build/Install/Run: atest WMShellUnitTests:MultiDisplayDragMoveIndicatorControllerTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class MultiDisplayDragMoveIndicatorControllerTest : ShellTestCase() {
+ private val displayController = mock<DisplayController>()
+ private val rootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>()
+ private val indicatorSurfaceFactory = mock<MultiDisplayDragMoveIndicatorSurface.Factory>()
+ private val indicatorSurface0 = mock<MultiDisplayDragMoveIndicatorSurface>()
+ private val indicatorSurface1 = mock<MultiDisplayDragMoveIndicatorSurface>()
+ private val transaction = mock<SurfaceControl.Transaction>()
+ private val transactionSupplier = mock<Supplier<SurfaceControl.Transaction>>()
+ private val taskInfo = mock<RunningTaskInfo>()
+ private val display0 = mock<Display>()
+ private val display1 = mock<Display>()
+
+ private lateinit var resources: TestableResources
+ private val executor = TestShellExecutor()
+
+ private lateinit var controller: MultiDisplayDragMoveIndicatorController
+
+ @Before
+ fun setUp() {
+ resources = mContext.getOrCreateTestableResources()
+ val resourceConfiguration = Configuration()
+ resourceConfiguration.uiMode = 0
+ resources.overrideConfiguration(resourceConfiguration)
+
+ controller =
+ MultiDisplayDragMoveIndicatorController(
+ displayController,
+ rootTaskDisplayAreaOrganizer,
+ indicatorSurfaceFactory,
+ executor,
+ )
+
+ val spyDisplayLayout0 =
+ MultiDisplayTestUtil.createSpyDisplayLayout(
+ MultiDisplayTestUtil.DISPLAY_GLOBAL_BOUNDS_0,
+ MultiDisplayTestUtil.DISPLAY_DPI_0,
+ resources.resources,
+ )
+ val spyDisplayLayout1 =
+ MultiDisplayTestUtil.createSpyDisplayLayout(
+ MultiDisplayTestUtil.DISPLAY_GLOBAL_BOUNDS_1,
+ MultiDisplayTestUtil.DISPLAY_DPI_1,
+ resources.resources,
+ )
+
+ taskInfo.taskId = TASK_ID
+ whenever(displayController.getDisplayLayout(0)).thenReturn(spyDisplayLayout0)
+ whenever(displayController.getDisplayLayout(1)).thenReturn(spyDisplayLayout1)
+ whenever(displayController.getDisplay(0)).thenReturn(display0)
+ whenever(displayController.getDisplay(1)).thenReturn(display1)
+ whenever(indicatorSurfaceFactory.create(taskInfo, display0)).thenReturn(indicatorSurface0)
+ whenever(indicatorSurfaceFactory.create(taskInfo, display1)).thenReturn(indicatorSurface1)
+ whenever(transactionSupplier.get()).thenReturn(transaction)
+ }
+
+ @Test
+ fun onDrag_boundsNotIntersectWithDisplay_noIndicator() {
+ controller.onDragMove(
+ RectF(2000f, 2000f, 2100f, 2200f), // not intersect with any display
+ startDisplayId = 0,
+ taskInfo,
+ displayIds = setOf(0, 1),
+ ) { transaction }
+ executor.flushAll()
+
+ verify(indicatorSurfaceFactory, never()).create(any(), any())
+ }
+
+ @Test
+ fun onDrag_boundsIntersectWithStartDisplay_noIndicator() {
+ controller.onDragMove(
+ RectF(100f, 100f, 200f, 200f), // intersect with display 0
+ startDisplayId = 0,
+ taskInfo,
+ displayIds = setOf(0, 1),
+ ) { transaction }
+ executor.flushAll()
+
+ verify(indicatorSurfaceFactory, never()).create(any(), any())
+ }
+
+ @Test
+ fun onDrag_boundsIntersectWithNonStartDisplay_showAndDisposeIndicator() {
+ controller.onDragMove(
+ RectF(100f, -100f, 200f, 200f), // intersect with display 0 and 1
+ startDisplayId = 0,
+ taskInfo,
+ displayIds = setOf(0, 1),
+ ) { transaction }
+ executor.flushAll()
+
+ verify(indicatorSurfaceFactory, times(1)).create(taskInfo, display1)
+ verify(indicatorSurface1, times(1))
+ .show(transaction, taskInfo, rootTaskDisplayAreaOrganizer, 1, Rect(0, 1800, 200, 2400))
+
+ controller.onDragMove(
+ RectF(2000f, 2000f, 2100f, 2200f), // not intersect with display 1
+ startDisplayId = 0,
+ taskInfo,
+ displayIds = setOf(0, 1)
+ ) { transaction }
+ while (executor.callbacks.isNotEmpty()) {
+ executor.flushAll()
+ }
+
+ verify(indicatorSurface1, times(1))
+ .relayout(any(), eq(transaction), shouldBeVisible = eq(false))
+
+ controller.onDragEnd(TASK_ID, { transaction })
+ while (executor.callbacks.isNotEmpty()) {
+ executor.flushAll()
+ }
+
+ verify(indicatorSurface1, times(1)).disposeSurface(transaction)
+ }
+
+ companion object {
+ private const val TASK_ID = 10
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
index 0d5741f..8ad54f5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
@@ -16,51 +16,28 @@
package com.android.wm.shell.desktopmode
-import android.app.ActivityManager.RunningTaskInfo
-import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
-import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
-import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
-import android.content.ContentResolver
-import android.os.Binder
import android.platform.test.annotations.EnableFlags
-import android.provider.Settings
-import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
import android.testing.AndroidTestingRunner
import android.view.Display.DEFAULT_DISPLAY
-import android.view.IWindowManager
-import android.view.WindowManager.TRANSIT_CHANGE
-import android.window.DisplayAreaInfo
-import android.window.WindowContainerTransaction
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.dx.mockito.inline.extended.ExtendedMockito.never
import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.window.flags.Flags
-import com.android.wm.shell.MockToken
-import com.android.wm.shell.RootTaskDisplayAreaOrganizer
-import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
-import com.android.wm.shell.transition.Transitions
-import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.isNull
import org.mockito.Mock
-import org.mockito.Mockito.anyInt
import org.mockito.Mockito.spy
-import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
-import org.mockito.kotlin.eq
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
@@ -73,27 +50,18 @@
@RunWith(AndroidTestingRunner::class)
class DesktopDisplayEventHandlerTest : ShellTestCase() {
@Mock lateinit var testExecutor: ShellExecutor
- @Mock lateinit var transitions: Transitions
@Mock lateinit var displayController: DisplayController
- @Mock lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
- @Mock private lateinit var mockWindowManager: IWindowManager
@Mock private lateinit var mockDesktopUserRepositories: DesktopUserRepositories
@Mock private lateinit var mockDesktopRepository: DesktopRepository
@Mock private lateinit var mockDesktopTasksController: DesktopTasksController
- @Mock private lateinit var shellTaskOrganizer: ShellTaskOrganizer
+ @Mock private lateinit var desktopDisplayModeController: DesktopDisplayModeController
private lateinit var mockitoSession: StaticMockitoSession
private lateinit var shellInit: ShellInit
private lateinit var handler: DesktopDisplayEventHandler
private val onDisplaysChangedListenerCaptor = argumentCaptor<OnDisplaysChangedListener>()
- private val runningTasks = mutableListOf<RunningTaskInfo>()
private val externalDisplayId = 100
- private val freeformTask =
- TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build()
- private val fullscreenTask =
- TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FULLSCREEN).build()
- private val defaultTDA = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
@Before
fun setUp() {
@@ -105,24 +73,15 @@
shellInit = spy(ShellInit(testExecutor))
whenever(mockDesktopUserRepositories.current).thenReturn(mockDesktopRepository)
- whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
- whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
- .thenReturn(defaultTDA)
handler =
DesktopDisplayEventHandler(
context,
shellInit,
- transitions,
displayController,
- rootTaskDisplayAreaOrganizer,
- mockWindowManager,
mockDesktopUserRepositories,
mockDesktopTasksController,
- shellTaskOrganizer,
+ desktopDisplayModeController,
)
- whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
- runningTasks.add(freeformTask)
- runningTasks.add(fullscreenTask)
shellInit.init()
verify(displayController)
.addDisplayWindowListener(onDisplaysChangedListenerCaptor.capture())
@@ -133,65 +92,6 @@
mockitoSession.finishMocking()
}
- private fun testDisplayWindowingModeSwitch(
- defaultWindowingMode: Int,
- extendedDisplayEnabled: Boolean,
- expectTransition: Boolean,
- ) {
- defaultTDA.configuration.windowConfiguration.windowingMode = defaultWindowingMode
- whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer { defaultWindowingMode }
- val settingsSession =
- ExtendedDisplaySettingsSession(
- context.contentResolver,
- if (extendedDisplayEnabled) 1 else 0,
- )
-
- settingsSession.use {
- connectExternalDisplay()
- defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
- disconnectExternalDisplay()
-
- if (expectTransition) {
- val arg = argumentCaptor<WindowContainerTransaction>()
- verify(transitions, times(2))
- .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
- assertThat(arg.firstValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FREEFORM)
- assertThat(arg.secondValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
- .isEqualTo(defaultWindowingMode)
- } else {
- verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull())
- }
- }
- }
-
- @Test
- fun displayWindowingModeSwitchOnDisplayConnected_extendedDisplayDisabled() {
- testDisplayWindowingModeSwitch(
- defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
- extendedDisplayEnabled = false,
- expectTransition = false,
- )
- }
-
- @Test
- fun displayWindowingModeSwitchOnDisplayConnected_fullscreenDisplay() {
- testDisplayWindowingModeSwitch(
- defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
- extendedDisplayEnabled = true,
- expectTransition = true,
- )
- }
-
- @Test
- fun displayWindowingModeSwitchOnDisplayConnected_freeformDisplay() {
- testDisplayWindowingModeSwitch(
- defaultWindowingMode = WINDOWING_MODE_FREEFORM,
- extendedDisplayEnabled = true,
- expectTransition = false,
- )
- }
-
@Test
fun testDisplayAdded_supportsDesks_createsDesk() {
whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
@@ -231,70 +131,14 @@
}
@Test
- fun displayWindowingModeSwitch_existingTasksOnConnected() {
- defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
- whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer {
- WINDOWING_MODE_FULLSCREEN
- }
-
- ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
- connectExternalDisplay()
-
- val arg = argumentCaptor<WindowContainerTransaction>()
- verify(transitions, times(1))
- .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
- assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_UNDEFINED)
- assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FULLSCREEN)
- }
+ fun testConnectExternalDisplay() {
+ onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(externalDisplayId)
+ verify(desktopDisplayModeController).refreshDisplayWindowingMode()
}
@Test
- fun displayWindowingModeSwitch_existingTasksOnDisconnected() {
- defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
- whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer {
- WINDOWING_MODE_FULLSCREEN
- }
-
- ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
- disconnectExternalDisplay()
-
- val arg = argumentCaptor<WindowContainerTransaction>()
- verify(transitions, times(1))
- .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
- assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FREEFORM)
- assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_UNDEFINED)
- }
- }
-
- private fun connectExternalDisplay() {
- whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
- .thenReturn(intArrayOf(DEFAULT_DISPLAY, externalDisplayId))
- onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(externalDisplayId)
- }
-
- private fun disconnectExternalDisplay() {
- whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
- .thenReturn(intArrayOf(DEFAULT_DISPLAY))
+ fun testDisconnectExternalDisplay() {
onDisplaysChangedListenerCaptor.lastValue.onDisplayRemoved(externalDisplayId)
- }
-
- private class ExtendedDisplaySettingsSession(
- private val contentResolver: ContentResolver,
- private val overrideValue: Int,
- ) : AutoCloseable {
- private val settingName = DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
- private val initialValue = Settings.Global.getInt(contentResolver, settingName, 0)
-
- init {
- Settings.Global.putInt(contentResolver, settingName, overrideValue)
- }
-
- override fun close() {
- Settings.Global.putInt(contentResolver, settingName, initialValue)
- }
+ verify(desktopDisplayModeController).refreshDisplayWindowingMode()
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
new file mode 100644
index 0000000..0ff7230
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
+import android.content.ContentResolver
+import android.os.Binder
+import android.provider.Settings
+import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
+import android.testing.AndroidTestingRunner
+import android.view.Display.DEFAULT_DISPLAY
+import android.view.IWindowManager
+import android.view.WindowManager.TRANSIT_CHANGE
+import android.window.DisplayAreaInfo
+import android.window.WindowContainerTransaction
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.never
+import com.android.wm.shell.MockToken
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
+import com.android.wm.shell.transition.Transitions
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.isNull
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+/**
+ * Test class for [DesktopDisplayModeController]
+ *
+ * Usage: atest WMShellUnitTests:DesktopDisplayModeControllerTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopDisplayModeControllerTest : ShellTestCase() {
+ private val transitions = mock<Transitions>()
+ private val rootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>()
+ private val mockWindowManager = mock<IWindowManager>()
+ private val shellTaskOrganizer = mock<ShellTaskOrganizer>()
+ private val desktopWallpaperActivityTokenProvider =
+ mock<DesktopWallpaperActivityTokenProvider>()
+
+ private lateinit var controller: DesktopDisplayModeController
+
+ private val runningTasks = mutableListOf<RunningTaskInfo>()
+ private val freeformTask =
+ TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build()
+ private val fullscreenTask =
+ TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FULLSCREEN).build()
+ private val defaultTDA = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
+ private val wallpaperToken = MockToken().token()
+
+ @Before
+ fun setUp() {
+ whenever(transitions.startTransition(anyInt(), any(), isNull())).thenReturn(Binder())
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
+ .thenReturn(defaultTDA)
+ controller =
+ DesktopDisplayModeController(
+ context,
+ transitions,
+ rootTaskDisplayAreaOrganizer,
+ mockWindowManager,
+ shellTaskOrganizer,
+ desktopWallpaperActivityTokenProvider,
+ )
+ runningTasks.add(freeformTask)
+ runningTasks.add(fullscreenTask)
+ whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(ArrayList(runningTasks))
+ whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(wallpaperToken)
+ }
+
+ private fun testDisplayWindowingModeSwitch(
+ defaultWindowingMode: Int,
+ extendedDisplayEnabled: Boolean,
+ expectTransition: Boolean,
+ ) {
+ defaultTDA.configuration.windowConfiguration.windowingMode = defaultWindowingMode
+ whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(defaultWindowingMode)
+ val settingsSession =
+ ExtendedDisplaySettingsSession(
+ context.contentResolver,
+ if (extendedDisplayEnabled) 1 else 0,
+ )
+
+ settingsSession.use {
+ connectExternalDisplay()
+ defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+ disconnectExternalDisplay()
+
+ if (expectTransition) {
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(transitions, times(2))
+ .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+ assertThat(arg.firstValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ assertThat(arg.firstValue.changes[wallpaperToken.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ assertThat(arg.secondValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
+ .isEqualTo(defaultWindowingMode)
+ assertThat(arg.secondValue.changes[wallpaperToken.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ } else {
+ verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull())
+ }
+ }
+ }
+
+ @Test
+ fun displayWindowingModeSwitchOnDisplayConnected_extendedDisplayDisabled() {
+ testDisplayWindowingModeSwitch(
+ defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ extendedDisplayEnabled = false,
+ expectTransition = false,
+ )
+ }
+
+ @Test
+ fun displayWindowingModeSwitchOnDisplayConnected_fullscreenDisplay() {
+ testDisplayWindowingModeSwitch(
+ defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ extendedDisplayEnabled = true,
+ expectTransition = true,
+ )
+ }
+
+ @Test
+ fun displayWindowingModeSwitchOnDisplayConnected_freeformDisplay() {
+ testDisplayWindowingModeSwitch(
+ defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+ extendedDisplayEnabled = true,
+ expectTransition = false,
+ )
+ }
+
+ @Test
+ fun displayWindowingModeSwitch_existingTasksOnConnected() {
+ defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+ whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN)
+
+ ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
+ connectExternalDisplay()
+
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(transitions, times(1))
+ .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+ assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED)
+ assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ }
+ }
+
+ @Test
+ fun displayWindowingModeSwitch_existingTasksOnDisconnected() {
+ defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+ whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer {
+ WINDOWING_MODE_FULLSCREEN
+ }
+
+ ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
+ disconnectExternalDisplay()
+
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(transitions, times(1))
+ .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+ assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED)
+ }
+ }
+
+ private fun connectExternalDisplay() {
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
+ .thenReturn(intArrayOf(DEFAULT_DISPLAY, EXTERNAL_DISPLAY_ID))
+ controller.refreshDisplayWindowingMode()
+ }
+
+ private fun disconnectExternalDisplay() {
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
+ .thenReturn(intArrayOf(DEFAULT_DISPLAY))
+ controller.refreshDisplayWindowingMode()
+ }
+
+ private class ExtendedDisplaySettingsSession(
+ private val contentResolver: ContentResolver,
+ private val overrideValue: Int,
+ ) : AutoCloseable {
+ private val settingName = DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
+ private val initialValue = Settings.Global.getInt(contentResolver, settingName, 0)
+
+ init {
+ Settings.Global.putInt(contentResolver, settingName, overrideValue)
+ }
+
+ override fun close() {
+ Settings.Global.putInt(contentResolver, settingName, initialValue)
+ }
+ }
+
+ private companion object {
+ const val EXTERNAL_DISPLAY_ID = 100
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandlerTest.kt
index 4c3325d..0d1c5722 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandlerTest.kt
@@ -21,6 +21,7 @@
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.app.WindowConfiguration.WindowingMode
+import android.os.Handler
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.SurfaceControl
@@ -56,7 +57,12 @@
@Before
fun setUp() {
handler =
- DesktopMinimizationTransitionHandler(testExecutor, testExecutor, displayController)
+ DesktopMinimizationTransitionHandler(
+ testExecutor,
+ testExecutor,
+ displayController,
+ mock<Handler>(),
+ )
whenever(displayController.getDisplayContext(any())).thenReturn(mContext)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
index ed9b97d..9bff287 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
@@ -333,7 +333,7 @@
@Test
fun isOnlyVisibleNonClosingTask_singleVisibleClosingTask() {
repo.updateTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
- repo.addClosingTask(DEFAULT_DISPLAY, 1)
+ repo.addClosingTask(displayId = DEFAULT_DISPLAY, deskId = 0, taskId = 1)
// A visible task that's closing
assertThat(repo.isVisibleTask(1)).isTrue()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index fcd92ac..9a73b02 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -2477,8 +2477,7 @@
controller.moveTaskToFront(task.taskId, unminimizeReason = UnminimizeReason.UNKNOWN)
val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
- assertThat(wct.hierarchyOps).hasSize(1)
- wct.assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
+ wct.assertLaunchTask(task.taskId, WINDOWING_MODE_FREEFORM)
}
@Test
@@ -2810,6 +2809,7 @@
// Should launch home
wct.assertPendingIntentAt(0, launchHomeIntent(DEFAULT_DISPLAY))
+ wct.assertPendingIntentActivityOptionsLaunchDisplayIdAt(0, DEFAULT_DISPLAY)
}
@Test
@@ -2827,7 +2827,7 @@
fun onDesktopWindowClose_singleActiveTask_isClosing() {
val task = setUpFreeformTask()
- taskRepository.addClosingTask(DEFAULT_DISPLAY, task.taskId)
+ taskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, deskId = 0, taskId = task.taskId)
val wct = WindowContainerTransaction()
controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)
@@ -2864,7 +2864,11 @@
val task1 = setUpFreeformTask()
val task2 = setUpFreeformTask()
- taskRepository.addClosingTask(DEFAULT_DISPLAY, task2.taskId)
+ taskRepository.addClosingTask(
+ displayId = DEFAULT_DISPLAY,
+ deskId = 0,
+ taskId = task2.taskId,
+ )
val wct = WindowContainerTransaction()
controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task1)
@@ -2966,9 +2970,13 @@
val captor = argumentCaptor<WindowContainerTransaction>()
verify(freeformTaskTransitionStarter)
.startMinimizedModeTransition(captor.capture(), eq(task.taskId), eq(false))
- captor.firstValue.hierarchyOps.none { hop ->
- hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK && hop.container == wallpaperToken.asBinder()
- }
+ assertThat(
+ captor.firstValue.hierarchyOps.none { hop ->
+ hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK &&
+ hop.container == wallpaperToken.asBinder()
+ }
+ )
+ .isTrue()
}
@Test
@@ -3057,9 +3065,13 @@
val captor = argumentCaptor<WindowContainerTransaction>()
verify(freeformTaskTransitionStarter).startPipTransition(captor.capture())
- captor.firstValue.hierarchyOps.none { hop ->
- hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK && hop.container == wallpaperToken.asBinder()
- }
+ assertThat(
+ captor.firstValue.hierarchyOps.none { hop ->
+ hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK &&
+ hop.container == wallpaperToken.asBinder()
+ }
+ )
+ .isTrue()
}
@Test
@@ -3080,7 +3092,12 @@
val captor = argumentCaptor<WindowContainerTransaction>()
verify(freeformTaskTransitionStarter)
.startMinimizedModeTransition(captor.capture(), eq(task.taskId), eq(true))
- captor.firstValue.hierarchyOps.none { hop -> hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK }
+ assertThat(
+ captor.firstValue.hierarchyOps.none { hop ->
+ hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK
+ }
+ )
+ .isTrue()
}
@Test
@@ -3127,9 +3144,13 @@
val captor = argumentCaptor<WindowContainerTransaction>()
verify(freeformTaskTransitionStarter)
.startMinimizedModeTransition(captor.capture(), eq(task.taskId), eq(false))
- captor.firstValue.hierarchyOps.none { hop ->
- hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK && hop.container == wallpaperToken.asBinder()
- }
+ assertThat(
+ captor.firstValue.hierarchyOps.none { hop ->
+ hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK &&
+ hop.container == wallpaperToken.asBinder()
+ }
+ )
+ .isTrue()
}
@Test
@@ -3151,9 +3172,13 @@
val captor = argumentCaptor<WindowContainerTransaction>()
verify(freeformTaskTransitionStarter)
.startMinimizedModeTransition(captor.capture(), eq(task1.taskId), eq(false))
- captor.firstValue.hierarchyOps.none { hop ->
- hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK && hop.container == wallpaperToken.asBinder()
- }
+ assertThat(
+ captor.firstValue.hierarchyOps.none { hop ->
+ hop.type == HIERARCHY_OP_TYPE_REMOVE_TASK &&
+ hop.container == wallpaperToken.asBinder()
+ }
+ )
+ .isTrue()
}
@Test
@@ -3225,6 +3250,30 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onDesktopWindowMinimize_minimizesTask() {
+ val task = setUpFreeformTask()
+ val transition = Binder()
+ val runOnTransit = RunOnStartTransitionCallback()
+ whenever(
+ freeformTaskTransitionStarter.startMinimizedModeTransition(
+ any(),
+ anyInt(),
+ anyBoolean(),
+ )
+ )
+ .thenReturn(transition)
+ whenever(mMockDesktopImmersiveController.exitImmersiveIfApplicable(any(), eq(task), any()))
+ .thenReturn(
+ ExitResult.Exit(exitingTask = task.taskId, runOnTransitionStart = runOnTransit)
+ )
+
+ controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
+
+ verify(desksOrganizer).minimizeTask(any(), /* deskId= */ eq(0), eq(task))
+ }
+
+ @Test
fun onDesktopWindowMinimize_triesToStopTiling() {
val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
val transition = Binder()
@@ -3883,6 +3932,7 @@
// Should launch home
assertNotNull(result, "Should handle request")
.assertPendingIntentAt(0, launchHomeIntent(DEFAULT_DISPLAY))
+ result!!.assertPendingIntentActivityOptionsLaunchDisplayIdAt(0, DEFAULT_DISPLAY)
}
@Test
@@ -3909,6 +3959,7 @@
// Should launch home
assertNotNull(result, "Should handle request")
.assertPendingIntentAt(0, launchHomeIntent(DEFAULT_DISPLAY))
+ result!!.assertPendingIntentActivityOptionsLaunchDisplayIdAt(0, DEFAULT_DISPLAY)
}
@Test
@@ -3972,7 +4023,11 @@
val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
- taskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+ taskRepository.addClosingTask(
+ displayId = DEFAULT_DISPLAY,
+ deskId = 0,
+ taskId = task2.taskId,
+ )
val result =
controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
@@ -4037,6 +4092,7 @@
// Should launch home
assertNotNull(result, "Should handle request")
.assertPendingIntentAt(0, launchHomeIntent(DEFAULT_DISPLAY))
+ result!!.assertPendingIntentActivityOptionsLaunchDisplayIdAt(0, DEFAULT_DISPLAY)
}
@Test
@@ -4053,6 +4109,7 @@
// Should launch home
assertNotNull(result, "Should handle request")
.assertPendingIntentAt(0, launchHomeIntent(SECOND_DISPLAY))
+ result!!.assertPendingIntentActivityOptionsLaunchDisplayIdAt(0, SECOND_DISPLAY)
}
@Test
@@ -4083,6 +4140,36 @@
}
@Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun handleRequest_closeTransition_onlyDesktopTask_deactivatesDesk() {
+ val task = setUpFreeformTask()
+
+ controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
+
+ verify(desksOrganizer).deactivateDesk(any(), /* deskId= */ eq(0))
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun handleRequest_closeTransition_onlyDesktopTask_addsDeactivatesDeskTransition() {
+ val transition = Binder()
+ val task = setUpFreeformTask()
+
+ controller.handleRequest(transition, createTransition(task, type = TRANSIT_CLOSE))
+
+ verify(desksTransitionsObserver)
+ .addPendingTransition(DeskTransition.DeactivateDesk(token = transition, deskId = 0))
+ }
+
+ @Test
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun handleRequest_closeTransition_multipleTasks_noWallpaper_doesNotHandle() {
val task1 = setUpFreeformTask()
@@ -4115,7 +4202,11 @@
val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
- taskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+ taskRepository.addClosingTask(
+ displayId = DEFAULT_DISPLAY,
+ deskId = 0,
+ taskId = task2.taskId,
+ )
val result =
controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
@@ -6240,6 +6331,61 @@
assertThat(taskRepository.getNumberOfDesks(DEFAULT_DISPLAY)).isEqualTo(currentDeskCount + 1)
}
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+ )
+ fun startLaunchTransition_desktopNotShowing_movesWallpaperToFront() {
+ val launchingTask = createFreeformTask()
+ val wct = WindowContainerTransaction()
+ wct.reorder(launchingTask.token, /* onTop= */ true)
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ eq(TRANSIT_OPEN),
+ any(),
+ anyOrNull(),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(Binder())
+
+ controller.startLaunchTransition(TRANSIT_OPEN, wct, launchingTaskId = null)
+
+ val latestWct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
+ val launchingTaskReorderIndex = latestWct.indexOfReorder(launchingTask, toTop = true)
+ val wallpaperReorderIndex = latestWct.indexOfReorder(wallpaperToken, toTop = true)
+ assertThat(launchingTaskReorderIndex).isNotEqualTo(-1)
+ assertThat(wallpaperReorderIndex).isNotEqualTo(-1)
+ assertThat(launchingTaskReorderIndex).isGreaterThan(wallpaperReorderIndex)
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+ )
+ fun startLaunchTransition_desktopShowing_doesNotReorderWallpaper() {
+ val wct = WindowContainerTransaction()
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ eq(TRANSIT_OPEN),
+ any(),
+ anyOrNull(),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(Binder())
+
+ setUpFreeformTask()
+ controller.startLaunchTransition(TRANSIT_OPEN, wct, launchingTaskId = null)
+
+ val latestWct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
+ assertNull(latestWct.hierarchyOps.find { op -> op.container == wallpaperToken.asBinder() })
+ }
+
private class RunOnStartTransitionCallback : ((IBinder) -> Unit) {
var invocations = 0
private set
@@ -6626,11 +6772,18 @@
}
private fun WindowContainerTransaction.indexOfReorder(
+ token: WindowContainerToken,
+ toTop: Boolean? = null,
+): Int {
+ val hop = hierarchyOps.singleOrNull(ReorderPredicate(token, toTop)) ?: return -1
+ return hierarchyOps.indexOf(hop)
+}
+
+private fun WindowContainerTransaction.indexOfReorder(
task: RunningTaskInfo,
toTop: Boolean? = null,
): Int {
- val hop = hierarchyOps.singleOrNull(ReorderPredicate(task.token, toTop)) ?: return -1
- return hierarchyOps.indexOf(hop)
+ return indexOfReorder(task.token, toTop)
}
private class ReorderPredicate(val token: WindowContainerToken, val toTop: Boolean? = null) :
@@ -6750,6 +6903,29 @@
assertThat(op.pendingIntent?.intent?.categories).isEqualTo(intent.categories)
}
+private fun WindowContainerTransaction.assertPendingIntentActivityOptionsLaunchDisplayIdAt(
+ index: Int,
+ displayId: Int,
+) {
+ assertIndexInBounds(index)
+ val op = hierarchyOps[index]
+ if (op.launchOptions != null) {
+ val options = ActivityOptions(op.launchOptions)
+ assertThat(options.launchDisplayId).isEqualTo(displayId)
+ }
+}
+
+private fun WindowContainerTransaction.assertLaunchTask(taskId: Int, windowingMode: Int) {
+ val keyLaunchWindowingMode = "android.activity.windowingMode"
+
+ assertHop { hop ->
+ hop.type == HIERARCHY_OP_TYPE_LAUNCH_TASK &&
+ hop.launchOptions?.getInt(LAUNCH_KEY_TASK_ID) == taskId &&
+ hop.launchOptions?.getInt(keyLaunchWindowingMode, WINDOWING_MODE_UNDEFINED) ==
+ windowingMode
+ }
+}
+
private fun WindowContainerTransaction.assertLaunchTaskAt(
index: Int,
taskId: Int,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
index d33209d..62e3c54 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
@@ -37,7 +37,6 @@
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
import com.android.dx.mockito.inline.extended.StaticMockitoSession
-import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
import com.android.internal.jank.InteractionJankMonitor
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION
import com.android.wm.shell.ShellTaskOrganizer
@@ -72,8 +71,6 @@
import org.mockito.Mockito.mock
import org.mockito.Mockito.spy
import org.mockito.Mockito.`when`
-import org.mockito.kotlin.eq
-import org.mockito.kotlin.verify
import org.mockito.quality.Strictness
/**
@@ -521,85 +518,6 @@
}
@Test
- fun minimizeTransitionReadyAndFinished_logsJankInstrumentationBeginAndEnd() {
- desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
- desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
- (1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
- val transition = Binder()
- val task = setUpFreeformTask()
- addPendingMinimizeChange(transition, taskId = task.taskId)
-
- callOnTransitionReady(
- transition,
- TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
- )
-
- desktopTasksLimiter.getTransitionObserver().onTransitionStarting(transition)
-
- verify(interactionJankMonitor)
- .begin(any(), eq(mContext), eq(handler), eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
-
- desktopTasksLimiter
- .getTransitionObserver()
- .onTransitionFinished(transition, /* aborted= */ false)
-
- verify(interactionJankMonitor).end(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
- }
-
- @Test
- fun minimizeTransitionReadyAndAborted_logsJankInstrumentationBeginAndCancel() {
- desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
- desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
- (1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
- val transition = Binder()
- val task = setUpFreeformTask()
- addPendingMinimizeChange(transition, taskId = task.taskId)
-
- callOnTransitionReady(
- transition,
- TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
- )
-
- desktopTasksLimiter.getTransitionObserver().onTransitionStarting(transition)
-
- verify(interactionJankMonitor)
- .begin(any(), eq(mContext), eq(handler), eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
-
- desktopTasksLimiter
- .getTransitionObserver()
- .onTransitionFinished(transition, /* aborted= */ true)
-
- verify(interactionJankMonitor).cancel(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
- }
-
- @Test
- fun minimizeTransitionReadyAndMerged_logsJankInstrumentationBeginAndEnd() {
- desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
- desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
- (1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
- val mergedTransition = Binder()
- val newTransition = Binder()
- val task = setUpFreeformTask()
- addPendingMinimizeChange(mergedTransition, taskId = task.taskId)
-
- callOnTransitionReady(
- mergedTransition,
- TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
- )
-
- desktopTasksLimiter.getTransitionObserver().onTransitionStarting(mergedTransition)
-
- verify(interactionJankMonitor)
- .begin(any(), eq(mContext), eq(handler), eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
-
- desktopTasksLimiter
- .getTransitionObserver()
- .onTransitionMerged(mergedTransition, newTransition)
-
- verify(interactionJankMonitor).end(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
- }
-
- @Test
fun getMinimizingTask_noPendingTransition_returnsNull() {
val transition = Binder()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt
index 8b10ca1..96b85ad 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt
@@ -22,6 +22,7 @@
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.window.TransitionInfo
import android.window.WindowContainerTransaction
+import android.window.WindowContainerTransaction.Change
import android.window.WindowContainerTransaction.HierarchyOp
import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT
import androidx.test.filters.SmallTest
@@ -29,15 +30,19 @@
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
+import com.android.wm.shell.desktopmode.multidesks.RootTaskDesksOrganizer.DeskMinimizationRoot
import com.android.wm.shell.desktopmode.multidesks.RootTaskDesksOrganizer.DeskRoot
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellInit
import com.google.common.truth.Truth.assertThat
+import kotlin.test.assertNotNull
import org.junit.Assert.assertEquals
import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.argThat
import org.mockito.kotlin.mock
/**
@@ -75,6 +80,43 @@
}
@Test
+ fun testCreateDesk_createsMinimizationRoot() {
+ val callback = FakeOnCreateCallback()
+ organizer.createDesk(Display.DEFAULT_DISPLAY, callback)
+ val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
+ organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+
+ val minimizationRootTask = createFreeformTask().apply { parentTaskId = -1 }
+ organizer.onTaskAppeared(minimizationRootTask, SurfaceControl())
+
+ val minimizationRoot = organizer.deskMinimizationRootsByDeskId[freeformRoot.taskId]
+ assertNotNull(minimizationRoot)
+ assertThat(minimizationRoot.deskId).isEqualTo(freeformRoot.taskId)
+ assertThat(minimizationRoot.rootId).isEqualTo(minimizationRootTask.taskId)
+ }
+
+ @Test
+ fun testCreateMinimizationRoot_marksHidden() {
+ organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback())
+ val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
+ organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+
+ val minimizationRootTask = createFreeformTask().apply { parentTaskId = -1 }
+ organizer.onTaskAppeared(minimizationRootTask, SurfaceControl())
+
+ verify(mockShellTaskOrganizer)
+ .applyTransaction(
+ argThat { wct ->
+ wct.changes.any { change ->
+ change.key == minimizationRootTask.token.asBinder() &&
+ (change.value.changeMask and Change.CHANGE_HIDDEN != 0) &&
+ change.value.hidden
+ }
+ }
+ )
+ }
+
+ @Test
fun testOnTaskAppeared_withoutRequest_throws() {
val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
@@ -105,57 +147,122 @@
}
@Test
+ fun testOnTaskAppeared_duplicateMinimizedRoot_throws() {
+ organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback())
+ val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
+ val minimizationRootTask = createFreeformTask().apply { parentTaskId = -1 }
+ organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+ organizer.onTaskAppeared(minimizationRootTask, SurfaceControl())
+
+ assertThrows(Exception::class.java) {
+ organizer.onTaskAppeared(minimizationRootTask, SurfaceControl())
+ }
+ }
+
+ @Test
fun testOnTaskVanished_removesRoot() {
val desk = createDesk()
- organizer.onTaskVanished(desk.taskInfo)
+ organizer.onTaskVanished(desk.deskRoot.taskInfo)
- assertThat(organizer.roots.contains(desk.deskId)).isFalse()
+ assertThat(organizer.deskRootsByDeskId.contains(desk.deskRoot.deskId)).isFalse()
+ }
+
+ @Test
+ fun testOnTaskVanished_removesMinimizedRoot() {
+ val desk = createDesk()
+
+ organizer.onTaskVanished(desk.deskRoot.taskInfo)
+ organizer.onTaskVanished(desk.minimizationRoot.taskInfo)
+
+ assertThat(organizer.deskMinimizationRootsByDeskId.contains(desk.deskRoot.deskId)).isFalse()
}
@Test
fun testDesktopWindowAppearsInDesk() {
val desk = createDesk()
- val child = createFreeformTask().apply { parentTaskId = desk.deskId }
+ val child = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
organizer.onTaskAppeared(child, SurfaceControl())
- assertThat(desk.children).contains(child.taskId)
+ assertThat(desk.deskRoot.children).contains(child.taskId)
+ }
+
+ @Test
+ fun testDesktopWindowAppearsInDeskMinimizationRoot() {
+ val desk = createDesk()
+ val child = createFreeformTask().apply { parentTaskId = desk.minimizationRoot.rootId }
+
+ organizer.onTaskAppeared(child, SurfaceControl())
+
+ assertThat(desk.minimizationRoot.children).contains(child.taskId)
+ }
+
+ @Test
+ fun testDesktopWindowMovesToMinimizationRoot() {
+ val desk = createDesk()
+ val child = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
+ organizer.onTaskAppeared(child, SurfaceControl())
+
+ child.parentTaskId = desk.minimizationRoot.rootId
+ organizer.onTaskInfoChanged(child)
+
+ assertThat(desk.deskRoot.children).doesNotContain(child.taskId)
+ assertThat(desk.minimizationRoot.children).contains(child.taskId)
}
@Test
fun testDesktopWindowDisappearsFromDesk() {
val desk = createDesk()
- val child = createFreeformTask().apply { parentTaskId = desk.deskId }
+ val child = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
organizer.onTaskAppeared(child, SurfaceControl())
organizer.onTaskVanished(child)
- assertThat(desk.children).doesNotContain(child.taskId)
+ assertThat(desk.deskRoot.children).doesNotContain(child.taskId)
}
@Test
- fun testRemoveDesk() {
+ fun testDesktopWindowDisappearsFromDeskMinimizationRoot() {
+ val desk = createDesk()
+ val child = createFreeformTask().apply { parentTaskId = desk.minimizationRoot.rootId }
+
+ organizer.onTaskAppeared(child, SurfaceControl())
+ organizer.onTaskVanished(child)
+
+ assertThat(desk.minimizationRoot.children).doesNotContain(child.taskId)
+ }
+
+ @Test
+ fun testRemoveDesk_removesDeskRoot() {
val desk = createDesk()
val wct = WindowContainerTransaction()
- organizer.removeDesk(wct, desk.deskId)
+ organizer.removeDesk(wct, desk.deskRoot.deskId)
assertThat(
wct.hierarchyOps.any { hop ->
hop.type == HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_ROOT_TASK &&
- hop.container == desk.taskInfo.token.asBinder()
+ hop.container == desk.deskRoot.token.asBinder()
}
)
.isTrue()
}
@Test
- fun testRemoveDesk_didNotExist_throws() {
- val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
+ fun testRemoveDesk_removesMinimizationRoot() {
+ val desk = createDesk()
val wct = WindowContainerTransaction()
- assertThrows(Exception::class.java) { organizer.removeDesk(wct, freeformRoot.taskId) }
+ organizer.removeDesk(wct, desk.deskRoot.deskId)
+
+ assertThat(
+ wct.hierarchyOps.any { hop ->
+ hop.type == HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_ROOT_TASK &&
+ hop.container == desk.minimizationRoot.token.asBinder()
+ }
+ )
+ .isTrue()
}
@Test
@@ -163,20 +270,20 @@
val desk = createDesk()
val wct = WindowContainerTransaction()
- organizer.activateDesk(wct, desk.deskId)
+ organizer.activateDesk(wct, desk.deskRoot.deskId)
assertThat(
wct.hierarchyOps.any { hop ->
hop.type == HierarchyOp.HIERARCHY_OP_TYPE_REORDER &&
hop.toTop &&
- hop.container == desk.taskInfo.token.asBinder()
+ hop.container == desk.deskRoot.taskInfo.token.asBinder()
}
)
.isTrue()
assertThat(
wct.hierarchyOps.any { hop ->
hop.type == HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT &&
- hop.container == desk.taskInfo.token.asBinder()
+ hop.container == desk.deskRoot.taskInfo.token.asBinder()
}
)
.isTrue()
@@ -196,14 +303,14 @@
val desktopTask = createFreeformTask().apply { parentTaskId = -1 }
val wct = WindowContainerTransaction()
- organizer.moveTaskToDesk(wct, desk.deskId, desktopTask)
+ organizer.moveTaskToDesk(wct, desk.deskRoot.deskId, desktopTask)
assertThat(
wct.hierarchyOps.any { hop ->
hop.isReparent &&
hop.toTop &&
hop.container == desktopTask.token.asBinder() &&
- hop.newParent == desk.taskInfo.token.asBinder()
+ hop.newParent == desk.deskRoot.taskInfo.token.asBinder()
}
)
.isTrue()
@@ -231,13 +338,26 @@
fun testGetDeskAtEnd() {
val desk = createDesk()
- val task = createFreeformTask().apply { parentTaskId = desk.deskId }
+ val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
val endDesk =
organizer.getDeskAtEnd(
TransitionInfo.Change(task.token, SurfaceControl()).apply { taskInfo = task }
)
- assertThat(endDesk).isEqualTo(desk.deskId)
+ assertThat(endDesk).isEqualTo(desk.deskRoot.deskId)
+ }
+
+ @Test
+ fun testGetDeskAtEnd_inMinimizationRoot() {
+ val desk = createDesk()
+
+ val task = createFreeformTask().apply { parentTaskId = desk.minimizationRoot.rootId }
+ val endDesk =
+ organizer.getDeskAtEnd(
+ TransitionInfo.Change(task.token, SurfaceControl()).apply { taskInfo = task }
+ )
+
+ assertThat(endDesk).isEqualTo(desk.deskRoot.deskId)
}
@Test
@@ -264,14 +384,14 @@
fun deactivateDesk_clearsLaunchRoot() {
val wct = WindowContainerTransaction()
val desk = createDesk()
- organizer.activateDesk(wct, desk.deskId)
+ organizer.activateDesk(wct, desk.deskRoot.deskId)
- organizer.deactivateDesk(wct, desk.deskId)
+ organizer.deactivateDesk(wct, desk.deskRoot.deskId)
assertThat(
wct.hierarchyOps.any { hop ->
hop.type == HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT &&
- hop.container == desk.taskInfo.token.asBinder() &&
+ hop.container == desk.deskRoot.taskInfo.token.asBinder() &&
hop.windowingModes == null &&
hop.activityTypes == null
}
@@ -280,25 +400,129 @@
}
@Test
- fun isDeskChange() {
+ fun isDeskChange_forDeskId() {
val desk = createDesk()
assertThat(
organizer.isDeskChange(
- TransitionInfo.Change(desk.taskInfo.token, desk.leash).apply {
- taskInfo = desk.taskInfo
+ TransitionInfo.Change(desk.deskRoot.taskInfo.token, desk.deskRoot.leash).apply {
+ taskInfo = desk.deskRoot.taskInfo
},
- desk.deskId,
+ desk.deskRoot.deskId,
)
)
.isTrue()
}
- private fun createDesk(): DeskRoot {
+ @Test
+ fun isDeskChange_forDeskId_inMinimizationRoot() {
+ val desk = createDesk()
+
+ assertThat(
+ organizer.isDeskChange(
+ change =
+ TransitionInfo.Change(
+ desk.minimizationRoot.token,
+ desk.minimizationRoot.leash,
+ )
+ .apply { taskInfo = desk.minimizationRoot.taskInfo },
+ deskId = desk.deskRoot.deskId,
+ )
+ )
+ .isTrue()
+ }
+
+ @Test
+ fun isDeskChange_anyDesk() {
+ val desk = createDesk()
+
+ assertThat(
+ organizer.isDeskChange(
+ change =
+ TransitionInfo.Change(desk.deskRoot.taskInfo.token, desk.deskRoot.leash)
+ .apply { taskInfo = desk.deskRoot.taskInfo }
+ )
+ )
+ .isTrue()
+ }
+
+ @Test
+ fun isDeskChange_anyDesk_inMinimizationRoot() {
+ val desk = createDesk()
+
+ assertThat(
+ organizer.isDeskChange(
+ change =
+ TransitionInfo.Change(
+ desk.minimizationRoot.taskInfo.token,
+ desk.minimizationRoot.leash,
+ )
+ .apply { taskInfo = desk.minimizationRoot.taskInfo }
+ )
+ )
+ .isTrue()
+ }
+
+ @Test
+ fun minimizeTask() {
+ val desk = createDesk()
+ val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
+ val wct = WindowContainerTransaction()
+ organizer.moveTaskToDesk(wct, desk.deskRoot.deskId, task)
+ organizer.onTaskAppeared(task, SurfaceControl())
+
+ organizer.minimizeTask(wct, deskId = desk.deskRoot.deskId, task)
+
+ assertThat(
+ wct.hierarchyOps.any { hop ->
+ hop.isReparent &&
+ hop.container == task.token.asBinder() &&
+ hop.newParent == desk.minimizationRoot.token.asBinder()
+ }
+ )
+ .isTrue()
+ }
+
+ @Test
+ fun minimizeTask_alreadyMinimized_noOp() {
+ val desk = createDesk()
+ val task = createFreeformTask().apply { parentTaskId = desk.minimizationRoot.rootId }
+ val wct = WindowContainerTransaction()
+ organizer.onTaskAppeared(task, SurfaceControl())
+
+ organizer.minimizeTask(wct, deskId = desk.deskRoot.deskId, task)
+
+ assertThat(wct.isEmpty).isTrue()
+ }
+
+ @Test
+ fun minimizeTask_inDifferentDesk_noOp() {
+ val desk = createDesk()
+ val otherDesk = createDesk()
+ val task = createFreeformTask().apply { parentTaskId = otherDesk.deskRoot.deskId }
+ val wct = WindowContainerTransaction()
+ organizer.onTaskAppeared(task, SurfaceControl())
+
+ organizer.minimizeTask(wct, deskId = desk.deskRoot.deskId, task)
+
+ assertThat(wct.isEmpty).isTrue()
+ }
+
+ private data class DeskRoots(
+ val deskRoot: DeskRoot,
+ val minimizationRoot: DeskMinimizationRoot,
+ )
+
+ private fun createDesk(): DeskRoots {
organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback())
val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
organizer.onTaskAppeared(freeformRoot, SurfaceControl())
- return organizer.roots[freeformRoot.taskId]
+ val minimizationRoot = createFreeformTask().apply { parentTaskId = -1 }
+ organizer.onTaskAppeared(minimizationRoot, SurfaceControl())
+ return DeskRoots(
+ organizer.deskRootsByDeskId[freeformRoot.taskId],
+ checkNotNull(organizer.deskMinimizationRootsByDeskId[freeformRoot.taskId]),
+ )
}
private class FakeOnCreateCallback : DesksOrganizer.OnCreateCallback {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
index fd5e567..93dd345 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
@@ -17,16 +17,20 @@
package com.android.wm.shell.recents;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_SLEEP;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX;
+import static com.android.wm.shell.Flags.FLAG_ENABLE_RECENTS_BOOKEND_TRANSITION;
import static com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_ANIMATING;
import static com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_NOT_RUNNING;
import static com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_REQUESTED;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_END_RECENTS_TRANSITION;
import static com.android.wm.shell.transition.Transitions.TRANSIT_START_RECENTS_TRANSITION;
import static com.google.common.truth.Truth.assertThat;
@@ -64,7 +68,6 @@
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.internal.os.IResultReceiver;
-import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
@@ -73,6 +76,7 @@
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
+import com.android.wm.shell.shared.R;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
@@ -308,8 +312,7 @@
mRecentsTransitionHandler.findController(transition).merge(
mergeTransitionInfo,
new StubTransaction(),
- finishT,
- transition,
+ new StubTransaction(),
mock(Transitions.TransitionFinishCallback.class));
mMainExecutor.flushAll();
@@ -318,6 +321,69 @@
}
@Test
+ @EnableFlags(FLAG_ENABLE_RECENTS_BOOKEND_TRANSITION)
+ public void testMerge_consumeBookendTransition() throws Exception {
+ // Start and finish the transition
+ final IRecentsAnimationRunner animationRunner = mock(IRecentsAnimationRunner.class);
+ final IBinder transition = startRecentsTransition(/* synthetic= */ false, animationRunner);
+ mRecentsTransitionHandler.startAnimation(
+ transition, createTransitionInfo(), new StubTransaction(), new StubTransaction(),
+ mock(Transitions.TransitionFinishCallback.class));
+ mRecentsTransitionHandler.findController(transition).finish(/* toHome= */ false,
+ false /* sendUserLeaveHint */, mock(IResultReceiver.class));
+ mMainExecutor.flushAll();
+
+ // Merge the bookend transition
+ TransitionInfo mergeTransitionInfo =
+ new TransitionInfoBuilder(TRANSIT_END_RECENTS_TRANSITION)
+ .addChange(TRANSIT_OPEN, new TestRunningTaskInfoBuilder().build())
+ .build();
+ SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+ Transitions.TransitionFinishCallback finishCallback
+ = mock(Transitions.TransitionFinishCallback.class);
+ mRecentsTransitionHandler.findController(transition).merge(
+ mergeTransitionInfo,
+ new StubTransaction(),
+ finishT,
+ finishCallback);
+ mMainExecutor.flushAll();
+
+ // Verify that we've merged
+ verify(finishCallback).onTransitionFinished(any());
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_RECENTS_BOOKEND_TRANSITION)
+ public void testMerge_pendingBookendTransition_mergesTransition() throws Exception {
+ // Start and finish the transition
+ final IRecentsAnimationRunner animationRunner = mock(IRecentsAnimationRunner.class);
+ final IBinder transition = startRecentsTransition(/* synthetic= */ false, animationRunner);
+ mRecentsTransitionHandler.startAnimation(
+ transition, createTransitionInfo(), new StubTransaction(), new StubTransaction(),
+ mock(Transitions.TransitionFinishCallback.class));
+ mRecentsTransitionHandler.findController(transition).finish(/* toHome= */ false,
+ false /* sendUserLeaveHint */, mock(IResultReceiver.class));
+ mMainExecutor.flushAll();
+
+ // Merge a new transition while we have a pending finish
+ TransitionInfo mergeTransitionInfo = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN, new TestRunningTaskInfoBuilder().build())
+ .build();
+ SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+ Transitions.TransitionFinishCallback finishCallback
+ = mock(Transitions.TransitionFinishCallback.class);
+ mRecentsTransitionHandler.findController(transition).merge(
+ mergeTransitionInfo,
+ new StubTransaction(),
+ finishT,
+ finishCallback);
+ mMainExecutor.flushAll();
+
+ // Verify that we've cleaned up the original transition
+ assertNull(mRecentsTransitionHandler.findController(transition));
+ }
+
+ @Test
@EnableFlags(FLAG_ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX)
public void testMergeAndFinish_openingFreeformTasks_setsCornerRadius() {
ActivityManager.RunningTaskInfo freeformTask =
@@ -336,7 +402,6 @@
mergeTransitionInfo,
new StubTransaction(),
finishT,
- transition,
mock(Transitions.TransitionFinishCallback.class));
mRecentsTransitionHandler.findController(transition).finish(/* toHome= */ false,
false /* sendUserLeaveHint */, mock(IResultReceiver.class));
@@ -385,15 +450,23 @@
}
private TransitionInfo createTransitionInfo() {
- final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder()
+ final ActivityManager.RunningTaskInfo homeTask = new TestRunningTaskInfoBuilder()
.setTopActivityType(ACTIVITY_TYPE_HOME)
.build();
+ final ActivityManager.RunningTaskInfo appTask = new TestRunningTaskInfoBuilder()
+ .setTopActivityType(ACTIVITY_TYPE_STANDARD)
+ .build();
final TransitionInfo.Change homeChange = new TransitionInfo.Change(
- task.token, new SurfaceControl());
+ homeTask.token, new SurfaceControl());
homeChange.setMode(TRANSIT_TO_FRONT);
- homeChange.setTaskInfo(task);
+ homeChange.setTaskInfo(homeTask);
+ final TransitionInfo.Change appChange = new TransitionInfo.Change(
+ appTask.token, new SurfaceControl());
+ appChange.setMode(TRANSIT_TO_FRONT);
+ appChange.setTaskInfo(appTask);
return new TransitionInfoBuilder(TRANSIT_START_RECENTS_TRANSITION)
.addChange(homeChange)
+ .addChange(appChange)
.build();
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/animation/MinimizeAnimatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/animation/MinimizeAnimatorTest.kt
new file mode 100644
index 0000000..ba609d4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/animation/MinimizeAnimatorTest.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.shared.animation
+
+import android.animation.Animator
+import android.animation.AnimatorSet
+import android.animation.ValueAnimator
+import android.content.Context
+import android.content.res.Resources
+import android.os.Handler
+import android.util.DisplayMetrics
+import android.view.SurfaceControl
+import android.view.SurfaceControl.Transaction
+import android.window.TransitionInfo
+import android.window.WindowContainerToken
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
+import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
+import com.android.internal.jank.InteractionJankMonitor
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyFloat
+import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class MinimizeAnimatorTest {
+ private val context = mock<Context>()
+ private val resources = mock<Resources>()
+ private val transaction = mock<Transaction>()
+ private val leash = mock<SurfaceControl>()
+ private val interactionJankMonitor = mock<InteractionJankMonitor>()
+ private val animationHandler = mock<Handler>()
+
+ private val displayMetrics = DisplayMetrics().apply { density = 1f }
+
+ @Before
+ fun setup() {
+ whenever(context.resources).thenReturn(resources)
+ whenever(resources.displayMetrics).thenReturn(displayMetrics)
+ whenever(transaction.setAlpha(any(), anyFloat())).thenReturn(transaction)
+ whenever(transaction.setPosition(any(), anyFloat(), anyFloat())).thenReturn(transaction)
+ whenever(transaction.setScale(any(), anyFloat(), anyFloat())).thenReturn(transaction)
+ whenever(transaction.setFrameTimeline(anyLong())).thenReturn(transaction)
+ }
+
+ @Test
+ fun create_returnsBoundsAndAlphaAnimators() {
+ val change = TransitionInfo.Change(mock<WindowContainerToken>(), leash)
+
+ val animator = createAnimator(change)
+
+ assertThat(animator).isInstanceOf(AnimatorSet::class.java)
+ val animatorSet = animator as AnimatorSet
+ assertThat(animatorSet.childAnimations).hasSize(2)
+ assertIsBoundsAnimator(animatorSet.childAnimations[0])
+ assertIsAlphaAnimator(animatorSet.childAnimations[1])
+ }
+
+ @Test
+ fun create_doesNotlogJankInstrumentation() = runOnUiThread {
+ val change = TransitionInfo.Change(mock<WindowContainerToken>(), leash)
+
+ createAnimator(change)
+
+ verify(interactionJankMonitor, never()).begin(
+ leash, context, animationHandler, CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
+ }
+
+ @Test
+ fun onAnimationStart_logsJankInstrumentation() = runOnUiThread {
+ val change = TransitionInfo.Change(mock<WindowContainerToken>(), leash)
+
+ createAnimator(change).start()
+
+ verify(interactionJankMonitor).begin(
+ leash, context, animationHandler, CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
+ }
+
+ private fun createAnimator(change: TransitionInfo.Change): Animator =
+ MinimizeAnimator.create(context, change, transaction, {}, interactionJankMonitor,
+ animationHandler)
+
+ private fun assertIsBoundsAnimator(animator: Animator) {
+ assertThat(animator).isInstanceOf(ValueAnimator::class.java)
+ assertThat(animator.duration).isEqualTo(200)
+ assertThat(animator.interpolator).isEqualTo(Interpolators.STANDARD_ACCELERATE)
+ }
+
+ private fun assertIsAlphaAnimator(animator: Animator) {
+ assertThat(animator).isInstanceOf(ValueAnimator::class.java)
+ assertThat(animator.duration).isEqualTo(100)
+ assertThat(animator.interpolator).isEqualTo(Interpolators.LINEAR)
+ }
+}
+
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
index f69bf34..88c6e49 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
@@ -16,13 +16,16 @@
package com.android.wm.shell.shared.desktopmode
+import android.Manifest.permission.SYSTEM_ALERT_WINDOW
import android.app.TaskInfo
import android.compat.testing.PlatformCompatChangeRule
import android.content.ComponentName
import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
+import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.os.Process
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
@@ -39,7 +42,9 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyString
import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
@@ -55,6 +60,7 @@
private lateinit var desktopModeCompatPolicy: DesktopModeCompatPolicy
private val packageManager: PackageManager = mock()
private val homeActivities = ComponentName(HOME_LAUNCHER_PACKAGE_NAME, /* class */ "")
+ private val baseActivityTest = ComponentName("com.test.dummypackage", "TestClass")
@Before
fun setUp() {
@@ -64,6 +70,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
fun testIsTopActivityExemptFromDesktopWindowing_onlyTransparentActivitiesInStack() {
assertTrue(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
createFreeformTask(/* displayId */ 0)
@@ -71,10 +78,39 @@
isActivityStackTransparent = true
isTopActivityNoDisplay = false
numActivities = 1
+ baseActivity = baseActivityTest
}))
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
+ fun testIsTopActivityExemptWithPermission_onlyTransparentActivitiesInStack() {
+ allowOverlayPermission(arrayOf(SYSTEM_ALERT_WINDOW))
+ assertTrue(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
+ createFreeformTask(/* displayId */ 0)
+ .apply {
+ isActivityStackTransparent = true
+ isTopActivityNoDisplay = false
+ numActivities = 1
+ baseActivity = baseActivityTest
+ }))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
+ fun testIsTopActivityExemptWithNoPermission_onlyTransparentActivitiesInStack() {
+ allowOverlayPermission(arrayOf())
+ assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
+ createFreeformTask(/* displayId */ 0)
+ .apply {
+ isActivityStackTransparent = true
+ isTopActivityNoDisplay = false
+ numActivities = 1
+ baseActivity = baseActivityTest
+ }))
+ }
+
+ @Test
fun testIsTopActivityExemptFromDesktopWindowing_noActivitiesInStack() {
assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
createFreeformTask(/* displayId */ 0)
@@ -219,4 +255,15 @@
}
}
}
+
+ fun allowOverlayPermission(permissions: Array<String>) {
+ val packageInfo = mock<PackageInfo>()
+ packageInfo.requestedPermissions = permissions
+ whenever(
+ packageManager.getPackageInfo(
+ anyString(),
+ eq(PackageManager.GET_PERMISSIONS)
+ )
+ ).thenReturn(packageInfo)
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
index 6f28e656..3099b0f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
@@ -17,36 +17,44 @@
package com.android.wm.shell.transition;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_CONVERT_TO_BUBBLE;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.WindowConfiguration.ActivityType;
import android.content.Context;
+import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionInfo.TransitionMode;
+import android.window.WindowContainerToken;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
@@ -188,6 +196,72 @@
}
@Test
+ @EnableFlags({Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE})
+ public void testDragTaskToBubbleOverHome_notifiesHomeIsVisible() throws RemoteException {
+ ActivityManager.RunningTaskInfo homeTask = createTaskInfo(1, ACTIVITY_TYPE_HOME);
+ ActivityManager.RunningTaskInfo bubbleTask = createTaskInfo(2, ACTIVITY_TYPE_STANDARD);
+
+ TransitionInfo startDragTransition =
+ new TransitionInfoBuilder(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP)
+ .addChange(TRANSIT_TO_FRONT, homeTask)
+ .addChange(TRANSIT_TO_BACK, bubbleTask)
+ .build();
+
+ // Start drag to desktop which brings home to front
+ mHomeTransitionObserver.onTransitionReady(new Binder(), startDragTransition,
+ MockTransactionPool.create(), MockTransactionPool.create());
+ // Does not notify home visibility yet
+ verify(mListener, never()).onHomeVisibilityChanged(anyBoolean());
+
+ TransitionInfo convertToBubbleTransition =
+ new TransitionInfoBuilder(TRANSIT_CONVERT_TO_BUBBLE)
+ .addChange(TRANSIT_TO_FRONT, bubbleTask)
+ .build();
+
+ // Convert to bubble. Transition does not include changes for home task
+ mHomeTransitionObserver.onTransitionReady(new Binder(), convertToBubbleTransition,
+ MockTransactionPool.create(), MockTransactionPool.create());
+
+ // Notifies home visibility change that was pending from the start of drag
+ verify(mListener).onHomeVisibilityChanged(true);
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE})
+ public void testDragTaskToBubbleOverOtherTask_notifiesHomeIsNotVisible()
+ throws RemoteException {
+ ActivityManager.RunningTaskInfo homeTask = createTaskInfo(1, ACTIVITY_TYPE_HOME);
+ ActivityManager.RunningTaskInfo bubbleTask = createTaskInfo(2, ACTIVITY_TYPE_STANDARD);
+ ActivityManager.RunningTaskInfo otherTask = createTaskInfo(3, ACTIVITY_TYPE_STANDARD);
+
+ TransitionInfo startDragTransition =
+ new TransitionInfoBuilder(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP)
+ .addChange(TRANSIT_TO_FRONT, homeTask)
+ .addChange(TRANSIT_TO_BACK, bubbleTask)
+ .build();
+
+ // Start drag to desktop which brings home to front
+ mHomeTransitionObserver.onTransitionReady(new Binder(), startDragTransition,
+ MockTransactionPool.create(), MockTransactionPool.create());
+ // Does not notify home visibility yet
+ verify(mListener, never()).onHomeVisibilityChanged(anyBoolean());
+
+ TransitionInfo convertToBubbleTransition =
+ new TransitionInfoBuilder(TRANSIT_CONVERT_TO_BUBBLE)
+ .addChange(TRANSIT_TO_FRONT, bubbleTask)
+ .addChange(TRANSIT_TO_FRONT, otherTask)
+ .addChange(TRANSIT_TO_BACK, homeTask)
+ .build();
+
+ // Convert to bubble. Transition includes home task to back which updates home visibility
+ mHomeTransitionObserver.onTransitionReady(new Binder(), convertToBubbleTransition,
+ MockTransactionPool.create(), MockTransactionPool.create());
+
+ // Notifies home visibility change due to home moving to back in the second transition
+ verify(mListener).onHomeVisibilityChanged(false);
+ }
+
+ @Test
public void testHomeActivityWithBackGestureNotifiesHomeIsVisibleAfterClose()
throws RemoteException {
TransitionInfo info = mock(TransitionInfo.class);
@@ -227,4 +301,14 @@
when(change.getMode()).thenReturn(mode);
taskInfo.isRunning = isRunning;
}
+
+ private static ActivityManager.RunningTaskInfo createTaskInfo(int taskId, int activityType) {
+ ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+ taskInfo.taskId = taskId;
+ taskInfo.topActivityType = activityType;
+ taskInfo.configuration.windowConfiguration.setActivityType(activityType);
+ taskInfo.token = mock(WindowContainerToken.class);
+ taskInfo.isRunning = true;
+ return taskInfo;
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
index 8cccdb2..81dfaed 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
@@ -52,6 +52,7 @@
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayInsetsController
import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.common.MultiDisplayDragMoveIndicatorController
import com.android.wm.shell.common.MultiInstanceHelper
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler
@@ -138,6 +139,8 @@
protected val mockFreeformTaskTransitionStarter = mock<FreeformTaskTransitionStarter>()
protected val mockActivityOrientationChangeHandler =
mock<DesktopActivityOrientationChangeHandler>()
+ protected val mockMultiDisplayDragMoveIndicatorController =
+ mock<MultiDisplayDragMoveIndicatorController>()
protected val mockInputManager = mock<InputManager>()
private val mockTaskPositionerFactory =
mock<DesktopModeWindowDecorViewModel.TaskPositionerFactory>()
@@ -229,6 +232,7 @@
mockRecentsTransitionHandler,
desktopModeCompatPolicy,
mockTilingWindowDecoration,
+ mockMultiDisplayDragMoveIndicatorController,
)
desktopModeWindowDecorViewModel.setSplitScreenController(mockSplitScreenController)
whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
@@ -243,6 +247,7 @@
any(),
any(),
any(),
+ any(),
any()
)
)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
index 937938d..a6b0770 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
@@ -41,6 +41,7 @@
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.common.MultiDisplayDragMoveIndicatorController
import com.android.wm.shell.common.MultiDisplayTestUtil
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TransitionFinishCallback
@@ -62,8 +63,8 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.Mockito.`when`
+import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
/**
@@ -93,7 +94,8 @@
@Mock private lateinit var mockTransitions: Transitions
@Mock private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
@Mock private lateinit var mockSurfaceControl: SurfaceControl
-
+ @Mock private lateinit var mockMultiDisplayDragMoveIndicatorController:
+ MultiDisplayDragMoveIndicatorController
private lateinit var resources: TestableResources
private lateinit var spyDisplayLayout0: DisplayLayout
private lateinit var spyDisplayLayout1: DisplayLayout
@@ -170,10 +172,11 @@
mockDesktopWindowDecoration,
mockDisplayController,
mockDragEventListener,
- mockTransactionFactory,
+ { mockTransaction },
mockTransitions,
mockInteractionJankMonitor,
mainHandler,
+ mockMultiDisplayDragMoveIndicatorController,
)
}
diff --git a/libs/hostgraphics/include/gui/BufferItemConsumer.h b/libs/hostgraphics/include/gui/BufferItemConsumer.h
index c259411..5c96c82 100644
--- a/libs/hostgraphics/include/gui/BufferItemConsumer.h
+++ b/libs/hostgraphics/include/gui/BufferItemConsumer.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_GUI_BUFFERITEMCONSUMER_H
#define ANDROID_GUI_BUFFERITEMCONSUMER_H
+#include <com_android_graphics_libgui_flags.h>
+#include <gui/BufferQueue.h>
#include <gui/ConsumerBase.h>
#include <gui/IGraphicBufferConsumer.h>
#include <utils/RefBase.h>
@@ -26,9 +28,22 @@
class BufferItemConsumer : public ConsumerBase {
public:
BufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
- int bufferCount, bool controlledByApp)
+ int bufferCount = -1, bool controlledByApp = false)
: mConsumer(consumer) {}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ BufferItemConsumer(uint64_t consumerUsage, int bufferCount = -1,
+ bool controlledByApp = false, bool isConsumerSurfaceFlinger = false) {
+ sp<IGraphicBufferProducer> producer;
+ BufferQueue::createBufferQueue(&producer, &mConsumer);
+ mSurface = sp<Surface>::make(producer, controlledByApp);
+ }
+
+ status_t setConsumerIsProtected(bool isProtected) {
+ return OK;
+ }
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
status_t acquireBuffer(BufferItem* item, nsecs_t presentWhen, bool waitForFence = true) {
return mConsumer->acquireBuffer(item, presentWhen, 0);
}
@@ -71,8 +86,20 @@
return OK;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+// Returns a Surface that can be used as the producer for this consumer.
+ sp<Surface> getSurface() const {
+ return mSurface;
+ }
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
private:
sp<IGraphicBufferConsumer> mConsumer;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ // This Surface wraps the IGraphicBufferConsumer created for this
+ // ConsumerBase.
+ sp<Surface> mSurface;
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
};
} // namespace android
diff --git a/libs/hostgraphics/include/gui/Surface.h b/libs/hostgraphics/include/gui/Surface.h
index 2774f89..e268ce6 100644
--- a/libs/hostgraphics/include/gui/Surface.h
+++ b/libs/hostgraphics/include/gui/Surface.h
@@ -34,6 +34,10 @@
ANativeWindow::query = hook_query;
}
+ sp<IGraphicBufferProducer> getIGraphicBufferProducer() const {
+ return mBufferProducer;
+ }
+
static bool isValid(const sp<Surface>& surface) {
return surface != nullptr;
}
diff --git a/libs/hostgraphics/include/ui/Fence.h b/libs/hostgraphics/include/ui/Fence.h
index 187c311..3364b8ae 100644
--- a/libs/hostgraphics/include/ui/Fence.h
+++ b/libs/hostgraphics/include/ui/Fence.h
@@ -60,6 +60,10 @@
return 0;
}
+ int get() const {
+ return 0;
+ }
+
inline Status getStatus() {
// The sync_wait call underlying wait() has been measured to be
// significantly faster than the sync_fence_info call underlying
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index bb2a53b..38ac8ab 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -233,6 +233,14 @@
}
filegroup {
+ name: "framework-graphics-ravenwood-policies",
+ srcs: [
+ "framework-graphics-ravenwood-policies.txt",
+ ],
+ visibility: ["//frameworks/base/ravenwood"],
+}
+
+filegroup {
name: "framework-graphics-srcs",
srcs: [
"apex/java/**/*.java",
@@ -461,6 +469,10 @@
},
linux: {
srcs: ["platform/linux/utils/SharedLib.cpp"],
+ shared_libs: [
+ "libbinder",
+ "libbinder_ndk",
+ ],
},
darwin: {
srcs: ["platform/darwin/utils/SharedLib.cpp"],
diff --git a/libs/hwui/framework-graphics-ravenwood-policies.txt b/libs/hwui/framework-graphics-ravenwood-policies.txt
new file mode 100644
index 0000000..7296225
--- /dev/null
+++ b/libs/hwui/framework-graphics-ravenwood-policies.txt
@@ -0,0 +1 @@
+class android.graphics.ColorMatrix keepclass
diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
index cfde0b2..27d4ac7 100644
--- a/libs/hwui/jni/Bitmap.cpp
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -613,7 +613,7 @@
///////////////////////////////////////////////////////////////////////////////
// TODO: Move somewhere else
-#ifdef __ANDROID__ // Layoutlib does not support parcel
+#ifdef __linux__ // Only Linux support parcel
#define ON_ERROR_RETURN(X) \
if ((error = (X)) != STATUS_OK) return error
@@ -717,7 +717,7 @@
#undef ON_ERROR_RETURN
-#endif // __ANDROID__ // Layoutlib does not support parcel
+#endif // __linux__ // Only Linux support parcel
// This is the maximum possible size because the SkColorSpace must be
// representable (and therefore serializable) using a matrix and numerical
@@ -733,7 +733,7 @@
}
static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
-#ifdef __ANDROID__ // Layoutlib does not support parcel
+#ifdef __linux__ // Only Linux support parcel
if (parcel == NULL) {
jniThrowNullPointerException(env, "parcel cannot be null");
return NULL;
@@ -836,14 +836,14 @@
return createBitmap(env, nativeBitmap.release(), getPremulBitmapCreateFlags(isMutable), nullptr,
nullptr, density, sourceId);
#else
- jniThrowRuntimeException(env, "Cannot use parcels outside of Android");
+ jniThrowRuntimeException(env, "Cannot use parcels outside of Linux");
return NULL;
#endif
}
static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, jlong bitmapHandle, jint density,
jobject parcel) {
-#ifdef __ANDROID__ // Layoutlib does not support parcel
+#ifdef __linux__ // Only Linux support parcel
if (parcel == NULL) {
ALOGD("------- writeToParcel null parcel\n");
return JNI_FALSE;
@@ -901,7 +901,7 @@
}
return JNI_TRUE;
#else
- doThrowRE(env, "Cannot use parcels outside of Android");
+ doThrowRE(env, "Cannot use parcels outside of Linux");
return JNI_FALSE;
#endif
}
diff --git a/libs/hwui/jni/Region.cpp b/libs/hwui/jni/Region.cpp
index 1e064b8..76986eeb 100644
--- a/libs/hwui/jni/Region.cpp
+++ b/libs/hwui/jni/Region.cpp
@@ -18,7 +18,7 @@
#include "SkPath.h"
#include "GraphicsJNI.h"
-#ifdef __ANDROID__ // Layoutlib does not support parcel
+#ifdef __linux__ // Only Linux support parcel
#include <android/binder_parcel.h>
#include <android/binder_parcel_jni.h>
#include <android/binder_parcel_utils.h>
@@ -202,7 +202,7 @@
static jlong Region_createFromParcel(JNIEnv* env, jobject clazz, jobject parcel)
{
-#ifdef __ANDROID__ // Layoutlib does not support parcel
+#ifdef __linux__ // Only Linux support parcel
if (parcel == nullptr) {
return 0;
}
@@ -230,7 +230,7 @@
static jboolean Region_writeToParcel(JNIEnv* env, jobject clazz, jlong regionHandle, jobject parcel)
{
-#ifdef __ANDROID__ // Layoutlib does not support parcel
+#ifdef __linux__ // Only Linux support parcel
const SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
if (parcel == nullptr) {
return JNI_FALSE;
diff --git a/libs/hwui/jni/ScopedParcel.cpp b/libs/hwui/jni/ScopedParcel.cpp
index 95e4e01..52cd988 100644
--- a/libs/hwui/jni/ScopedParcel.cpp
+++ b/libs/hwui/jni/ScopedParcel.cpp
@@ -15,7 +15,7 @@
*/
#include "ScopedParcel.h"
-#ifdef __ANDROID__ // Layoutlib does not support parcel
+#ifdef __linux__ // Only Linux support parcel
using namespace android;
@@ -92,4 +92,4 @@
AParcel_writeByteArray(mParcel, nullptr, -1);
}
}
-#endif // __ANDROID__ // Layoutlib does not support parcel
+#endif // __linux__ // Only Linux support parcel
diff --git a/libs/hwui/jni/ScopedParcel.h b/libs/hwui/jni/ScopedParcel.h
index f2f138f..f2b793a 100644
--- a/libs/hwui/jni/ScopedParcel.h
+++ b/libs/hwui/jni/ScopedParcel.h
@@ -15,7 +15,7 @@
*/
#include "SkData.h"
-#ifdef __ANDROID__ // Layoutlib does not support parcel
+#ifdef __linux__ // Only Linux support parcel
#include <android-base/unique_fd.h>
#include <android/binder_parcel.h>
#include <android/binder_parcel_jni.h>
@@ -64,4 +64,4 @@
ASHMEM,
};
-#endif // __ANDROID__ // Layoutlib does not support parcel
\ No newline at end of file
+#endif // __linux__ // Only Linux support parcel
diff --git a/libs/hwui/jni/graphics_jni_helpers.h b/libs/hwui/jni/graphics_jni_helpers.h
index 91db134..ff26ec1 100644
--- a/libs/hwui/jni/graphics_jni_helpers.h
+++ b/libs/hwui/jni/graphics_jni_helpers.h
@@ -21,6 +21,7 @@
#include <nativehelper/JNIPlatformHelp.h>
#include <nativehelper/scoped_local_ref.h>
#include <nativehelper/scoped_utf_chars.h>
+#include <nativehelper/scoped_primitive_array.h>
#include <string>
// Host targets (layoutlib) do not differentiate between regular and critical native methods,
diff --git a/media/java/android/media/AudioDeviceVolumeManager.java b/media/java/android/media/AudioDeviceVolumeManager.java
index 892a861..56d3df3 100644
--- a/media/java/android/media/AudioDeviceVolumeManager.java
+++ b/media/java/android/media/AudioDeviceVolumeManager.java
@@ -16,6 +16,9 @@
package android.media;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+import static android.media.audio.Flags.FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT;
+
import static com.android.media.flags.Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL;
import android.Manifest;
@@ -91,6 +94,8 @@
* @see #setDeviceAbsoluteVolumeBehavior(AudioDeviceAttributes, VolumeInfo, boolean, Executor,
* OnAudioDeviceVolumeChangedListener)
*/
+ @SystemApi(client = MODULE_LIBRARIES)
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
public interface OnAudioDeviceVolumeChangedListener {
/**
* Called the device for the given audio device has changed.
@@ -203,6 +208,30 @@
* volume updates to apply on that device
* @param device the audio device set to absolute volume mode
* @param volume the type of volume this device responds to
+ * @param executor the Executor used for receiving volume updates through the listener
+ * @param vclistener the callback for volume updates
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+ android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ android.Manifest.permission.BLUETOOTH_STACK})
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
+ public void setDeviceAbsoluteVolumeBehavior(
+ @NonNull AudioDeviceAttributes device,
+ @NonNull VolumeInfo volume,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnAudioDeviceVolumeChangedListener vclistener) {
+ setDeviceAbsoluteVolumeBehavior(device, volume, /*handlesVolumeAdjustment=*/false, executor,
+ vclistener);
+ }
+
+ /**
+ * @hide
+ * Configures a device to use absolute volume model, and registers a listener for receiving
+ * volume updates to apply on that device
+ * @param device the audio device set to absolute volume mode
+ * @param volume the type of volume this device responds to
* @param handlesVolumeAdjustment whether the controller handles volume adjustments separately
* from volume changes. If true, adjustments from {@link AudioManager#adjustStreamVolume}
* will be sent via {@link OnAudioDeviceVolumeChangedListener#onAudioDeviceVolumeAdjusted}.
@@ -210,7 +239,7 @@
* @param vclistener the callback for volume updates
*/
@RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
- android.Manifest.permission.BLUETOOTH_PRIVILEGED })
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED})
public void setDeviceAbsoluteVolumeBehavior(
@NonNull AudioDeviceAttributes device,
@NonNull VolumeInfo volume,
@@ -229,6 +258,30 @@
* registers a listener for receiving volume updates to apply on that device
* @param device the audio device set to absolute multi-volume mode
* @param volumes the list of volumes the given device responds to
+ * @param executor the Executor used for receiving volume updates through the listener
+ * @param vclistener the callback for volume updates
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+ android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ android.Manifest.permission.BLUETOOTH_STACK})
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
+ public void setDeviceAbsoluteMultiVolumeBehavior(
+ @NonNull AudioDeviceAttributes device,
+ @NonNull List<VolumeInfo> volumes,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnAudioDeviceVolumeChangedListener vclistener) {
+ setDeviceAbsoluteMultiVolumeBehavior(device, volumes, /*handlesVolumeAdjustment=*/false,
+ executor, vclistener);
+ }
+
+ /**
+ * @hide
+ * Configures a device to use absolute volume model applied to different volume types, and
+ * registers a listener for receiving volume updates to apply on that device
+ * @param device the audio device set to absolute multi-volume mode
+ * @param volumes the list of volumes the given device responds to
* @param handlesVolumeAdjustment whether the controller handles volume adjustments separately
* from volume changes. If true, adjustments from {@link AudioManager#adjustStreamVolume}
* will be sent via {@link OnAudioDeviceVolumeChangedListener#onAudioDeviceVolumeAdjusted}.
@@ -236,7 +289,7 @@
* @param vclistener the callback for volume updates
*/
@RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
- android.Manifest.permission.BLUETOOTH_PRIVILEGED })
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED})
public void setDeviceAbsoluteMultiVolumeBehavior(
@NonNull AudioDeviceAttributes device,
@NonNull List<VolumeInfo> volumes,
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/ActionButtonsPreference/res/layout-v35/settingslib_expressive_action_buttons.xml b/packages/SettingsLib/ActionButtonsPreference/res/layout-v36/settingslib_expressive_action_buttons.xml
similarity index 100%
rename from packages/SettingsLib/ActionButtonsPreference/res/layout-v35/settingslib_expressive_action_buttons.xml
rename to packages/SettingsLib/ActionButtonsPreference/res/layout-v36/settingslib_expressive_action_buttons.xml
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/values-v35/styles_expressive.xml b/packages/SettingsLib/ActionButtonsPreference/res/values-v36/styles_expressive.xml
similarity index 100%
rename from packages/SettingsLib/ActionButtonsPreference/res/values-v35/styles_expressive.xml
rename to packages/SettingsLib/ActionButtonsPreference/res/values-v36/styles_expressive.xml
diff --git a/packages/SettingsLib/BannerMessagePreference/Android.bp b/packages/SettingsLib/BannerMessagePreference/Android.bp
index 182daeb..203a3bf 100644
--- a/packages/SettingsLib/BannerMessagePreference/Android.bp
+++ b/packages/SettingsLib/BannerMessagePreference/Android.bp
@@ -31,5 +31,6 @@
apex_available: [
"//apex_available:platform",
"com.android.healthfitness",
+ "com.android.permission",
],
}
diff --git a/packages/SettingsLib/BannerMessagePreference/res/drawable-v35/settingslib_expressive_card_background.xml b/packages/SettingsLib/BannerMessagePreference/res/drawable-v36/settingslib_expressive_card_background.xml
similarity index 100%
rename from packages/SettingsLib/BannerMessagePreference/res/drawable-v35/settingslib_expressive_card_background.xml
rename to packages/SettingsLib/BannerMessagePreference/res/drawable-v36/settingslib_expressive_card_background.xml
diff --git a/packages/SettingsLib/BannerMessagePreference/res/drawable-v36/settingslib_resolved_banner_avd.xml b/packages/SettingsLib/BannerMessagePreference/res/drawable-v36/settingslib_resolved_banner_avd.xml
new file mode 100644
index 0000000..c999de7
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/drawable-v36/settingslib_resolved_banner_avd.xml
@@ -0,0 +1,157 @@
+<!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="112dp"
+ android:height="112dp"
+ android:viewportHeight="112"
+ android:viewportWidth="112">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:translateX="56.5"
+ android:translateY="56.625">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="0"
+ android:fillColor="?android:attr/textColorPrimary"
+ android:fillType="nonZero"
+ android:pathData=" M-14.75 -0.59 C-14.75,-0.59 -15.32,-1.16 -15.32,-1.16 C-15.32,-1.16 -14.67,-0.73 -14.67,-0.73 C-14.67,-0.73 -14.39,-0.44 -14.39,-0.44 C-14.39,-0.44 -17.32,2.5 -17.32,2.5 C-17.32,2.5 -17.59,2.23 -17.59,2.23 C-17.59,2.23 -14.75,-0.59 -14.75,-0.59c "
+ android:trimPathEnd="1"
+ android:trimPathOffset="0"
+ android:trimPathStart="0" />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:rotation="-90"
+ android:translateX="56"
+ android:translateY="56">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:pathData=" M53.5 0 C53.5,-29.53 29.53,-53.5 0,-53.5 C0,-53.5 0,-53.5 0,-53.5 C-29.53,-53.5 -53.5,-29.53 -53.5,0 C-53.5,0 -53.5,0 -53.5,0 C-53.5,29.53 -29.53,53.5 0,53.5 C0,53.5 0,53.5 0,53.5 C29.53,53.5 53.5,29.53 53.5,0 C53.5,0 53.5,0 53.5,0c "
+ android:strokeAlpha="1"
+ android:strokeColor="?android:attr/textColorTertiary"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="5"
+ android:trimPathEnd="0"
+ android:trimPathOffset="0"
+ android:trimPathStart="0" />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="400"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="fillAlpha"
+ android:startOffset="400"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="400"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M-14.75 -0.59 C-14.75,-0.59 -15.32,-1.16 -15.32,-1.16 C-15.32,-1.16 -14.67,-0.73 -14.67,-0.73 C-14.67,-0.73 -14.39,-0.44 -14.39,-0.44 C-14.39,-0.44 -17.32,2.5 -17.32,2.5 C-17.32,2.5 -17.59,2.23 -17.59,2.23 C-17.59,2.23 -14.75,-0.59 -14.75,-0.59c "
+ android:valueTo="M-14.75 -0.59 C-14.75,-0.59 -15.32,-1.16 -15.32,-1.16 C-15.32,-1.16 -14.67,-0.73 -14.67,-0.73 C-14.67,-0.73 -14.39,-0.44 -14.39,-0.44 C-14.39,-0.44 -17.32,2.5 -17.32,2.5 C-17.32,2.5 -17.59,2.23 -17.59,2.23 C-17.59,2.23 -14.75,-0.59 -14.75,-0.59c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="pathData"
+ android:startOffset="400"
+ android:valueFrom="M-14.75 -0.59 C-14.75,-0.59 -15.32,-1.16 -15.32,-1.16 C-15.32,-1.16 -14.67,-0.73 -14.67,-0.73 C-14.67,-0.73 -14.39,-0.44 -14.39,-0.44 C-14.39,-0.44 -17.32,2.5 -17.32,2.5 C-17.32,2.5 -17.59,2.23 -17.59,2.23 C-17.59,2.23 -14.75,-0.59 -14.75,-0.59c "
+ android:valueTo="M-14.75 -0.59 C-14.75,-0.59 -6.41,7.75 -6.41,7.75 C-6.41,7.75 -6.3,7.65 -6.3,7.65 C-6.3,7.65 -3.48,10.47 -3.48,10.47 C-3.48,10.47 -6.41,13.41 -6.41,13.41 C-6.41,13.41 -17.59,2.23 -17.59,2.23 C-17.59,2.23 -14.75,-0.59 -14.75,-0.59c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="pathData"
+ android:startOffset="483"
+ android:valueFrom="M-14.75 -0.59 C-14.75,-0.59 -6.41,7.75 -6.41,7.75 C-6.41,7.75 -6.3,7.65 -6.3,7.65 C-6.3,7.65 -3.48,10.47 -3.48,10.47 C-3.48,10.47 -6.41,13.41 -6.41,13.41 C-6.41,13.41 -17.59,2.23 -17.59,2.23 C-17.59,2.23 -14.75,-0.59 -14.75,-0.59c "
+ android:valueTo="M-14.75 -0.59 C-14.75,-0.59 -6.41,7.75 -6.41,7.75 C-6.41,7.75 14.77,-13.41 14.77,-13.41 C14.77,-13.41 17.59,-10.59 17.59,-10.59 C17.59,-10.59 -6.41,13.41 -6.41,13.41 C-6.41,13.41 -17.59,2.23 -17.59,2.23 C-17.59,2.23 -14.75,-0.59 -14.75,-0.59c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="500"
+ android:propertyName="trimPathEnd"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="667"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/BannerMessagePreference/res/layout-v35/settingslib_expressive_banner_message.xml b/packages/SettingsLib/BannerMessagePreference/res/layout-v35/settingslib_expressive_banner_message.xml
deleted file mode 100644
index b10ef6e..0000000
--- a/packages/SettingsLib/BannerMessagePreference/res/layout-v35/settingslib_expressive_banner_message.xml
+++ /dev/null
@@ -1,108 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2025 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart">
-
- <com.android.settingslib.widget.BannerMessageView
- android:id="@+id/banner_background"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- style="@style/Banner.Preference.SettingsLib.Expressive">
-
- <RelativeLayout
- android:id="@+id/top_row"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
- android:layout_marginEnd="@dimen/settingslib_expressive_space_medium4"
- android:orientation="vertical">
- <TextView
- android:id="@+id/banner_header"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/Banner.Header.SettingsLib.Expressive"/>
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
-
- <ImageView
- android:id="@+id/banner_icon"
- android:layout_width="@dimen/settingslib_expressive_space_small3"
- android:layout_height="@dimen/settingslib_expressive_space_small3"
- android:layout_gravity="center_vertical"
- android:importantForAccessibility="no"
- android:scaleType="fitCenter" />
-
- <TextView
- android:id="@+id/banner_title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/Banner.Title.SettingsLib.Expressive" />
- </LinearLayout>
-
- <TextView
- android:id="@+id/banner_subtitle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/Banner.Subtitle.SettingsLib.Expressive"/>
- </LinearLayout>
-
- <ImageButton
- android:id="@+id/banner_dismiss_btn"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@style/Banner.Dismiss.SettingsLib.Expressive" />
- </RelativeLayout>
-
- <TextView
- android:id="@+id/banner_summary"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@style/Banner.Summary.SettingsLib.Expressive"/>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/banner_buttons_frame"
- android:paddingTop="@dimen/settingslib_expressive_space_extrasmall6"
- android:orientation="horizontal">
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/banner_negative_btn"
- android:layout_weight="1"
- style="@style/Banner.NegativeButton.SettingsLib.Expressive"/>
- <Space
- android:layout_width="@dimen/settingslib_expressive_space_extrasmall4"
- android:layout_height="@dimen/settingslib_expressive_space_small1"/>
- <com.google.android.material.button.MaterialButton
- android:id="@+id/banner_positive_btn"
- android:layout_weight="1"
- style="@style/Banner.PositiveButton.SettingsLib.Expressive"/>
- </LinearLayout>
- </com.android.settingslib.widget.BannerMessageView>
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/BannerMessagePreference/res/layout-v36/settingslib_expressive_banner_message.xml b/packages/SettingsLib/BannerMessagePreference/res/layout-v36/settingslib_expressive_banner_message.xml
new file mode 100644
index 0000000..c448a2d
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/layout-v36/settingslib_expressive_banner_message.xml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart">
+
+ <com.android.settingslib.widget.BannerMessageView
+ android:id="@+id/banner_background"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ style="@style/Banner.Preference.SettingsLib.Expressive">
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:id="@+id/banner_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <RelativeLayout
+ android:id="@+id/top_row"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_marginEnd="@dimen/settingslib_expressive_space_medium4"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/banner_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/Banner.Header.SettingsLib.Expressive"/>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@+id/banner_icon"
+ android:layout_width="@dimen/settingslib_expressive_space_small3"
+ android:layout_height="@dimen/settingslib_expressive_space_small3"
+ android:layout_gravity="center_vertical"
+ android:layout_marginEnd="@dimen/settingslib_expressive_space_extrasmall4"
+ android:importantForAccessibility="no"
+ android:scaleType="fitCenter" />
+
+ <TextView
+ android:id="@+id/banner_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/Banner.Title.SettingsLib.Expressive" />
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/banner_subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/Banner.Subtitle.SettingsLib.Expressive"/>
+ </LinearLayout>
+
+ <ImageButton
+ android:id="@+id/banner_dismiss_btn"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/accessibility_banner_message_dismiss"
+ style="@style/Banner.Dismiss.SettingsLib.Expressive" />
+ </RelativeLayout>
+
+ <TextView
+ android:id="@+id/banner_summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/Banner.Summary.SettingsLib.Expressive"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/banner_buttons_frame"
+ android:paddingTop="@dimen/settingslib_expressive_space_extrasmall6"
+ android:orientation="horizontal">
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/banner_negative_btn"
+ android:layout_weight="1"
+ style="@style/Banner.NegativeButton.SettingsLib.Expressive"/>
+ <Space
+ android:id="@+id/banner_button_space"
+ android:layout_width="@dimen/settingslib_expressive_space_extrasmall4"
+ android:layout_height="@dimen/settingslib_expressive_space_small1"/>
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/banner_positive_btn"
+ android:layout_weight="1"
+ style="@style/Banner.PositiveButton.SettingsLib.Expressive"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/resolved_banner_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:drawableTop="@drawable/settingslib_resolved_banner_avd"
+ android:visibility="gone"
+ style="@style/Banner.ResolvedText.SettingsLib.Expressive"/>
+ </FrameLayout>
+ </com.android.settingslib.widget.BannerMessageView>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-v35/styles_expressive.xml b/packages/SettingsLib/BannerMessagePreference/res/values-v36/styles_expressive.xml
similarity index 89%
rename from packages/SettingsLib/BannerMessagePreference/res/values-v35/styles_expressive.xml
rename to packages/SettingsLib/BannerMessagePreference/res/values-v36/styles_expressive.xml
index b864311..09e07cc 100644
--- a/packages/SettingsLib/BannerMessagePreference/res/values-v35/styles_expressive.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-v36/styles_expressive.xml
@@ -33,7 +33,6 @@
<style name="Banner.Title.SettingsLib.Expressive"
parent="">
<item name="android:layout_gravity">start</item>
- <item name="android:layout_marginLeft">@dimen/settingslib_expressive_space_extrasmall4</item>
<item name="android:textAlignment">viewStart</item>
<item name="android:textAppearance">@style/TextAppearance.SettingsLib.TitleLarge.Emphasized</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
@@ -73,4 +72,11 @@
parent="@style/SettingsLibButtonStyle.Expressive.Outline.Extra">
<item name="materialSizeOverlay">@style/SizeOverlay.Material3Expressive.Button.Small</item>
</style>
+
+ <style name="Banner.ResolvedText.SettingsLib.Expressive" parent="">
+ <item name="android:layout_gravity">center</item>
+ <item name="android:drawablePadding">@dimen/settingslib_expressive_space_small1</item>
+ <item name="android:textAppearance">@style/TextAppearance.SettingsLib.BodyMedium</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
index c82829d..c90a76a 100644
--- a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
+++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
@@ -35,6 +35,8 @@
import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.StringRes;
import androidx.preference.Preference;
@@ -43,6 +45,9 @@
import com.android.settingslib.widget.preference.banner.R;
import com.google.android.material.button.MaterialButton;
+
+import java.util.Objects;
+
/**
* Banner message is a banner displaying important information (permission request, page error etc),
* and provide actions for user to address. It requires a user action to be dismissed.
@@ -67,13 +72,15 @@
R.color.banner_accent_attention_normal,
R.color.settingslib_banner_button_background_normal);
- // Corresponds to the enum valye of R.attr.attentionLevel
+ // Corresponds to the enum value of R.attr.attentionLevel
private final int mAttrValue;
@ColorRes private final int mBackgroundColorResId;
@ColorRes private final int mAccentColorResId;
@ColorRes private final int mButtonBackgroundColorResId;
- AttentionLevel(int attrValue, @ColorRes int backgroundColorResId,
+ AttentionLevel(
+ int attrValue,
+ @ColorRes int backgroundColorResId,
@ColorRes int accentColorResId,
@ColorRes int buttonBackgroundColorResId) {
mAttrValue = attrValue;
@@ -115,33 +122,38 @@
new BannerMessagePreference.DismissButtonInfo();
// Default attention level is High.
- private AttentionLevel mAttentionLevel = AttentionLevel.HIGH;
- private CharSequence mSubtitle;
- private CharSequence mHeader;
+ @NonNull private AttentionLevel mAttentionLevel = AttentionLevel.HIGH;
+ @Nullable private CharSequence mSubtitle;
+ @Nullable private CharSequence mHeader;
private int mButtonOrientation;
+ @Nullable private ResolutionAnimator.Data mResolutionData;
- public BannerMessagePreference(Context context) {
+ public BannerMessagePreference(@NonNull Context context) {
super(context);
init(context, null /* attrs */);
}
- public BannerMessagePreference(Context context, AttributeSet attrs) {
+ public BannerMessagePreference(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
- public BannerMessagePreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ public BannerMessagePreference(
+ @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
- public BannerMessagePreference(Context context, AttributeSet attrs, int defStyleAttr,
+ public BannerMessagePreference(
+ @NonNull Context context,
+ @Nullable AttributeSet attrs,
+ int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
- private void init(Context context, AttributeSet attrs) {
+ private void init(@NonNull Context context, @Nullable AttributeSet attrs) {
setSelectable(false);
int resId = SettingsThemeHelper.isExpressiveTheme(context)
? R.layout.settingslib_expressive_banner_message
@@ -166,7 +178,7 @@
}
@Override
- public void onBindViewHolder(PreferenceViewHolder holder) {
+ public void onBindViewHolder(@NonNull PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
final Context context = getContext();
@@ -193,14 +205,20 @@
final ImageView iconView = (ImageView) holder.findViewById(R.id.banner_icon);
if (iconView != null) {
Drawable icon = getIcon();
- iconView.setImageDrawable(
- icon == null
- ? getContext().getDrawable(R.drawable.ic_warning)
- : icon);
- if (mAttentionLevel != AttentionLevel.NORMAL
- && !SettingsThemeHelper.isExpressiveTheme(context)) {
- iconView.setColorFilter(
- new PorterDuffColorFilter(accentColor, PorterDuff.Mode.SRC_IN));
+
+ if (icon == null && SettingsThemeHelper.isExpressiveTheme(context)) {
+ iconView.setVisibility(View.GONE);
+ } else {
+ iconView.setVisibility(View.VISIBLE);
+ iconView.setImageDrawable(
+ icon == null
+ ? getContext().getDrawable(R.drawable.ic_warning)
+ : icon);
+ if (mAttentionLevel != AttentionLevel.NORMAL
+ && !SettingsThemeHelper.isExpressiveTheme(context)) {
+ iconView.setColorFilter(
+ new PorterDuffColorFilter(accentColor, PorterDuff.Mode.SRC_IN));
+ }
}
}
@@ -233,8 +251,10 @@
mDismissButtonInfo.setUpButton();
final TextView subtitleView = (TextView) holder.findViewById(R.id.banner_subtitle);
- subtitleView.setText(mSubtitle);
- subtitleView.setVisibility(mSubtitle == null ? View.GONE : View.VISIBLE);
+ if (subtitleView != null) {
+ subtitleView.setText(mSubtitle);
+ subtitleView.setVisibility(mSubtitle == null ? View.GONE : View.VISIBLE);
+ }
TextView headerView = (TextView) holder.findViewById(R.id.banner_header);
if (headerView != null) {
@@ -268,11 +288,25 @@
linearLayout.setOrientation(mButtonOrientation);
}
}
+
+ View buttonSpace = holder.findViewById(R.id.banner_button_space);
+ if (buttonSpace != null) {
+ if (mPositiveButtonInfo.shouldBeVisible() && mNegativeButtonInfo.shouldBeVisible()) {
+ buttonSpace.setVisibility(View.VISIBLE);
+ } else {
+ buttonSpace.setVisibility(View.GONE);
+ }
+ }
+
+ if (mResolutionData != null) {
+ new ResolutionAnimator(mResolutionData, holder).startResolutionAnimation();
+ }
}
/**
- * Set the visibility state of positive button.
+ * Sets the visibility state of the positive button.
*/
+ @NonNull
public BannerMessagePreference setPositiveButtonVisible(boolean isVisible) {
if (isVisible != mPositiveButtonInfo.mIsVisible) {
mPositiveButtonInfo.mIsVisible = isVisible;
@@ -282,8 +316,9 @@
}
/**
- * Set the visibility state of negative button.
+ * Sets the visibility state of the negative button.
*/
+ @NonNull
public BannerMessagePreference setNegativeButtonVisible(boolean isVisible) {
if (isVisible != mNegativeButtonInfo.mIsVisible) {
mNegativeButtonInfo.mIsVisible = isVisible;
@@ -293,9 +328,10 @@
}
/**
- * Set the visibility state of dismiss button.
+ * Sets the visibility state of the dismiss button.
*/
@RequiresApi(Build.VERSION_CODES.S)
+ @NonNull
public BannerMessagePreference setDismissButtonVisible(boolean isVisible) {
if (isVisible != mDismissButtonInfo.mIsVisible) {
mDismissButtonInfo.mIsVisible = isVisible;
@@ -305,10 +341,35 @@
}
/**
- * Register a callback to be invoked when positive button is clicked.
+ * Sets the enabled state of the positive button.
*/
+ @NonNull
+ public BannerMessagePreference setPositiveButtonEnabled(boolean isEnabled) {
+ if (isEnabled != mPositiveButtonInfo.mIsEnabled) {
+ mPositiveButtonInfo.mIsEnabled = isEnabled;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the enabled state of the negative button.
+ */
+ @NonNull
+ public BannerMessagePreference setNegativeButtonEnabled(boolean isEnabled) {
+ if (isEnabled != mNegativeButtonInfo.mIsEnabled) {
+ mNegativeButtonInfo.mIsEnabled = isEnabled;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Registers a callback to be invoked when positive button is clicked.
+ */
+ @NonNull
public BannerMessagePreference setPositiveButtonOnClickListener(
- View.OnClickListener listener) {
+ @Nullable View.OnClickListener listener) {
if (listener != mPositiveButtonInfo.mListener) {
mPositiveButtonInfo.mListener = listener;
notifyChanged();
@@ -317,10 +378,11 @@
}
/**
- * Register a callback to be invoked when negative button is clicked.
+ * Registers a callback to be invoked when negative button is clicked.
*/
+ @NonNull
public BannerMessagePreference setNegativeButtonOnClickListener(
- View.OnClickListener listener) {
+ @Nullable View.OnClickListener listener) {
if (listener != mNegativeButtonInfo.mListener) {
mNegativeButtonInfo.mListener = listener;
notifyChanged();
@@ -329,11 +391,12 @@
}
/**
- * Register a callback to be invoked when the dismiss button is clicked.
+ * Registers a callback to be invoked when the dismiss button is clicked.
*/
@RequiresApi(Build.VERSION_CODES.S)
+ @NonNull
public BannerMessagePreference setDismissButtonOnClickListener(
- View.OnClickListener listener) {
+ @Nullable View.OnClickListener listener) {
if (listener != mDismissButtonInfo.mListener) {
mDismissButtonInfo.mListener = listener;
notifyChanged();
@@ -344,6 +407,7 @@
/**
* Sets the text to be displayed in positive button.
*/
+ @NonNull
public BannerMessagePreference setPositiveButtonText(@StringRes int textResId) {
return setPositiveButtonText(getContext().getString(textResId));
}
@@ -351,7 +415,9 @@
/**
* Sets the text to be displayed in positive button.
*/
- public BannerMessagePreference setPositiveButtonText(CharSequence positiveButtonText) {
+ @NonNull
+ public BannerMessagePreference setPositiveButtonText(
+ @Nullable CharSequence positiveButtonText) {
if (!TextUtils.equals(positiveButtonText, mPositiveButtonInfo.mText)) {
mPositiveButtonInfo.mText = positiveButtonText;
notifyChanged();
@@ -362,6 +428,7 @@
/**
* Sets the text to be displayed in negative button.
*/
+ @NonNull
public BannerMessagePreference setNegativeButtonText(@StringRes int textResId) {
return setNegativeButtonText(getContext().getString(textResId));
}
@@ -369,7 +436,9 @@
/**
* Sets the text to be displayed in negative button.
*/
- public BannerMessagePreference setNegativeButtonText(CharSequence negativeButtonText) {
+ @NonNull
+ public BannerMessagePreference setNegativeButtonText(
+ @Nullable CharSequence negativeButtonText) {
if (!TextUtils.equals(negativeButtonText, mNegativeButtonInfo.mText)) {
mNegativeButtonInfo.mText = negativeButtonText;
notifyChanged();
@@ -380,8 +449,12 @@
/**
* Sets button orientation.
*/
- @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ @NonNull
public BannerMessagePreference setButtonOrientation(int orientation) {
+ if (!SettingsThemeHelper.isExpressiveTheme(getContext())) {
+ return this;
+ }
+
if (mButtonOrientation != orientation) {
mButtonOrientation = orientation;
notifyChanged();
@@ -393,6 +466,7 @@
* Sets the subtitle.
*/
@RequiresApi(Build.VERSION_CODES.S)
+ @NonNull
public BannerMessagePreference setSubtitle(@StringRes int textResId) {
return setSubtitle(getContext().getString(textResId));
}
@@ -401,7 +475,8 @@
* Sets the subtitle.
*/
@RequiresApi(Build.VERSION_CODES.S)
- public BannerMessagePreference setSubtitle(CharSequence subtitle) {
+ @NonNull
+ public BannerMessagePreference setSubtitle(@Nullable CharSequence subtitle) {
if (!TextUtils.equals(subtitle, mSubtitle)) {
mSubtitle = subtitle;
notifyChanged();
@@ -412,7 +487,7 @@
/**
* Sets the header.
*/
- @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ @NonNull
public BannerMessagePreference setHeader(@StringRes int textResId) {
return setHeader(getContext().getString(textResId));
}
@@ -420,8 +495,12 @@
/**
* Sets the header.
*/
- @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
- public BannerMessagePreference setHeader(CharSequence header) {
+ @NonNull
+ public BannerMessagePreference setHeader(@Nullable CharSequence header) {
+ if (!SettingsThemeHelper.isExpressiveTheme(getContext())) {
+ return this;
+ }
+
if (!TextUtils.equals(header, mHeader)) {
mHeader = header;
notifyChanged();
@@ -430,32 +509,75 @@
}
/**
- * Sets the attention level. This will update the color theme of the preference.
+ * Plays a resolution animation, showing the given message.
*/
- public BannerMessagePreference setAttentionLevel(AttentionLevel attentionLevel) {
- if (attentionLevel == mAttentionLevel) {
+ @NonNull
+ public BannerMessagePreference showResolutionAnimation(
+ @NonNull CharSequence resolutionMessage,
+ @NonNull ResolutionCompletedCallback onCompletionCallback) {
+ if (!SettingsThemeHelper.isExpressiveTheme(getContext())) {
return this;
}
-
- if (attentionLevel != null) {
- mAttentionLevel = attentionLevel;
+ ResolutionAnimator.Data resolutionData =
+ new ResolutionAnimator.Data(resolutionMessage, onCompletionCallback);
+ if (!Objects.equals(mResolutionData, resolutionData)) {
+ mResolutionData = resolutionData;
notifyChanged();
}
return this;
}
+ /**
+ * Removes the resolution animation from this preference.
+ *
+ * <p>Should be called after the resolution animation completes if this preference will be
+ * reused. Otherwise the resolution animation will be played everytime this preference is
+ * displayed.
+ */
+ @NonNull
+ public BannerMessagePreference clearResolutionAnimation() {
+ if (!SettingsThemeHelper.isExpressiveTheme(getContext())) {
+ return this;
+ }
+ if (mResolutionData != null) {
+ mResolutionData = null;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the attention level. This will update the color theme of the preference.
+ */
+ @NonNull
+ public BannerMessagePreference setAttentionLevel(@NonNull AttentionLevel attentionLevel) {
+ if (attentionLevel == mAttentionLevel) {
+ return this;
+ }
+
+ mAttentionLevel = attentionLevel;
+ notifyChanged();
+ return this;
+ }
+
static class ButtonInfo {
- private Button mButton;
- private CharSequence mText;
- private View.OnClickListener mListener;
+ @Nullable private Button mButton;
+ @Nullable private CharSequence mText;
+ @Nullable private View.OnClickListener mListener;
private boolean mIsVisible = true;
+ private boolean mIsEnabled = true;
@ColorInt private int mColor;
@ColorInt private int mBackgroundColor;
- private ColorStateList mStrokeColor;
+ @Nullable private ColorStateList mStrokeColor;
void setUpButton() {
+ if (mButton == null) {
+ return;
+ }
+
mButton.setText(mText);
mButton.setOnClickListener(mListener);
+ mButton.setEnabled(mIsEnabled);
MaterialButton btn = null;
if (mButton instanceof MaterialButton) {
@@ -492,11 +614,15 @@
}
static class DismissButtonInfo {
- private ImageButton mButton;
- private View.OnClickListener mListener;
+ @Nullable private ImageButton mButton;
+ @Nullable private View.OnClickListener mListener;
private boolean mIsVisible = true;
void setUpButton() {
+ if (mButton == null) {
+ return;
+ }
+
mButton.setOnClickListener(mListener);
if (shouldBeVisible()) {
mButton.setVisibility(View.VISIBLE);
diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessageView.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessageView.java
index ff4e79d..eabb634 100644
--- a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessageView.java
+++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessageView.java
@@ -23,6 +23,7 @@
import android.view.View;
import android.widget.LinearLayout;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.settingslib.widget.preference.banner.R;
@@ -34,22 +35,25 @@
* {@link BannerMessagePreference} to a {@code PreferenceScreen}.
*/
public class BannerMessageView extends LinearLayout {
- private Rect mTouchTargetForDismissButton;
+ @Nullable private Rect mTouchTargetForDismissButton;
- public BannerMessageView(Context context) {
+ public BannerMessageView(@NonNull Context context) {
super(context);
}
- public BannerMessageView(Context context,
- @Nullable AttributeSet attrs) {
+ public BannerMessageView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
- public BannerMessageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ public BannerMessageView(
+ @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
- public BannerMessageView(Context context, AttributeSet attrs, int defStyleAttr,
+ public BannerMessageView(
+ @NonNull Context context,
+ @Nullable AttributeSet attrs,
+ int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/ResolutionAnimator.kt b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/ResolutionAnimator.kt
new file mode 100644
index 0000000..fbf910a
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/ResolutionAnimator.kt
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget
+
+import android.graphics.drawable.Animatable2
+import android.graphics.drawable.AnimatedVectorDrawable
+import android.graphics.drawable.Drawable
+import android.os.Build
+import android.provider.DeviceConfig
+import android.transition.Fade
+import android.transition.Transition
+import android.transition.TransitionListenerAdapter
+import android.transition.TransitionManager
+import android.transition.TransitionSet
+import android.view.View
+import android.view.ViewGroup
+import android.view.animation.LinearInterpolator
+import android.widget.TextView
+import androidx.preference.PreferenceViewHolder
+import com.android.settingslib.widget.preference.banner.R
+import java.time.Duration
+
+/** Callback to communicate when a banner message resolution animation is completed. */
+fun interface ResolutionCompletedCallback {
+ fun onCompleted()
+}
+
+internal class ResolutionAnimator(
+ private val data: Data,
+ private val preferenceViewHolder: PreferenceViewHolder,
+) {
+
+ data class Data(
+ val resolutionMessage: CharSequence,
+ val resolutionCompletedCallback: ResolutionCompletedCallback,
+ )
+
+ private val defaultBannerContent: View?
+ get() = preferenceViewHolder.findView(R.id.banner_content)
+ private val resolvedTextView: TextView?
+ get() = preferenceViewHolder.findView(R.id.resolved_banner_text)
+
+ fun startResolutionAnimation() {
+ resolvedTextView?.text = data.resolutionMessage
+ resolvedTextView?.resolutionDrawable?.reset()
+
+ val transitionSet =
+ TransitionSet()
+ .setOrdering(TransitionSet.ORDERING_SEQUENTIAL)
+ .setInterpolator(linearInterpolator)
+ .addTransition(hideIssueContentTransition)
+ .addTransition(
+ showResolvedContentTransition
+ .clone()
+ .addListener(
+ object : TransitionListenerAdapter() {
+ override fun onTransitionEnd(transition: Transition) {
+ super.onTransitionEnd(transition)
+ startIssueResolvedAnimation()
+ }
+ }
+ )
+ )
+
+ preferenceViewHolder.itemView.post {
+ TransitionManager.beginDelayedTransition(
+ preferenceViewHolder.itemView as ViewGroup,
+ transitionSet,
+ )
+
+ defaultBannerContent?.visibility = View.INVISIBLE
+ resolvedTextView?.visibility = View.VISIBLE
+ }
+
+ preferenceViewHolder.itemView.addOnAttachStateChangeListener(
+ object : View.OnAttachStateChangeListener {
+ override fun onViewAttachedToWindow(v: View) {}
+
+ override fun onViewDetachedFromWindow(v: View) {
+ v.removeOnAttachStateChangeListener(this)
+ cancelAnimationsAndFinish()
+ }
+ }
+ )
+ }
+
+ private fun startIssueResolvedAnimation() {
+ val animatedDrawable = resolvedTextView?.resolutionDrawable
+
+ if (animatedDrawable == null) {
+ hideResolvedUiAndFinish()
+ return
+ }
+
+ animatedDrawable.apply {
+ clearAnimationCallbacks()
+ registerAnimationCallback(
+ object : Animatable2.AnimationCallback() {
+ override fun onAnimationEnd(drawable: Drawable) {
+ super.onAnimationEnd(drawable)
+ hideResolvedUiAndFinish()
+ }
+ }
+ )
+ start()
+ }
+ }
+
+ private fun hideResolvedUiAndFinish() {
+ val hideTransition =
+ hideResolvedContentTransition
+ .clone()
+ .setInterpolator(linearInterpolator)
+ .addListener(
+ object : TransitionListenerAdapter() {
+ override fun onTransitionEnd(transition: Transition) {
+ super.onTransitionEnd(transition)
+ data.resolutionCompletedCallback.onCompleted()
+ }
+ }
+ )
+ TransitionManager.beginDelayedTransition(
+ preferenceViewHolder.itemView as ViewGroup,
+ hideTransition,
+ )
+ resolvedTextView?.visibility = View.GONE
+ }
+
+ private fun cancelAnimationsAndFinish() {
+ TransitionManager.endTransitions(preferenceViewHolder.itemView as ViewGroup)
+
+ resolvedTextView?.visibility = View.GONE
+
+ val animatedDrawable = resolvedTextView?.resolutionDrawable
+ animatedDrawable?.clearAnimationCallbacks()
+ animatedDrawable?.stop()
+
+ data.resolutionCompletedCallback.onCompleted()
+ }
+
+ private companion object {
+ private val linearInterpolator = LinearInterpolator()
+
+ private val HIDE_ISSUE_CONTENT_TRANSITION_DURATION = Duration.ofMillis(333)
+ private val hideIssueContentTransition =
+ Fade(Fade.OUT).setDuration(HIDE_ISSUE_CONTENT_TRANSITION_DURATION.toMillis())
+
+ private val SHOW_RESOLVED_CONTENT_TRANSITION_DELAY = Duration.ofMillis(133)
+ private val SHOW_RESOLVED_CONTENT_TRANSITION_DURATION = Duration.ofMillis(250)
+ private val showResolvedContentTransition =
+ Fade(Fade.IN)
+ .setStartDelay(SHOW_RESOLVED_CONTENT_TRANSITION_DELAY.toMillis())
+ .setDuration(SHOW_RESOLVED_CONTENT_TRANSITION_DURATION.toMillis())
+
+ private val hideResolvedContentTransitionDelay
+ get() =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ Duration.ofMillis(
+ DeviceConfig.getLong(
+ "settings_ui",
+ "banner_message_pref_hide_resolved_content_delay_millis",
+ 400,
+ )
+ )
+ } else {
+ Duration.ofMillis(400)
+ }
+
+ private val HIDE_RESOLVED_UI_TRANSITION_DURATION = Duration.ofMillis(167)
+ private val hideResolvedContentTransition
+ get() =
+ Fade(Fade.OUT)
+ .setStartDelay(hideResolvedContentTransitionDelay.toMillis())
+ .setDuration(HIDE_RESOLVED_UI_TRANSITION_DURATION.toMillis())
+
+ inline fun <reified T : View> PreferenceViewHolder.findView(id: Int): T? =
+ findViewById(id) as? T
+
+ val TextView.resolutionDrawable: AnimatedVectorDrawable?
+ get() = compoundDrawables.find { it != null } as? AnimatedVectorDrawable
+ }
+}
diff --git a/packages/SettingsLib/ButtonPreference/Android.bp b/packages/SettingsLib/ButtonPreference/Android.bp
index a377f31..c8375a9 100644
--- a/packages/SettingsLib/ButtonPreference/Android.bp
+++ b/packages/SettingsLib/ButtonPreference/Android.bp
@@ -30,5 +30,6 @@
apex_available: [
"//apex_available:platform",
"com.android.healthfitness",
+ "com.android.permission",
],
}
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled.xml b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_filled.xml
similarity index 100%
rename from packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled.xml
rename to packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_filled.xml
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_extra.xml b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_filled_extra.xml
similarity index 100%
rename from packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_extra.xml
rename to packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_filled_extra.xml
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_large.xml b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_filled_large.xml
similarity index 100%
rename from packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_large.xml
rename to packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_filled_large.xml
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline.xml b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_outline.xml
similarity index 100%
rename from packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline.xml
rename to packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_outline.xml
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_extra.xml b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_outline_extra.xml
similarity index 100%
rename from packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_extra.xml
rename to packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_outline_extra.xml
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_large.xml b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_outline_large.xml
similarity index 100%
rename from packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_large.xml
rename to packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_outline_large.xml
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal.xml b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_tonal.xml
similarity index 100%
rename from packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal.xml
rename to packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_tonal.xml
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_extra.xml b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_tonal_extra.xml
similarity index 100%
rename from packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_extra.xml
rename to packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_tonal_extra.xml
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_large.xml b/packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_tonal_large.xml
similarity index 100%
rename from packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_large.xml
rename to packages/SettingsLib/ButtonPreference/res/layout-v36/settingslib_expressive_button_tonal_large.xml
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/drawable-v35/settingslib_expressive_icon_back.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/drawable-v36/settingslib_expressive_icon_back.xml
similarity index 100%
rename from packages/SettingsLib/CollapsingToolbarBaseActivity/res/drawable-v35/settingslib_expressive_icon_back.xml
rename to packages/SettingsLib/CollapsingToolbarBaseActivity/res/drawable-v36/settingslib_expressive_icon_back.xml
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v35/settingslib_expressive_collapsing_toolbar_base_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_base_layout.xml
similarity index 100%
rename from packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v35/settingslib_expressive_collapsing_toolbar_base_layout.xml
rename to packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_base_layout.xml
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v35/settingslib_expressive_collapsing_toolbar_content_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml
similarity index 100%
rename from packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v35/settingslib_expressive_collapsing_toolbar_content_layout.xml
rename to packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v35/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v36/themes.xml
similarity index 100%
rename from packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v35/themes.xml
rename to packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v36/themes.xml
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/styles.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v36/styles.xml
similarity index 100%
rename from packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/styles.xml
rename to packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v36/styles.xml
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/styles_expressive.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v36/styles_expressive.xml
similarity index 100%
rename from packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/styles_expressive.xml
rename to packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v36/styles_expressive.xml
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v36/themes.xml
similarity index 100%
rename from packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/themes.xml
rename to packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v36/themes.xml
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/themes_expressive.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v36/themes_expressive.xml
similarity index 100%
rename from packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v35/themes_expressive.xml
rename to packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v36/themes_expressive.xml
diff --git a/packages/SettingsLib/MainSwitchPreference/res/color-night-v35/settingslib_main_switch_text_color.xml b/packages/SettingsLib/MainSwitchPreference/res/color-night-v36/settingslib_main_switch_text_color.xml
similarity index 100%
rename from packages/SettingsLib/MainSwitchPreference/res/color-night-v35/settingslib_main_switch_text_color.xml
rename to packages/SettingsLib/MainSwitchPreference/res/color-night-v36/settingslib_main_switch_text_color.xml
diff --git a/packages/SettingsLib/MainSwitchPreference/res/color-v35/settingslib_main_switch_text_color.xml b/packages/SettingsLib/MainSwitchPreference/res/color-v36/settingslib_main_switch_text_color.xml
similarity index 100%
rename from packages/SettingsLib/MainSwitchPreference/res/color-v35/settingslib_main_switch_text_color.xml
rename to packages/SettingsLib/MainSwitchPreference/res/color-v36/settingslib_main_switch_text_color.xml
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable-v35/settingslib_expressive_switch_bar_bg.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable-v36/settingslib_expressive_switch_bar_bg.xml
similarity index 100%
rename from packages/SettingsLib/MainSwitchPreference/res/drawable-v35/settingslib_expressive_switch_bar_bg.xml
rename to packages/SettingsLib/MainSwitchPreference/res/drawable-v36/settingslib_expressive_switch_bar_bg.xml
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v36/settingslib_expressive_main_switch_bar.xml
similarity index 100%
rename from packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml
rename to packages/SettingsLib/MainSwitchPreference/res/layout-v36/settingslib_expressive_main_switch_bar.xml
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_layout.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v36/settingslib_expressive_main_switch_layout.xml
similarity index 100%
rename from packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_layout.xml
rename to packages/SettingsLib/MainSwitchPreference/res/layout-v36/settingslib_expressive_main_switch_layout.xml
diff --git a/packages/SettingsLib/ProfileSelector/res/color-night-v35/settingslib_tabs_indicator_color.xml b/packages/SettingsLib/ProfileSelector/res/color-night-v36/settingslib_tabs_indicator_color.xml
similarity index 100%
rename from packages/SettingsLib/ProfileSelector/res/color-night-v35/settingslib_tabs_indicator_color.xml
rename to packages/SettingsLib/ProfileSelector/res/color-night-v36/settingslib_tabs_indicator_color.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/color-night-v35/settingslib_switch_track_outline_color.xml b/packages/SettingsLib/SettingsTheme/res/color-night-v36/settingslib_switch_track_outline_color.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/color-night-v35/settingslib_switch_track_outline_color.xml
rename to packages/SettingsLib/SettingsTheme/res/color-night-v36/settingslib_switch_track_outline_color.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_expressive_color_main_switch_track.xml b/packages/SettingsLib/SettingsTheme/res/color-v36/settingslib_expressive_color_main_switch_track.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_expressive_color_main_switch_track.xml
rename to packages/SettingsLib/SettingsTheme/res/color-v36/settingslib_expressive_color_main_switch_track.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_preference_bg_color.xml b/packages/SettingsLib/SettingsTheme/res/color-v36/settingslib_preference_bg_color.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_preference_bg_color.xml
rename to packages/SettingsLib/SettingsTheme/res/color-v36/settingslib_preference_bg_color.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_switch_track_outline_color.xml b/packages/SettingsLib/SettingsTheme/res/color-v36/settingslib_switch_track_outline_color.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_switch_track_outline_color.xml
rename to packages/SettingsLib/SettingsTheme/res/color-v36/settingslib_switch_track_outline_color.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_text_color_primary.xml b/packages/SettingsLib/SettingsTheme/res/color-v36/settingslib_text_color_primary.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_text_color_primary.xml
rename to packages/SettingsLib/SettingsTheme/res/color-v36/settingslib_text_color_primary.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_text_color_secondary.xml b/packages/SettingsLib/SettingsTheme/res/color-v36/settingslib_text_color_secondary.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_text_color_secondary.xml
rename to packages/SettingsLib/SettingsTheme/res/color-v36/settingslib_text_color_secondary.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_arrow_drop_down.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_arrow_drop_down.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_arrow_drop_down.xml
rename to packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_arrow_drop_down.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_list_divider.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_list_divider.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_list_divider.xml
rename to packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_list_divider.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_progress_horizontal.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_progress_horizontal.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_progress_horizontal.xml
rename to packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_progress_horizontal.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background.xml
rename to packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom.xml
rename to packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_highlighted.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom_highlighted.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_highlighted.xml
rename to packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom_highlighted.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom_selected.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_selected.xml
rename to packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_bottom_selected.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center.xml
rename to packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_highlighted.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center_highlighted.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_highlighted.xml
rename to packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center_highlighted.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center_selected.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_selected.xml
rename to packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_center_selected.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_highlighted.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_highlighted.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_highlighted.xml
rename to packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_highlighted.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_selected.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_selected.xml
rename to packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_selected.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top.xml
rename to packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_highlighted.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top_highlighted.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_highlighted.xml
rename to packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top_highlighted.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top_selected.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_selected.xml
rename to packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_round_background_top_selected.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_spinner_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_spinner_background.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_spinner_background.xml
rename to packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_spinner_background.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_spinner_dropdown_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_spinner_dropdown_background.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_spinner_dropdown_background.xml
rename to packages/SettingsLib/SettingsTheme/res/drawable-v36/settingslib_spinner_dropdown_background.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference.xml b/packages/SettingsLib/SettingsTheme/res/layout-v36/settingslib_expressive_preference.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference.xml
rename to packages/SettingsLib/SettingsTheme/res/layout-v36/settingslib_expressive_preference.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml b/packages/SettingsLib/SettingsTheme/res/layout-v36/settingslib_expressive_preference_icon_frame.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml
rename to packages/SettingsLib/SettingsTheme/res/layout-v36/settingslib_expressive_preference_icon_frame.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_switch.xml b/packages/SettingsLib/SettingsTheme/res/layout-v36/settingslib_expressive_preference_switch.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_switch.xml
rename to packages/SettingsLib/SettingsTheme/res/layout-v36/settingslib_expressive_preference_switch.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_two_target_divider.xml b/packages/SettingsLib/SettingsTheme/res/layout-v36/settingslib_expressive_two_target_divider.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_two_target_divider.xml
rename to packages/SettingsLib/SettingsTheme/res/layout-v36/settingslib_expressive_two_target_divider.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/values-night-v35/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-night-v36/colors.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/values-night-v35/colors.xml
rename to packages/SettingsLib/SettingsTheme/res/values-night-v36/colors.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v36/colors.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/values-v35/colors.xml
rename to packages/SettingsLib/SettingsTheme/res/values-v36/colors.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values-v36/dimens.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/values-v35/dimens.xml
rename to packages/SettingsLib/SettingsTheme/res/values-v36/dimens.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/styles_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v36/styles_expressive.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/values-v35/styles_expressive.xml
rename to packages/SettingsLib/SettingsTheme/res/values-v36/styles_expressive.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/styles_preference_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v36/styles_preference_expressive.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/values-v35/styles_preference_expressive.xml
rename to packages/SettingsLib/SettingsTheme/res/values-v36/styles_preference_expressive.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v36/themes.xml
similarity index 92%
rename from packages/SettingsLib/SettingsTheme/res/values-v35/themes.xml
rename to packages/SettingsLib/SettingsTheme/res/values-v36/themes.xml
index dc5c9b2..1c45ff6 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v35/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v36/themes.xml
@@ -16,9 +16,9 @@
-->
<resources>
- <style name="Theme.SettingsBase_v35" parent="Theme.SettingsBase_v33" >
+ <style name="Theme.SettingsBase_v36" 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>
@@ -27,5 +27,5 @@
<item name="android:clipChildren">false</item>
</style>
- <style name="Theme.SettingsBase" parent="Theme.SettingsBase_v35" />
+ <style name="Theme.SettingsBase" parent="Theme.SettingsBase_v36" />
</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/themes_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v36/themes_expressive.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/values-v35/themes_expressive.xml
rename to packages/SettingsLib/SettingsTheme/res/values-v36/themes_expressive.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/themes_preference_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v36/themes_preference_expressive.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/values-v35/themes_preference_expressive.xml
rename to packages/SettingsLib/SettingsTheme/res/values-v36/themes_preference_expressive.xml
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
index 6a06320..a04fce7 100644
--- a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
@@ -47,7 +47,7 @@
private val mHandler = Handler(Looper.getMainLooper())
- private val syncRunnable = Runnable { updatePreferences() }
+ private val syncRunnable = Runnable { updatePreferencesList() }
init {
val context = preferenceGroup.context
@@ -64,7 +64,7 @@
true, /* resolveRefs */
)
mLegacyBackgroundRes = outValue.resourceId
- updatePreferences()
+ updatePreferencesList()
}
@SuppressLint("RestrictedApi")
@@ -82,7 +82,7 @@
updateBackground(holder, position)
}
- private fun updatePreferences() {
+ private fun updatePreferencesList() {
val oldList = ArrayList(mRoundCornerMappingList)
mRoundCornerMappingList = ArrayList()
mappingPreferenceGroup(mRoundCornerMappingList, mPreferenceGroup)
diff --git a/packages/SettingsLib/TwoTargetPreference/res/layout-v35/settingslib_expressive_preference_two_target.xml b/packages/SettingsLib/TwoTargetPreference/res/layout-v36/settingslib_expressive_preference_two_target.xml
similarity index 100%
rename from packages/SettingsLib/TwoTargetPreference/res/layout-v35/settingslib_expressive_preference_two_target.xml
rename to packages/SettingsLib/TwoTargetPreference/res/layout-v36/settingslib_expressive_preference_two_target.xml
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 522a436..31948e4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -23,6 +23,7 @@
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.hardware.input.InputManager;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
@@ -34,6 +35,7 @@
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
+import android.view.InputDevice;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
@@ -1054,7 +1056,7 @@
/** Get develop option value for audio sharing preview. */
@WorkerThread
- private static boolean getAudioSharingPreviewValue(@Nullable ContentResolver contentResolver) {
+ public static boolean getAudioSharingPreviewValue(@Nullable ContentResolver contentResolver) {
if (contentResolver == null) return false;
return Settings.Global.getInt(
contentResolver,
@@ -1193,4 +1195,53 @@
}
device.setMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS, fastPairCustomizedMeta.getBytes());
}
+
+ /**
+ * Returns the {@link InputDevice} of the given bluetooth address if the device is a input
+ * device.
+ *
+ * @param address The address of the bluetooth device
+ * @return The {@link InputDevice} of the given address if applicable
+ */
+ @Nullable
+ public static InputDevice getInputDevice(Context context, String address) {
+ InputManager im = context.getSystemService(InputManager.class);
+
+ if (im != null) {
+ for (int deviceId : im.getInputDeviceIds()) {
+ String btAddress = im.getInputDeviceBluetoothAddress(deviceId);
+
+ if (btAddress != null && btAddress.equals(address)) {
+ return im.getInputDevice(deviceId);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Identifies whether a device is a stylus using the associated {@link InputDevice} or
+ * {@link CachedBluetoothDevice}.
+ * InputDevices are only available when the device is USI or Bluetooth-connected, whereas
+ * CachedBluetoothDevices are available for Bluetooth devices when connected or paired,
+ * so to handle all cases, both are needed.
+ *
+ * @param inputDevice The associated input device of the stylus
+ * @param cachedBluetoothDevice The associated bluetooth device of the stylus
+ */
+ public static boolean isDeviceStylus(@Nullable InputDevice inputDevice,
+ @Nullable CachedBluetoothDevice cachedBluetoothDevice) {
+ if (inputDevice != null && inputDevice.supportsSource(InputDevice.SOURCE_STYLUS)) {
+ return true;
+ }
+
+ if (cachedBluetoothDevice != null) {
+ BluetoothDevice bluetoothDevice = cachedBluetoothDevice.getDevice();
+ String deviceType = BluetoothUtils.getStringMetaData(bluetoothDevice,
+ BluetoothDevice.METADATA_DEVICE_TYPE);
+ return TextUtils.equals(deviceType, BluetoothDevice.DEVICE_TYPE_STYLUS);
+ }
+
+ return false;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index 3017d798..f18a2da 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -63,6 +63,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.charset.StandardCharsets;
+import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -70,7 +71,6 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -108,6 +108,10 @@
private static final String SYSUI_PKG = "com.android.systemui";
private static final String TAG = "LocalBluetoothLeBroadcast";
private static final boolean DEBUG = BluetoothUtils.D;
+ private static final String VALID_PASSWORD_CHARACTERS =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+[]{}|;:,"
+ + ".<>?/";
+ private static final int PASSWORD_LENGTH = 16;
static final String NAME = "LE_AUDIO_BROADCAST";
private static final String UNDERLINE = "_";
@@ -145,8 +149,8 @@
private ContentResolver mContentResolver;
private ContentObserver mSettingsObserver;
// Cached broadcast callbacks being register before service is connected.
- private Map<BluetoothLeBroadcast.Callback, Executor> mCachedBroadcastCallbackExecutorMap =
- new ConcurrentHashMap<>();
+ private ConcurrentHashMap<BluetoothLeBroadcast.Callback, Executor>
+ mCachedBroadcastCallbackExecutorMap = new ConcurrentHashMap<>();
private final ServiceListener mServiceListener =
new ServiceListener() {
@@ -876,7 +880,7 @@
@NonNull @CallbackExecutor Executor executor,
@NonNull BluetoothLeBroadcast.Callback callback) {
if (mServiceBroadcast == null) {
- Log.d(TAG, "registerServiceCallBack failed, the BluetoothLeBroadcast is null.");
+ Log.d(TAG, "registerServiceCallBack failed, proxy not attached.");
mCachedBroadcastCallbackExecutorMap.putIfAbsent(callback, executor);
return;
}
@@ -898,10 +902,7 @@
@NonNull @CallbackExecutor Executor executor,
@NonNull BluetoothLeBroadcastAssistant.Callback callback) {
if (mServiceBroadcastAssistant == null) {
- Log.d(
- TAG,
- "registerBroadcastAssistantCallback failed, "
- + "the BluetoothLeBroadcastAssistant is null.");
+ Log.d(TAG, "registerBroadcastAssistantCallback failed, proxy not attached.");
return;
}
@@ -916,7 +917,7 @@
public void unregisterServiceCallBack(@NonNull BluetoothLeBroadcast.Callback callback) {
mCachedBroadcastCallbackExecutorMap.remove(callback);
if (mServiceBroadcast == null) {
- Log.d(TAG, "unregisterServiceCallBack failed, the BluetoothLeBroadcast is null.");
+ Log.d(TAG, "unregisterServiceCallBack failed, proxy not attached.");
return;
}
@@ -935,10 +936,7 @@
private void unregisterBroadcastAssistantCallback(
@NonNull BluetoothLeBroadcastAssistant.Callback callback) {
if (mServiceBroadcastAssistant == null) {
- Log.d(
- TAG,
- "unregisterBroadcastAssistantCallback, "
- + "the BluetoothLeBroadcastAssistant is null.");
+ Log.d(TAG, "unregisterBroadcastAssistantCallback, proxy not attched.");
return;
}
@@ -1088,11 +1086,16 @@
mBroadcastId = UNKNOWN_VALUE_PLACEHOLDER;
}
- private String generateRandomPassword() {
- String randomUUID = UUID.randomUUID().toString();
- // first 16 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
- return randomUUID.substring(0, 8) + randomUUID.substring(9, 13) + randomUUID.substring(14,
- 18);
+ private static String generateRandomPassword() {
+ SecureRandom random = new SecureRandom();
+ StringBuilder stringBuilder = new StringBuilder(PASSWORD_LENGTH);
+
+ for (int i = 0; i < PASSWORD_LENGTH; i++) {
+ int randomIndex = random.nextInt(VALID_PASSWORD_CHARACTERS.length());
+ stringBuilder.append(VALID_PASSWORD_CHARACTERS.charAt(randomIndex));
+ }
+
+ return stringBuilder.toString();
}
private void registerContentObserver() {
@@ -1131,7 +1134,9 @@
/** Update fallback active device if needed. */
public void updateFallbackActiveDeviceIfNeeded() {
- if (Flags.disableAudioSharingAutoPickFallbackInUi()) {
+ if (Flags.disableAudioSharingAutoPickFallbackInUi() || (mContext != null
+ && Flags.audioSharingDeveloperOption()
+ && BluetoothUtils.getAudioSharingPreviewValue(mContext.getContentResolver()))) {
Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded, disable flag is on");
return;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/connectivity/OWNERS b/packages/SettingsLib/src/com/android/settingslib/connectivity/OWNERS
index 9f15c1b..b676d8d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/connectivity/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/connectivity/OWNERS
@@ -1,6 +1,5 @@
# Default reviewers for this and subdirectories.
andychou@google.com
-arcwang@google.com
changbetty@google.com
qal@google.com
wengsu@google.com
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeControllerTest.java
index abc1d226..e4381ae 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeControllerTest.java
@@ -26,6 +26,7 @@
import android.bluetooth.AudioInputControl;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
@@ -67,6 +68,9 @@
@Before
public void setUp() {
when(mProfileManager.getVolumeControlProfile()).thenReturn(mVolumeControlProfile);
+ when(mVolumeControlProfile.isProfileReady()).thenReturn(true);
+ when(mVolumeControlProfile.getConnectionStatus(mDevice)).thenReturn(
+ BluetoothProfile.STATE_CONNECTED);
when(mDevice.getAddress()).thenReturn(TEST_ADDRESS);
when(mDevice.isConnected()).thenReturn(true);
mVolumeController = new AmbientVolumeController(mProfileManager, mCallback);
@@ -74,8 +78,6 @@
@Test
public void onServiceConnected_notifyCallback() {
- when(mVolumeControlProfile.isProfileReady()).thenReturn(true);
-
mVolumeController.onServiceConnected();
verify(mCallback).onVolumeControlServiceConnected();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index ebe6128..0325c0e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -15,7 +15,9 @@
*/
package com.android.settingslib.bluetooth;
+import static com.android.settingslib.bluetooth.BluetoothUtils.getInputDevice;
import static com.android.settingslib.bluetooth.BluetoothUtils.isAvailableAudioSharingMediaBluetoothDevice;
+import static com.android.settingslib.bluetooth.BluetoothUtils.isDeviceStylus;
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.UNKNOWN_VALUE_PLACEHOLDER;
import static com.android.settingslib.flags.Flags.FLAG_ENABLE_DETERMINING_ADVANCED_DETAILS_HEADER_WITH_METADATA;
@@ -42,6 +44,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
+import android.hardware.input.InputManager;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
@@ -51,6 +54,7 @@
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.util.Pair;
+import android.view.InputDevice;
import com.android.internal.R;
import com.android.settingslib.flags.Flags;
@@ -97,14 +101,18 @@
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
@Mock private CachedBluetoothDeviceManager mDeviceManager;
@Mock private BluetoothLeBroadcastReceiveState mLeBroadcastReceiveState;
+ @Mock
+ private InputManager mInputManager;
private Context mContext;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+ private final InputDevice mInputDevice = mock(InputDevice.class);
private static final String STRING_METADATA = "string_metadata";
private static final String LE_AUDIO_SHARING_METADATA = "le_audio_sharing";
private static final String BOOL_METADATA = "true";
private static final String INT_METADATA = "25";
private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25;
+ private static final int TEST_DEVICE_ID = 123;
private static final String KEY_HEARABLE_CONTROL_SLICE = "HEARABLE_CONTROL_SLICE_WITH_WIDTH";
private static final String CONTROL_METADATA =
"<HEARABLE_CONTROL_SLICE_WITH_WIDTH>"
@@ -115,6 +123,7 @@
private static final String FAKE_TEMP_BOND_METADATA = "<TEMP_BOND_TYPE>fake</TEMP_BOND_TYPE>";
private static final String TEST_EXCLUSIVE_MANAGER_PACKAGE = "com.test.manager";
private static final String TEST_EXCLUSIVE_MANAGER_COMPONENT = "com.test.manager/.component";
+ private static final String TEST_ADDRESS = "11:22:33:44:55:66";
private static final int TEST_BROADCAST_ID = 25;
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -134,6 +143,10 @@
when(mA2dpProfile.getProfileId()).thenReturn(BluetoothProfile.A2DP);
when(mLeAudioProfile.getProfileId()).thenReturn(BluetoothProfile.LE_AUDIO);
when(mHearingAid.getProfileId()).thenReturn(BluetoothProfile.HEARING_AID);
+ when(mContext.getSystemService(InputManager.class)).thenReturn(mInputManager);
+ when(mInputManager.getInputDeviceIds()).thenReturn(new int[]{TEST_DEVICE_ID});
+ when(mInputManager.getInputDeviceBluetoothAddress(TEST_DEVICE_ID)).thenReturn(TEST_ADDRESS);
+ when(mInputManager.getInputDevice(TEST_DEVICE_ID)).thenReturn(mInputDevice);
}
@Test
@@ -1097,9 +1110,8 @@
@Test
public void getAudioDeviceAttributesForSpatialAudio_bleHeadset() {
- String address = "11:22:33:44:55:66";
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
- when(mCachedBluetoothDevice.getAddress()).thenReturn(address);
+ when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_ADDRESS);
when(mCachedBluetoothDevice.getProfiles()).thenReturn(List.of(mLeAudioProfile));
when(mLeAudioProfile.isEnabled(mBluetoothDevice)).thenReturn(true);
@@ -1112,14 +1124,13 @@
new AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT,
AudioDeviceInfo.TYPE_BLE_HEADSET,
- address));
+ TEST_ADDRESS));
}
@Test
public void getAudioDeviceAttributesForSpatialAudio_bleSpeaker() {
- String address = "11:22:33:44:55:66";
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
- when(mCachedBluetoothDevice.getAddress()).thenReturn(address);
+ when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_ADDRESS);
when(mCachedBluetoothDevice.getProfiles()).thenReturn(List.of(mLeAudioProfile));
when(mLeAudioProfile.isEnabled(mBluetoothDevice)).thenReturn(true);
@@ -1132,14 +1143,14 @@
new AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT,
AudioDeviceInfo.TYPE_BLE_SPEAKER,
- address));
+ TEST_ADDRESS));
}
@Test
public void getAudioDeviceAttributesForSpatialAudio_a2dp() {
- String address = "11:22:33:44:55:66";
+
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
- when(mCachedBluetoothDevice.getAddress()).thenReturn(address);
+ when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_ADDRESS);
when(mCachedBluetoothDevice.getProfiles()).thenReturn(List.of(mA2dpProfile));
when(mA2dpProfile.isEnabled(mBluetoothDevice)).thenReturn(true);
@@ -1152,14 +1163,13 @@
new AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT,
AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
- address));
+ TEST_ADDRESS));
}
@Test
public void getAudioDeviceAttributesForSpatialAudio_hearingAid() {
- String address = "11:22:33:44:55:66";
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
- when(mCachedBluetoothDevice.getAddress()).thenReturn(address);
+ when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_ADDRESS);
when(mCachedBluetoothDevice.getProfiles()).thenReturn(List.of(mHearingAid));
when(mHearingAid.isEnabled(mBluetoothDevice)).thenReturn(true);
@@ -1172,7 +1182,7 @@
new AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT,
AudioDeviceInfo.TYPE_HEARING_AID,
- address));
+ TEST_ADDRESS));
}
@Test
@@ -1375,4 +1385,54 @@
verify(mBluetoothDevice).setMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS,
TEMP_BOND_METADATA.getBytes());
}
+
+ @Test
+ public void getInputDevice_addressNotMatched_returnsNull() {
+ assertThat(getInputDevice(mContext, "123")).isNull();
+ }
+
+ @Test
+ public void getInputDevice_isInputDevice_returnsInputDevice() {
+ assertThat(getInputDevice(mContext, TEST_ADDRESS)).isEqualTo(mInputDevice);
+ }
+
+ @Test
+ public void isDeviceStylus_noDevices_false() {
+ assertThat(isDeviceStylus(null, null)).isFalse();
+ }
+
+ @Test
+ public void isDeviceStylus_nonStylusInputDevice_false() {
+ InputDevice inputDevice = new InputDevice.Builder()
+ .setSources(InputDevice.SOURCE_DPAD)
+ .build();
+
+ assertThat(isDeviceStylus(inputDevice, null)).isFalse();
+ }
+
+ @Test
+ public void isDeviceStylus_stylusInputDevice_true() {
+ InputDevice inputDevice = new InputDevice.Builder()
+ .setSources(InputDevice.SOURCE_STYLUS)
+ .build();
+
+ assertThat(isDeviceStylus(inputDevice, null)).isTrue();
+ }
+
+ @Test
+ public void isDeviceStylus_nonStylusBluetoothDevice_false() {
+ when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
+ BluetoothDevice.DEVICE_TYPE_WATCH.getBytes());
+
+ assertThat(isDeviceStylus(null, mCachedBluetoothDevice)).isFalse();
+ }
+
+ @Test
+ public void isDeviceStylus_stylusBluetoothDevice_true() {
+ when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
+ BluetoothDevice.DEVICE_TYPE_STYLUS.getBytes());
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+
+ assertThat(isDeviceStylus(null, mCachedBluetoothDevice)).isTrue();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 69e99c6..94199df 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -662,10 +662,10 @@
@Test
@RequiresFlagsEnabled(
com.android.settingslib.flags.Flags.FLAG_HEARING_DEVICE_SET_CONNECTION_STATUS_REPORT)
- public void onDeviceUnpaired_hearingDevice_callReportConnectionStatus() {
+ public void onDeviceUnpaired_containsHearingAidInfo_callReportConnectionStatus() {
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
- when(mCachedDevice1.getProfiles()).thenReturn(
- ImmutableList.of(mHapClientProfile, mHearingAidProfile));
+ mCachedDevice1.setHearingAidInfo(
+ new HearingAidInfo.Builder().setHiSyncId(HISYNCID1).build());
mCachedDeviceManager.onDeviceUnpaired(mCachedDevice1);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 85617ba..70c042c 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -811,7 +811,7 @@
Settings.Secure.V_TO_U_RESTORE_ALLOWLIST,
Settings.Secure.V_TO_U_RESTORE_DENYLIST,
Settings.Secure.REDACT_OTP_NOTIFICATION_WHILE_CONNECTED_TO_WIFI,
- Settings.Secure.REDACT_OTP_NOTIFICATION_IMMEDIATELY);
+ Settings.Secure.OTP_NOTIFICATION_REDACTION_LOCK_TIME);
@Test
public void systemSettingsBackedUpOrDenied() {
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index eb5b22f..f2c76ba 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1939,6 +1939,16 @@
}
flag {
+ name: "notification_appear_nonlinear"
+ namespace: "systemui"
+ description: "Fix linear usage of notification appear"
+ bug: "397658189"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "disable_shade_trackpad_two_finger_swipe"
namespace: "systemui"
description: "Disables expansion of the shade via two finger swipe on a trackpad"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
index f8bcb81..bc75b1d 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
@@ -22,19 +22,8 @@
import android.util.LruCache
import android.util.MathUtils
import androidx.annotation.VisibleForTesting
-import java.lang.Float.max
-import java.lang.Float.min
import kotlin.math.roundToInt
-private const val TAG_WGHT = "wght"
-private const val TAG_ITAL = "ital"
-
-private const val FONT_WEIGHT_DEFAULT_VALUE = 400f
-private const val FONT_ITALIC_MAX = 1f
-private const val FONT_ITALIC_MIN = 0f
-private const val FONT_ITALIC_ANIMATION_STEP = 0.1f
-private const val FONT_ITALIC_DEFAULT_VALUE = 0f
-
/** Caches for font interpolation */
interface FontCache {
val animationFrameCount: Int
@@ -91,11 +80,8 @@
class FontInterpolator(val fontCache: FontCache = FontCacheImpl()) {
/** Linear interpolate the font variation settings. */
fun lerp(start: Font, end: Font, progress: Float, linearProgress: Float): Font {
- if (progress == 0f) {
- return start
- } else if (progress == 1f) {
- return end
- }
+ if (progress <= 0f) return start
+ if (progress >= 1f) return end
val startAxes = start.axes ?: EMPTY_AXES
val endAxes = end.axes ?: EMPTY_AXES
@@ -110,7 +96,7 @@
InterpKey(start, end, (linearProgress * fontCache.animationFrameCount).roundToInt())
fontCache.get(iKey)?.let {
if (DEBUG) {
- Log.d(LOG_TAG, "[$progress] Interp. cache hit for $iKey")
+ Log.d(LOG_TAG, "[$progress, $linearProgress] Interp. cache hit for $iKey")
}
return it
}
@@ -121,37 +107,16 @@
// and also pre-fill the missing axes value with default value from 'fvar' table.
val newAxes =
lerp(startAxes, endAxes) { tag, startValue, endValue ->
- when (tag) {
- TAG_WGHT ->
- MathUtils.lerp(
- startValue ?: FONT_WEIGHT_DEFAULT_VALUE,
- endValue ?: FONT_WEIGHT_DEFAULT_VALUE,
- progress,
- )
- TAG_ITAL ->
- adjustItalic(
- MathUtils.lerp(
- startValue ?: FONT_ITALIC_DEFAULT_VALUE,
- endValue ?: FONT_ITALIC_DEFAULT_VALUE,
- progress,
- )
- )
- else -> {
- require(startValue != null && endValue != null) {
- "Unable to interpolate due to unknown default axes value : $tag"
- }
- MathUtils.lerp(startValue, endValue, progress)
- }
- }
+ MathUtils.lerp(startValue, endValue, progress)
}
// Check if we already make font for this axes. This is typically happens if the animation
- // happens backward.
+ // happens backward and is being linearly interpolated.
val vKey = VarFontKey(start, newAxes)
fontCache.get(vKey)?.let {
fontCache.put(iKey, it)
if (DEBUG) {
- Log.d(LOG_TAG, "[$progress] Axis cache hit for $vKey")
+ Log.d(LOG_TAG, "[$progress, $linearProgress] Axis cache hit for $vKey")
}
return it
}
@@ -164,14 +129,14 @@
fontCache.put(vKey, newFont)
// Cache misses are likely to create memory leaks, so this is logged at error level.
- Log.e(LOG_TAG, "[$progress] Cache MISS for $iKey / $vKey")
+ Log.e(LOG_TAG, "[$progress, $linearProgress] Cache MISS for $iKey / $vKey")
return newFont
}
private fun lerp(
start: Array<FontVariationAxis>,
end: Array<FontVariationAxis>,
- filter: (tag: String, left: Float?, right: Float?) -> Float,
+ filter: (tag: String, left: Float, right: Float) -> Float,
): List<FontVariationAxis> {
// Safe to modify result of Font#getAxes since it returns cloned object.
start.sortBy { axis -> axis.tag }
@@ -191,39 +156,37 @@
else -> tagA.compareTo(tagB)
}
- val axis =
+ val tag =
when {
- comp == 0 -> {
- val v = filter(tagA!!, start[i++].styleValue, end[j++].styleValue)
- FontVariationAxis(tagA, v)
- }
- comp < 0 -> {
- val v = filter(tagA!!, start[i++].styleValue, null)
- FontVariationAxis(tagA, v)
- }
- else -> { // comp > 0
- val v = filter(tagB!!, null, end[j++].styleValue)
- FontVariationAxis(tagB, v)
- }
+ comp == 0 -> tagA!!
+ comp < 0 -> tagA!!
+ else -> tagB!!
}
- result.add(axis)
+ val axisDefinition = GSFAxes.getAxis(tag)
+ require(comp == 0 || axisDefinition != null) {
+ "Unable to interpolate due to unknown default axes value: $tag"
+ }
+
+ val axisValue =
+ when {
+ comp == 0 -> filter(tag, start[i++].styleValue, end[j++].styleValue)
+ comp < 0 -> filter(tag, start[i++].styleValue, axisDefinition!!.defaultValue)
+ else -> filter(tag, axisDefinition!!.defaultValue, end[j++].styleValue)
+ }
+
+ // Round axis value to valid intermediate steps. This improves the cache hit rate.
+ val step = axisDefinition?.animationStep ?: DEFAULT_ANIMATION_STEP
+ result.add(FontVariationAxis(tag, (axisValue / step).roundToInt() * step))
}
return result
}
- // For the performance reasons, we animate italic with FONT_ITALIC_ANIMATION_STEP. This helps
- // Cache hit ratio in the Skia glyph cache.
- private fun adjustItalic(value: Float) =
- coerceInWithStep(value, FONT_ITALIC_MIN, FONT_ITALIC_MAX, FONT_ITALIC_ANIMATION_STEP)
-
- private fun coerceInWithStep(v: Float, min: Float, max: Float, step: Float) =
- (v.coerceIn(min, max) / step).toInt() * step
-
companion object {
private const val LOG_TAG = "FontInterpolator"
private val DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG)
private val EMPTY_AXES = arrayOf<FontVariationAxis>()
+ private const val DEFAULT_ANIMATION_STEP = 1f
// Returns true if given two font instance can be interpolated.
fun canInterpolate(start: Font, end: Font) =
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt
index 9545bda..9a74687 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt
@@ -1,12 +1,20 @@
-package com.android.systemui.animation
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
-object GSFAxes {
- const val WEIGHT = "wght"
- const val WIDTH = "wdth"
- const val SLANT = "slnt"
- const val ROUND = "ROND"
- const val OPTICAL_SIZE = "opsz"
-}
+package com.android.systemui.animation
class FontVariationUtils {
private var mWeight = -1
@@ -46,20 +54,20 @@
}
var resultString = ""
if (mWeight >= 0) {
- resultString += "'${GSFAxes.WEIGHT}' $mWeight"
+ resultString += "'${GSFAxes.WEIGHT.tag}' $mWeight"
}
if (mWidth >= 0) {
resultString +=
- (if (resultString.isBlank()) "" else ", ") + "'${GSFAxes.WIDTH}' $mWidth"
+ (if (resultString.isBlank()) "" else ", ") + "'${GSFAxes.WIDTH.tag}' $mWidth"
}
if (mOpticalSize >= 0) {
resultString +=
(if (resultString.isBlank()) "" else ", ") +
- "'${GSFAxes.OPTICAL_SIZE}' $mOpticalSize"
+ "'${GSFAxes.OPTICAL_SIZE.tag}' $mOpticalSize"
}
if (mRoundness >= 0) {
resultString +=
- (if (resultString.isBlank()) "" else ", ") + "'${GSFAxes.ROUND}' $mRoundness"
+ (if (resultString.isBlank()) "" else ", ") + "'${GSFAxes.ROUND.tag}' $mRoundness"
}
return if (isUpdated) resultString else ""
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GSFAxes.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GSFAxes.kt
new file mode 100644
index 0000000..f4e0361
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GSFAxes.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.animation
+
+data class AxisDefinition(
+ val tag: String,
+ val minValue: Float,
+ val defaultValue: Float,
+ val maxValue: Float,
+ val animationStep: Float,
+)
+
+object GSFAxes {
+ val WEIGHT =
+ AxisDefinition(
+ tag = "wght",
+ minValue = 1f,
+ defaultValue = 400f,
+ maxValue = 1000f,
+ animationStep = 10f,
+ )
+
+ val WIDTH =
+ AxisDefinition(
+ tag = "wdth",
+ minValue = 25f,
+ defaultValue = 100f,
+ maxValue = 151f,
+ animationStep = 1f,
+ )
+
+ val SLANT =
+ AxisDefinition(
+ tag = "slnt",
+ minValue = 0f,
+ defaultValue = 0f,
+ maxValue = -10f,
+ animationStep = 0.1f,
+ )
+
+ val ROUND =
+ AxisDefinition(
+ tag = "ROND",
+ minValue = 0f,
+ defaultValue = 0f,
+ maxValue = 100f,
+ animationStep = 1f,
+ )
+
+ val GRADE =
+ AxisDefinition(
+ tag = "GRAD",
+ minValue = 0f,
+ defaultValue = 0f,
+ maxValue = 100f,
+ animationStep = 1f,
+ )
+
+ val OPTICAL_SIZE =
+ AxisDefinition(
+ tag = "opsz",
+ minValue = 6f,
+ defaultValue = 18f,
+ maxValue = 144f,
+ animationStep = 1f,
+ )
+
+ // Not a GSF Axis, but present for FontInterpolator compatibility
+ val ITALIC =
+ AxisDefinition(
+ tag = "ITAL",
+ minValue = 0f,
+ defaultValue = 0f,
+ maxValue = 1f,
+ animationStep = 0.1f,
+ )
+
+ private val AXIS_MAP =
+ listOf(WEIGHT, WIDTH, SLANT, ROUND, GRADE, OPTICAL_SIZE, ITALIC)
+ .map { def -> def.tag.toLowerCase() to def }
+ .toMap()
+
+ fun getAxis(axis: String): AxisDefinition? = AXIS_MAP[axis.toLowerCase()]
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
index 297995b..7cd6c6b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
@@ -124,7 +124,6 @@
SmallClock(
burnInParams = burnIn.parameters,
onTopChanged = burnIn.onSmallClockTopChanged,
- modifier = Modifier.fillMaxWidth(),
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
index cdb1e2e..7e7b629 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
@@ -54,6 +54,7 @@
import com.android.compose.animation.scene.LowestZIndexContentPicker
import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.systemui.res.R
+import androidx.compose.ui.unit.Dp
/** Renders a lightweight shade UI container, as an overlay. */
@Composable
@@ -202,10 +203,15 @@
}
object Dimensions {
- val PanelCornerRadius = 46.dp
+ val PanelCornerRadius: Dp
+ @Composable
+ @ReadOnlyComposable get() =
+ dimensionResource(R.dimen.overlay_shade_panel_shape_radius)
}
object Shapes {
- val RoundedCornerPanel = RoundedCornerShape(Dimensions.PanelCornerRadius)
+ val RoundedCornerPanel: RoundedCornerShape
+ @Composable
+ @ReadOnlyComposable get() = RoundedCornerShape(Dimensions.PanelCornerRadius)
}
}
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/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
index 004d1aa..ac1c5a8 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
@@ -130,39 +130,25 @@
private val FONT_AXES =
listOf(
- ClockFontAxis(
- key = GSFAxes.WEIGHT,
+ GSFAxes.WEIGHT.toClockAxis(
type = AxisType.Float,
- minValue = 25f,
currentValue = 400f,
- maxValue = 1000f,
name = "Weight",
description = "Glyph Weight",
),
- ClockFontAxis(
- key = GSFAxes.WIDTH,
+ GSFAxes.WIDTH.toClockAxis(
type = AxisType.Float,
- minValue = 25f,
currentValue = 85f,
- maxValue = 151f,
name = "Width",
description = "Glyph Width",
),
- ClockFontAxis(
- key = GSFAxes.ROUND,
+ GSFAxes.ROUND.toClockAxis(
type = AxisType.Boolean,
- minValue = 0f,
- currentValue = 0f,
- maxValue = 100f,
name = "Round",
description = "Glyph Roundness",
),
- ClockFontAxis(
- key = GSFAxes.SLANT,
+ GSFAxes.SLANT.toClockAxis(
type = AxisType.Boolean,
- minValue = 0f,
- currentValue = 0f,
- maxValue = -10f,
name = "Slant",
description = "Glyph Slant",
),
@@ -170,10 +156,10 @@
private val LEGACY_FLEX_SETTINGS =
listOf(
- ClockFontAxisSetting(GSFAxes.WEIGHT, 600f),
- ClockFontAxisSetting(GSFAxes.WIDTH, 100f),
- ClockFontAxisSetting(GSFAxes.ROUND, 100f),
- ClockFontAxisSetting(GSFAxes.SLANT, 0f),
+ GSFAxes.WEIGHT.toClockAxisSetting(600f),
+ GSFAxes.WIDTH.toClockAxisSetting(100f),
+ GSFAxes.ROUND.toClockAxisSetting(100f),
+ GSFAxes.SLANT.toClockAxisSetting(0f),
)
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
index b2dbd65..b4c2f5d 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
@@ -132,7 +132,7 @@
if (!isLargeClock) {
axes =
axes.map { axis ->
- if (axis.key == GSFAxes.WIDTH && axis.value > SMALL_CLOCK_MAX_WDTH) {
+ if (axis.key == GSFAxes.WIDTH.tag && axis.value > SMALL_CLOCK_MAX_WDTH) {
axis.copy(value = SMALL_CLOCK_MAX_WDTH)
} else {
axis
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt
new file mode 100644
index 0000000..212b1e2
--- /dev/null
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.clocks
+
+import com.android.systemui.animation.AxisDefinition
+import com.android.systemui.plugins.clocks.AxisType
+import com.android.systemui.plugins.clocks.ClockFontAxis
+import com.android.systemui.plugins.clocks.ClockFontAxisSetting
+
+fun AxisDefinition.toClockAxis(
+ type: AxisType,
+ currentValue: Float? = null,
+ name: String,
+ description: String,
+): ClockFontAxis {
+ return ClockFontAxis(
+ key = this.tag,
+ type = type,
+ maxValue = this.maxValue,
+ minValue = this.minValue,
+ currentValue = currentValue ?: this.defaultValue,
+ name = name,
+ description = description,
+ )
+}
+
+fun AxisDefinition.toClockAxisSetting(value: Float? = null): ClockFontAxisSetting {
+ return ClockFontAxisSetting(this.tag, value ?: this.defaultValue)
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
index 54be4d8..b7ce20e 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
@@ -49,6 +49,7 @@
import com.android.systemui.shared.clocks.DimensionParser
import com.android.systemui.shared.clocks.FLEX_CLOCK_ID
import com.android.systemui.shared.clocks.FontTextStyle
+import com.android.systemui.shared.clocks.toClockAxisSetting
import java.lang.Thread
import kotlin.math.max
import kotlin.math.min
@@ -585,25 +586,25 @@
val FIDGET_INTERPOLATOR = PathInterpolator(0.26873f, 0f, 0.45042f, 1f)
val FIDGET_DISTS =
mapOf(
- GSFAxes.WEIGHT to Pair(200f, 500f),
- GSFAxes.WIDTH to Pair(30f, 75f),
- GSFAxes.ROUND to Pair(0f, 50f),
- GSFAxes.SLANT to Pair(0f, -5f),
+ GSFAxes.WEIGHT.tag to Pair(200f, 500f),
+ GSFAxes.WIDTH.tag to Pair(30f, 75f),
+ GSFAxes.ROUND.tag to Pair(0f, 50f),
+ GSFAxes.SLANT.tag to Pair(0f, -5f),
)
val AOD_COLOR = Color.WHITE
- val LS_WEIGHT_AXIS = ClockFontAxisSetting(GSFAxes.WEIGHT, 400f)
- val AOD_WEIGHT_AXIS = ClockFontAxisSetting(GSFAxes.WEIGHT, 200f)
- val WIDTH_AXIS = ClockFontAxisSetting(GSFAxes.WIDTH, 85f)
- val ROUND_AXIS = ClockFontAxisSetting(GSFAxes.ROUND, 0f)
- val SLANT_AXIS = ClockFontAxisSetting(GSFAxes.SLANT, 0f)
+ val LS_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(400f)
+ val AOD_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(200f)
+ val WIDTH_AXIS = GSFAxes.WIDTH.toClockAxisSetting(85f)
+ val ROUND_AXIS = GSFAxes.ROUND.toClockAxisSetting(0f)
+ val SLANT_AXIS = GSFAxes.SLANT.toClockAxisSetting(0f)
// Axes for Legacy version of the Flex Clock
- val FLEX_LS_WEIGHT_AXIS = ClockFontAxisSetting(GSFAxes.WEIGHT, 600f)
- val FLEX_AOD_LARGE_WEIGHT_AXIS = ClockFontAxisSetting(GSFAxes.WEIGHT, 74f)
- val FLEX_AOD_SMALL_WEIGHT_AXIS = ClockFontAxisSetting(GSFAxes.WEIGHT, 133f)
- val FLEX_LS_WIDTH_AXIS = ClockFontAxisSetting(GSFAxes.WIDTH, 100f)
- val FLEX_AOD_WIDTH_AXIS = ClockFontAxisSetting(GSFAxes.WIDTH, 43f)
- val FLEX_ROUND_AXIS = ClockFontAxisSetting(GSFAxes.ROUND, 100f)
+ val FLEX_LS_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(600f)
+ val FLEX_AOD_LARGE_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(74f)
+ val FLEX_AOD_SMALL_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(133f)
+ val FLEX_LS_WIDTH_AXIS = GSFAxes.WIDTH.toClockAxisSetting(100f)
+ val FLEX_AOD_WIDTH_AXIS = GSFAxes.WIDTH.toClockAxisSetting(43f)
+ val FLEX_ROUND_AXIS = GSFAxes.ROUND.toClockAxisSetting(100f)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/EmergencyButtonControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
index c42e25b..d046ad1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
@@ -103,7 +103,7 @@
fun testUpdateEmergencyButton() {
Mockito.`when`(telecomManager.isInCall).thenReturn(true)
Mockito.`when`(lockPatternUtils.isSecure(anyInt())).thenReturn(true)
- Mockito.`when`(packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY))
+ Mockito.`when`(packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING))
.thenReturn(true)
underTest.updateEmergencyCallButton()
backgroundExecutor.runAllReady()
@@ -112,7 +112,7 @@
/* isInCall= */ any(),
/* hasTelephonyRadio= */ any(),
/* simLocked= */ any(),
- /* isSecure= */ any()
+ /* isSecure= */ any(),
)
mainExecutor.runAllReady()
verify(emergencyButton)
@@ -120,7 +120,7 @@
/* isInCall= */ eq(true),
/* hasTelephonyRadio= */ eq(true),
/* simLocked= */ any(),
- /* isSecure= */ eq(true)
+ /* isSecure= */ eq(true),
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/FontVariationUtilsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/FontVariationUtilsTest.kt
index f44769d..8d3640d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/FontVariationUtilsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/FontVariationUtilsTest.kt
@@ -21,7 +21,7 @@
roundness = 100,
)
Assert.assertEquals(
- "'${GSFAxes.WEIGHT}' 100, '${GSFAxes.WIDTH}' 100, '${GSFAxes.ROUND}' 100",
+ "'${GSFAxes.WEIGHT.tag}' 100, '${GSFAxes.WIDTH.tag}' 100, '${GSFAxes.ROUND.tag}' 100",
initFvar,
)
val updatedFvar =
@@ -32,7 +32,8 @@
roundness = 100,
)
Assert.assertEquals(
- "'${GSFAxes.WEIGHT}' 200, '${GSFAxes.WIDTH}' 100, '${GSFAxes.OPTICAL_SIZE}' 0, '${GSFAxes.ROUND}' 100",
+ "'${GSFAxes.WEIGHT.tag}' 200, '${GSFAxes.WIDTH.tag}' 100," +
+ " '${GSFAxes.OPTICAL_SIZE.tag}' 0, '${GSFAxes.ROUND.tag}' 100",
updatedFvar,
)
}
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/qs/tiles/CastTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java
index 31a627f..765c574 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -50,6 +50,8 @@
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.shade.domain.interactor.FakeShadeDialogContextInteractor;
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor;
import com.android.systemui.statusbar.connectivity.IconState;
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.connectivity.SignalCallback;
@@ -107,6 +109,8 @@
private final FakeConnectivityRepository mConnectivityRepository =
new FakeConnectivityRepository();
private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
+ private final ShadeDialogContextInteractor mShadeDialogContextInteractor =
+ new FakeShadeDialogContextInteractor(mContext);
private TestableLooper mTestableLooper;
private CastTile mCastTile;
@@ -535,7 +539,8 @@
mDialogTransitionAnimator,
mConnectivityRepository,
mJavaAdapter,
- mFeatureFlags
+ mFeatureFlags,
+ mShadeDialogContextInteractor
);
mCastTile.initialize();
@@ -578,7 +583,8 @@
mDialogTransitionAnimator,
mConnectivityRepository,
mJavaAdapter,
- mFeatureFlags
+ mFeatureFlags,
+ mShadeDialogContextInteractor
);
mCastTile.initialize();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index 83361da..4b06cde 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -34,7 +34,6 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeExpansionChangeEvent
-import com.android.systemui.shared.Flags as SharedFlags
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.ScrimController
@@ -44,11 +43,10 @@
import com.android.systemui.testKosmos
import com.android.systemui.util.WallpaperController
import com.android.systemui.util.mockito.eq
+import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor
import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
import com.android.wm.shell.appzoomout.AppZoomOut
import com.google.common.truth.Truth.assertThat
-import java.util.Optional
-import java.util.function.Consumer
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -69,6 +67,8 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
+import java.util.Optional
+import java.util.function.Consumer
@RunWith(AndroidJUnit4::class)
@RunWithLooper
@@ -76,6 +76,7 @@
class NotificationShadeDepthControllerTest : SysuiTestCase() {
private val kosmos = testKosmos()
+ private val applicationScope = kosmos.testScope.backgroundScope
@Mock private lateinit var windowRootViewBlurInteractor: WindowRootViewBlurInteractor
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var blurUtils: BlurUtils
@@ -84,6 +85,7 @@
@Mock private lateinit var keyguardInteractor: KeyguardInteractor
@Mock private lateinit var choreographer: Choreographer
@Mock private lateinit var wallpaperController: WallpaperController
+ @Mock private lateinit var wallpaperInteractor: WallpaperInteractor
@Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var appZoomOutOptional: Optional<AppZoomOut>
@@ -128,12 +130,14 @@
keyguardInteractor,
choreographer,
wallpaperController,
+ wallpaperInteractor,
notificationShadeWindowController,
dozeParameters,
context,
ResourcesSplitShadeStateController(),
windowRootViewBlurInteractor,
appZoomOutOptional,
+ applicationScope,
dumpManager,
configurationController,
)
@@ -310,22 +314,24 @@
}
@Test
- @DisableFlags(SharedFlags.FLAG_AMBIENT_AOD)
- fun onDozeAmountChanged_appliesBlur() {
- statusBarStateListener.onDozeAmountChanged(1f, 1f)
- notificationShadeDepthController.updateBlurCallback.doFrame(0)
- verify(blurUtils).applyBlur(any(), eq(maxBlur), eq(false))
- }
-
- @Test
- @EnableFlags(SharedFlags.FLAG_AMBIENT_AOD)
fun onDozeAmountChanged_doesNotApplyBlurWithAmbientAod() {
+ notificationShadeDepthController.wallpaperSupportsAmbientMode = false
+
statusBarStateListener.onDozeAmountChanged(1f, 1f)
notificationShadeDepthController.updateBlurCallback.doFrame(0)
verify(blurUtils).applyBlur(any(), eq(0), eq(false))
}
@Test
+ fun onDozeAmountChanged_appliesBlurWithAmbientAod() {
+ notificationShadeDepthController.wallpaperSupportsAmbientMode = true
+
+ statusBarStateListener.onDozeAmountChanged(1f, 1f)
+ notificationShadeDepthController.updateBlurCallback.doFrame(0)
+ verify(blurUtils).applyBlur(any(), eq(maxBlur), eq(false))
+ }
+
+ @Test
fun setFullShadeTransition_appliesBlur_onlyIfSupported() {
reset(blurUtils)
`when`(blurUtils.blurRadiusOfRatio(anyFloat())).then { answer ->
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt
index e38ea30..9fd189f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt
@@ -20,11 +20,14 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
-import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.addOngoingCallState
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.removeOngoingCallState
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.test.runTest
@@ -33,22 +36,20 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class CallChipInteractorTest : SysuiTestCase() {
- val kosmos = Kosmos()
+ val kosmos = testKosmos().useUnconfinedTestDispatcher()
val repo = kosmos.ongoingCallRepository
val underTest = kosmos.callChipInteractor
@Test
- fun ongoingCallState_matchesRepo() =
- kosmos.testScope.runTest {
+ fun ongoingCallState_matchesState() =
+ kosmos.runTest {
val latest by collectLastValue(underTest.ongoingCallState)
- val inCall = inCallModel(startTimeMs = 1000)
- repo.setOngoingCallState(inCall)
- assertThat(latest).isEqualTo(inCall)
+ addOngoingCallState(key = "testKey")
+ assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
- val noCall = OngoingCallModel.NoCall
- repo.setOngoingCallState(noCall)
- assertThat(latest).isEqualTo(noCall)
+ removeOngoingCallState(key = "testKey")
+ assertThat(latest).isEqualTo(OngoingCallModel.NoCall)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
index e044d1d..fda4ab0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
@@ -27,7 +27,9 @@
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.plugins.activityStarter
import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView
@@ -35,17 +37,11 @@
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
-import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
-import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
-import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
-import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
-import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.statusbar.phone.ongoingcall.DisableChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization
-import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
-import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
-import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.addOngoingCallState
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.removeOngoingCallState
import com.android.systemui.testKosmos
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -60,10 +56,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class CallChipViewModelTest : SysuiTestCase() {
- private val kosmos = testKosmos()
- private val notificationListRepository = kosmos.activeNotificationListRepository
- private val testScope = kosmos.testScope
- private val repo = kosmos.ongoingCallRepository
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val chipBackgroundView = mock<ChipBackgroundContainer>()
private val chipView =
@@ -82,53 +75,53 @@
@Test
fun chip_noCall_isHidden() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
- repo.setOngoingCallState(OngoingCallModel.NoCall)
+ removeOngoingCallState("testKey")
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
}
@Test
fun chip_inCall_zeroStartTime_isShownAsIconOnly() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
- repo.setOngoingCallState(inCallModel(startTimeMs = 0))
+ addOngoingCallState(startTimeMs = 0)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
}
@Test
fun chip_inCall_negativeStartTime_isShownAsIconOnly() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
- repo.setOngoingCallState(inCallModel(startTimeMs = -2))
+ addOngoingCallState(startTimeMs = -2)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
}
@Test
fun chip_inCall_positiveStartTime_isShownAsTimer() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
- repo.setOngoingCallState(inCallModel(startTimeMs = 345))
+ addOngoingCallState(startTimeMs = 345)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
}
@Test
fun chip_inCall_startTimeConvertedToElapsedRealtime() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
kosmos.fakeSystemClock.setCurrentTimeMillis(3000)
kosmos.fakeSystemClock.setElapsedRealtime(400_000)
- repo.setOngoingCallState(inCallModel(startTimeMs = 1000))
+ addOngoingCallState(startTimeMs = 1000)
// The OngoingCallModel start time is relative to currentTimeMillis, so this call
// started 2000ms ago (1000 - 3000). The OngoingActivityChipModel start time needs to be
@@ -141,13 +134,11 @@
@Test
@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
fun chip_positiveStartTime_connectedDisplaysFlagOn_iconIsNotifIcon() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
val notifKey = "testNotifKey"
- repo.setOngoingCallState(
- inCallModel(startTimeMs = 1000, notificationIcon = null, notificationKey = notifKey)
- )
+ addOngoingCallState(startTimeMs = 1000, statusBarChipIconView = null, key = notifKey)
assertThat((latest as OngoingActivityChipModel.Active).icon)
.isInstanceOf(
@@ -163,16 +154,14 @@
@Test
@DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
fun chip_zeroStartTime_cdFlagOff_iconIsNotifIcon_withContentDescription() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
val notifIcon = createStatusBarIconViewOrNull()
- repo.setOngoingCallState(
- inCallModel(
- startTimeMs = 0,
- notificationIcon = notifIcon,
- appName = "Fake app name",
- )
+ addOngoingCallState(
+ startTimeMs = 0,
+ statusBarChipIconView = notifIcon,
+ appName = "Fake app name",
)
assertThat((latest as OngoingActivityChipModel.Active).icon)
@@ -190,16 +179,13 @@
@Test
@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
fun chip_zeroStartTime_cdFlagOn_iconIsNotifKeyIcon_withContentDescription() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
- repo.setOngoingCallState(
- inCallModel(
- startTimeMs = 0,
- notificationIcon = createStatusBarIconViewOrNull(),
- notificationKey = "notifKey",
- appName = "Fake app name",
- )
+ addOngoingCallState(
+ key = "notifKey",
+ statusBarChipIconView = createStatusBarIconViewOrNull(),
+ appName = "Fake app name",
)
assertThat((latest as OngoingActivityChipModel.Active).icon)
@@ -219,10 +205,10 @@
@Test
@DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
fun chip_notifIconFlagOn_butNullNotifIcon_cdFlagOff_iconIsPhone() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
- repo.setOngoingCallState(inCallModel(startTimeMs = 1000, notificationIcon = null))
+ addOngoingCallState(statusBarChipIconView = null)
assertThat((latest as OngoingActivityChipModel.Active).icon)
.isInstanceOf(OngoingActivityChipModel.ChipIcon.SingleColorIcon::class.java)
@@ -237,16 +223,13 @@
@Test
@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
fun chip_notifIconFlagOn_butNullNotifIcon_cdFlagOn_iconIsNotifKeyIcon_withContentDescription() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
- repo.setOngoingCallState(
- inCallModel(
- startTimeMs = 1000,
- notificationIcon = null,
- notificationKey = "notifKey",
- appName = "Fake app name",
- )
+ addOngoingCallState(
+ key = "notifKey",
+ statusBarChipIconView = null,
+ appName = "Fake app name",
)
assertThat((latest as OngoingActivityChipModel.Active).icon)
@@ -265,10 +248,10 @@
@Test
fun chip_positiveStartTime_colorsAreAccentThemed() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
- repo.setOngoingCallState(inCallModel(startTimeMs = 1000, promotedContent = null))
+ addOngoingCallState(startTimeMs = 1000, promotedContent = null)
assertThat((latest as OngoingActivityChipModel.Active).colors)
.isEqualTo(ColorsModel.AccentThemed)
@@ -276,10 +259,10 @@
@Test
fun chip_zeroStartTime_colorsAreAccentThemed() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
- repo.setOngoingCallState(inCallModel(startTimeMs = 0, promotedContent = null))
+ addOngoingCallState(startTimeMs = 0, promotedContent = null)
assertThat((latest as OngoingActivityChipModel.Active).colors)
.isEqualTo(ColorsModel.AccentThemed)
@@ -287,19 +270,19 @@
@Test
fun chip_resetsCorrectly() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
kosmos.fakeSystemClock.setCurrentTimeMillis(3000)
kosmos.fakeSystemClock.setElapsedRealtime(400_000)
// Start a call
- repo.setOngoingCallState(inCallModel(startTimeMs = 1000))
+ addOngoingCallState(key = "testKey", startTimeMs = 1000)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
assertThat((latest as OngoingActivityChipModel.Active.Timer).startTimeMs)
.isEqualTo(398_000)
// End the call
- repo.setOngoingCallState(OngoingCallModel.NoCall)
+ removeOngoingCallState(key = "testKey")
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
// Let 100_000ms elapse
@@ -307,7 +290,7 @@
kosmos.fakeSystemClock.setElapsedRealtime(500_000)
// Start a new call, which started 1000ms ago
- repo.setOngoingCallState(inCallModel(startTimeMs = 102_000))
+ addOngoingCallState(key = "testKey", startTimeMs = 102_000)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
assertThat((latest as OngoingActivityChipModel.Active.Timer).startTimeMs)
.isEqualTo(499_000)
@@ -316,10 +299,10 @@
@Test
@DisableChipsModernization
fun chip_inCall_nullIntent_nullClickListener() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
- repo.setOngoingCallState(inCallModel(startTimeMs = 1000, intent = null))
+ addOngoingCallState(contentIntent = null)
assertThat((latest as OngoingActivityChipModel.Active).onClickListenerLegacy).isNull()
}
@@ -327,11 +310,11 @@
@Test
@DisableChipsModernization
fun chip_inCall_positiveStartTime_validIntent_clickListenerLaunchesIntent() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
val pendingIntent = mock<PendingIntent>()
- repo.setOngoingCallState(inCallModel(startTimeMs = 1000, intent = pendingIntent))
+ addOngoingCallState(startTimeMs = 1000, contentIntent = pendingIntent)
val clickListener = (latest as OngoingActivityChipModel.Active).onClickListenerLegacy
assertThat(clickListener).isNotNull()
@@ -345,11 +328,11 @@
@Test
@DisableChipsModernization
fun chip_inCall_zeroStartTime_validIntent_clickListenerLaunchesIntent() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
val pendingIntent = mock<PendingIntent>()
- repo.setOngoingCallState(inCallModel(startTimeMs = 0, intent = pendingIntent))
+ addOngoingCallState(startTimeMs = 0, contentIntent = pendingIntent)
val clickListener = (latest as OngoingActivityChipModel.Active).onClickListenerLegacy
assertThat(clickListener).isNotNull()
@@ -364,14 +347,10 @@
@Test
@EnableChipsModernization
fun chip_inCall_nullIntent_noneClickBehavior() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
- postOngoingCallNotification(
- repository = notificationListRepository,
- startTimeMs = 1000L,
- intent = null,
- )
+ addOngoingCallState(startTimeMs = 1000, contentIntent = null)
assertThat((latest as OngoingActivityChipModel.Active).clickBehavior)
.isInstanceOf(OngoingActivityChipModel.ClickBehavior.None::class.java)
@@ -380,15 +359,11 @@
@Test
@EnableChipsModernization
fun chip_inCall_positiveStartTime_validIntent_clickBehaviorLaunchesIntent() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
val pendingIntent = mock<PendingIntent>()
- postOngoingCallNotification(
- repository = notificationListRepository,
- startTimeMs = 1000L,
- intent = pendingIntent,
- )
+ addOngoingCallState(startTimeMs = 1000, contentIntent = pendingIntent)
val clickBehavior = (latest as OngoingActivityChipModel.Active).clickBehavior
assertThat(clickBehavior)
@@ -405,15 +380,11 @@
@Test
@EnableChipsModernization
fun chip_inCall_zeroStartTime_validIntent_clickBehaviorLaunchesIntent() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
val pendingIntent = mock<PendingIntent>()
- postOngoingCallNotification(
- repository = notificationListRepository,
- startTimeMs = 0L,
- intent = pendingIntent,
- )
+ addOngoingCallState(startTimeMs = 0, contentIntent = pendingIntent)
val clickBehavior = (latest as OngoingActivityChipModel.Active).clickBehavior
assertThat(clickBehavior)
@@ -435,27 +406,6 @@
mock<StatusBarIconView>()
}
- fun postOngoingCallNotification(
- repository: ActiveNotificationListRepository,
- startTimeMs: Long,
- intent: PendingIntent?,
- ) {
- repository.activeNotifications.value =
- ActiveNotificationsStore.Builder()
- .apply {
- addIndividualNotif(
- activeNotificationModel(
- key = "notif1",
- whenTime = startTimeMs,
- callType = CallType.Ongoing,
- statusBarChipIcon = null,
- contentIntent = intent,
- )
- )
- }
- .build()
- }
-
private val PROMOTED_CONTENT_WITH_COLOR =
PromotedNotificationContentModel.Builder("notif")
.apply {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
index 626dcd5..1b5b0d6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
@@ -22,15 +22,18 @@
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
@@ -48,16 +51,13 @@
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
-import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
-import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
-import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.addOngoingCallState
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.removeOngoingCallState
import com.android.systemui.testKosmos
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -74,13 +74,11 @@
@RunWith(AndroidJUnit4::class)
@DisableFlags(StatusBarNotifChips.FLAG_NAME)
class OngoingActivityChipsViewModelTest : SysuiTestCase() {
- private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val systemClock = kosmos.fakeSystemClock
private val screenRecordState = kosmos.screenRecordRepository.screenRecordState
private val mediaProjectionState = kosmos.fakeMediaProjectionRepository.mediaProjectionState
- private val callRepo = kosmos.ongoingCallRepository
private val mockSystemUIDialog = mock<SystemUIDialog>()
private val chipBackgroundView = mock<ChipBackgroundContainer>()
@@ -96,7 +94,7 @@
private val mockExpandable: Expandable =
mock<Expandable>().apply { whenever(dialogTransitionController(any())).thenReturn(mock()) }
- private val underTest = kosmos.ongoingActivityChipsViewModel
+ private val Kosmos.underTest by Kosmos.Fixture { ongoingActivityChipsViewModel }
@Before
fun setUp() {
@@ -111,10 +109,10 @@
@Test
fun primaryChip_allHidden_hidden() =
- testScope.runTest {
+ kosmos.runTest {
screenRecordState.value = ScreenRecordModel.DoingNothing
mediaProjectionState.value = MediaProjectionState.NotProjecting
- callRepo.setOngoingCallState(OngoingCallModel.NoCall)
+ removeOngoingCallState("testKey")
val latest by collectLastValue(underTest.primaryChip)
@@ -123,10 +121,10 @@
@Test
fun primaryChip_screenRecordShow_restHidden_screenRecordShown() =
- testScope.runTest {
+ kosmos.runTest {
screenRecordState.value = ScreenRecordModel.Recording
mediaProjectionState.value = MediaProjectionState.NotProjecting
- callRepo.setOngoingCallState(OngoingCallModel.NoCall)
+ removeOngoingCallState("testKey")
val latest by collectLastValue(underTest.primaryChip)
@@ -135,10 +133,10 @@
@Test
fun primaryChip_screenRecordShowAndCallShow_screenRecordShown() =
- testScope.runTest {
+ kosmos.runTest {
screenRecordState.value = ScreenRecordModel.Recording
- callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+ addOngoingCallState()
val latest by collectLastValue(underTest.primaryChip)
@@ -147,11 +145,11 @@
@Test
fun primaryChip_screenRecordShowAndShareToAppShow_screenRecordShown() =
- testScope.runTest {
+ kosmos.runTest {
screenRecordState.value = ScreenRecordModel.Recording
mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
- callRepo.setOngoingCallState(OngoingCallModel.NoCall)
+ removeOngoingCallState("testKey")
val latest by collectLastValue(underTest.primaryChip)
@@ -160,11 +158,11 @@
@Test
fun primaryChip_shareToAppShowAndCallShow_shareToAppShown() =
- testScope.runTest {
+ kosmos.runTest {
screenRecordState.value = ScreenRecordModel.DoingNothing
mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
- callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+ addOngoingCallState()
val latest by collectLastValue(underTest.primaryChip)
@@ -173,15 +171,13 @@
@Test
fun primaryChip_screenRecordAndShareToAppAndCastToOtherHideAndCallShown_callShown() =
- testScope.runTest {
+ kosmos.runTest {
val notificationKey = "call"
screenRecordState.value = ScreenRecordModel.DoingNothing
// MediaProjection covers both share-to-app and cast-to-other-device
mediaProjectionState.value = MediaProjectionState.NotProjecting
- callRepo.setOngoingCallState(
- inCallModel(startTimeMs = 34, notificationKey = notificationKey)
- )
+ addOngoingCallState(key = notificationKey)
val latest by collectLastValue(underTest.primaryChip)
@@ -190,12 +186,10 @@
@Test
fun primaryChip_higherPriorityChipAdded_lowerPriorityChipReplaced() =
- testScope.runTest {
+ kosmos.runTest {
// Start with just the lowest priority chip shown
val callNotificationKey = "call"
- callRepo.setOngoingCallState(
- inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
- )
+ addOngoingCallState(key = callNotificationKey)
// And everything else hidden
mediaProjectionState.value = MediaProjectionState.NotProjecting
screenRecordState.value = ScreenRecordModel.DoingNothing
@@ -224,15 +218,13 @@
@Test
fun primaryChip_highestPriorityChipRemoved_showsNextPriorityChip() =
- testScope.runTest {
+ kosmos.runTest {
// WHEN all chips are active
screenRecordState.value = ScreenRecordModel.Recording
mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
val callNotificationKey = "call"
- callRepo.setOngoingCallState(
- inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
- )
+ addOngoingCallState(key = callNotificationKey)
val latest by collectLastValue(underTest.primaryChip)
@@ -255,17 +247,15 @@
/** Regression test for b/347726238. */
@Test
fun primaryChip_timerDoesNotResetAfterSubscribersRestart() =
- testScope.runTest {
+ kosmos.runTest {
var latest: OngoingActivityChipModel? = null
- val job1 = underTest.primaryChip.onEach { latest = it }.launchIn(this)
+ val job1 = underTest.primaryChip.onEach { latest = it }.launchIn(kosmos.testScope)
// Start a chip with a timer
systemClock.setElapsedRealtime(1234)
screenRecordState.value = ScreenRecordModel.Recording
- runCurrent()
-
assertThat((latest as OngoingActivityChipModel.Active.Timer).startTimeMs)
.isEqualTo(1234)
@@ -276,9 +266,7 @@
systemClock.setElapsedRealtime(5678)
// WHEN we re-subscribe to the chip flow
- val job2 = underTest.primaryChip.onEach { latest = it }.launchIn(this)
-
- runCurrent()
+ val job2 = underTest.primaryChip.onEach { latest = it }.launchIn(kosmos.testScope)
// THEN the old start time is still used
assertThat((latest as OngoingActivityChipModel.Active.Timer).startTimeMs)
@@ -289,14 +277,14 @@
@Test
fun primaryChip_screenRecordStoppedViaDialog_chipHiddenWithoutAnimation() =
- testScope.runTest {
+ kosmos.runTest {
screenRecordState.value = ScreenRecordModel.Recording
mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(
NORMAL_PACKAGE,
hostDeviceName = "Recording Display",
)
- callRepo.setOngoingCallState(OngoingCallModel.NoCall)
+ removeOngoingCallState("testKey")
val latest by collectLastValue(underTest.primaryChip)
@@ -319,11 +307,11 @@
@Test
fun primaryChip_projectionStoppedViaDialog_chipHiddenWithoutAnimation() =
- testScope.runTest {
+ kosmos.runTest {
mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
screenRecordState.value = ScreenRecordModel.DoingNothing
- callRepo.setOngoingCallState(OngoingCallModel.NoCall)
+ removeOngoingCallState("testKey")
val latest by collectLastValue(underTest.primaryChip)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
index ab475c5..2f6bedb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
@@ -37,6 +37,8 @@
import com.android.systemui.statusbar.layout.LetterboxAppearanceCalculator
import com.android.systemui.statusbar.layout.StatusBarBoundsProvider
import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent
+import com.android.systemui.statusbar.phone.ongoingcall.DisableChipsModernization
+import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
@@ -387,6 +389,7 @@
}
@Test
+ @DisableChipsModernization
fun statusBarMode_ongoingCallAndFullscreen_semiTransparent() =
testScope.runTest {
val latest by collectLastValue(underTest.statusBarAppearance)
@@ -398,6 +401,19 @@
}
@Test
+ @EnableChipsModernization
+ fun statusBarMode_ongoingProcessRequiresStatusBarVisible_andFullscreen_semiTransparent() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.statusBarAppearance)
+
+ underTest.setOngoingProcessRequiresStatusBarVisible(true)
+ onSystemBarAttributesChanged(requestedVisibleTypes = WindowInsets.Type.navigationBars())
+
+ assertThat(latest!!.mode).isEqualTo(StatusBarMode.SEMI_TRANSPARENT)
+ }
+
+ @Test
+ @DisableChipsModernization
fun statusBarMode_ongoingCallButNotFullscreen_matchesAppearance() =
testScope.runTest {
val latest by collectLastValue(underTest.statusBarAppearance)
@@ -413,6 +429,23 @@
}
@Test
+ @EnableChipsModernization
+ fun statusBarMode_ongoingProcessRequiresStatusBarVisible_butNotFullscreen_matchesAppearance() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.statusBarAppearance)
+
+ underTest.setOngoingProcessRequiresStatusBarVisible(true)
+
+ onSystemBarAttributesChanged(
+ requestedVisibleTypes = WindowInsets.Type.statusBars(),
+ appearance = APPEARANCE_OPAQUE_STATUS_BARS,
+ )
+
+ assertThat(latest!!.mode).isEqualTo(StatusBarMode.OPAQUE)
+ }
+
+ @Test
+ @DisableChipsModernization
fun statusBarMode_fullscreenButNotOngoingCall_matchesAppearance() =
testScope.runTest {
val latest by collectLastValue(underTest.statusBarAppearance)
@@ -427,6 +460,21 @@
}
@Test
+ @EnableChipsModernization
+ fun statusBarMode_fullscreen_butNotOngoingProcessRequiresStatusBarVisible_matchesAppearance() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.statusBarAppearance)
+
+ underTest.setOngoingProcessRequiresStatusBarVisible(false)
+ onSystemBarAttributesChanged(
+ requestedVisibleTypes = WindowInsets.Type.navigationBars(),
+ appearance = APPEARANCE_OPAQUE_STATUS_BARS,
+ )
+
+ assertThat(latest!!.mode).isEqualTo(StatusBarMode.OPAQUE)
+ }
+
+ @Test
fun statusBarMode_transientShown_semiTransparent() =
testScope.runTest {
val latest by collectLastValue(underTest.statusBarAppearance)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt
index a1772e3..510167d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt
@@ -10,6 +10,7 @@
import com.android.systemui.jank.interactionJankMonitor
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
+import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.data.repository.NotificationLaunchAnimationRepository
@@ -165,7 +166,10 @@
.setSummary(summary)
.addChild(notification.entry)
.build()
- assertSame(summary, notification.entry.parent?.summary)
+
+ val parentSummary = if (notification.entry.parent is GroupEntry)
+ (notification.entry.parent as GroupEntry).summary else null
+ assertSame(summary, parentSummary)
`when`(headsUpManager.isHeadsUpEntry(notificationKey)).thenReturn(false)
`when`(headsUpManager.isHeadsUpEntry(summary.key)).thenReturn(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
index 99bd4fc..d9c9177 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
@@ -26,6 +26,7 @@
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.getAttachState
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
@@ -409,7 +410,7 @@
): NotifSection {
return NotifSection(object : NotifSectioner("Section $index (bucket=$bucket)", bucket) {
- override fun isInSection(entry: ListEntry?): Boolean {
+ override fun isInSection(entry: PipelineEntry?): Boolean {
throw NotImplementedError("This should never be called")
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt
index e6fbc72..4a05804 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt
@@ -24,6 +24,7 @@
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder
import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener
import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderGroupListener
@@ -40,6 +41,7 @@
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoMoreInteractions
+import java.nio.channels.Pipe
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -198,7 +200,7 @@
)
private class FakeNotifViewRenderer : NotifViewRenderer {
- override fun onRenderList(notifList: List<ListEntry>) {}
+ override fun onRenderList(notifList: List<PipelineEntry>) {}
override fun getGroupController(group: GroupEntry): NotifGroupController = mock()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt
index 9a6a699..081f52c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt
@@ -135,6 +135,7 @@
.thenReturn(mock())
whenever(requireViewById<View>(R.id.app_name_text)).thenReturn(mock())
whenever(requireViewById<View>(R.id.conversation_text)).thenReturn(mock())
+ whenever(requireViewById<View>(R.id.title)).thenReturn(mock())
}
return mockView
}
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/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 01ba4df..7603eec 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -146,6 +146,20 @@
}
@Test
+ @EnableSceneContainer
+ fun resetViewStates_defaultHun_hasShadow() {
+ val headsUpTop = 200f
+ ambientState.headsUpTop = headsUpTop
+
+ whenever(notificationRow.isPinned).thenReturn(true)
+ whenever(notificationRow.isHeadsUp).thenReturn(true)
+
+ stackScrollAlgorithm.resetViewStates(ambientState, 0)
+
+ assertThat(notificationRow.viewState.zTranslation).isGreaterThan(baseZ)
+ }
+
+ @Test
@DisableSceneContainer
fun resetViewStates_defaultHunWhenShadeIsOpening_yTranslationIsInset() {
whenever(notificationRow.isPinned).thenReturn(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
index 67415de..e7be20e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
@@ -88,6 +88,7 @@
/* delay= */ 0L,
/* duration= */ ANIMATION_DURATION_HEADS_UP_APPEAR.toLong(),
/* isHeadsUpAppear= */ true,
+ /* isHeadsUpCycling= */ false,
/* onEndRunnable= */ null,
)
}
@@ -111,6 +112,7 @@
/* delay= */ 0L,
/* duration= */ ANIMATION_DURATION_HEADS_UP_APPEAR.toLong(),
/* isHeadsUpAppear= */ true,
+ /* isHeadsUpCycling= */ false,
/* onEndRunnable= */ null,
)
}
@@ -128,6 +130,7 @@
/* delay= */ eq(0L),
/* translationDirection= */ eq(0f),
/* isHeadsUpAnimation= */ eq(true),
+ /* isHeadsUpCycling= */ eq(false),
/* onStartedRunnable= */ any(),
/* onFinishedRunnable= */ runnableCaptor.capture(),
/* animationListener= */ any(),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 21297e3..c630a1c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -17,14 +17,15 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
-import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
-import com.android.systemui.communal.data.repository.communalSceneRepository
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
@@ -54,11 +55,14 @@
import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
import com.android.systemui.keyguard.ui.viewmodel.aodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.keyguardRootViewModel
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.scene.data.repository.Idle
import com.android.systemui.scene.data.repository.Transition
import com.android.systemui.scene.data.repository.setTransition
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.enableDualShade
import com.android.systemui.shade.domain.interactor.enableSingleShade
@@ -125,7 +129,6 @@
kosmos.sharedNotificationContainerInteractor
}
private val largeScreenHeaderHelper by lazy { kosmos.mockLargeScreenHeaderHelper }
- private val communalSceneRepository by lazy { kosmos.communalSceneRepository }
lateinit var underTest: SharedNotificationContainerViewModel
@@ -591,6 +594,25 @@
}
@Test
+ @DisableSceneContainer
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ fun isOnLockscreenFalseWhenCommunalShowing() =
+ kosmos.runTest {
+ val isOnLockscreen by collectLastValue(underTest.isOnLockscreen)
+
+ setTransition(
+ sceneTransition = Idle(Scenes.Bouncer),
+ stateTransition = TransitionStep(from = LOCKSCREEN, to = PRIMARY_BOUNCER),
+ )
+ assertThat(isOnLockscreen).isTrue()
+
+ testScope.showCommunalScene()
+
+ // If bouncer is showing over the hub, it should not be considered on lockscreen
+ assertThat(isOnLockscreen).isFalse()
+ }
+
+ @Test
fun isOnLockscreenWithoutShade() =
testScope.runTest {
val isOnLockscreenWithoutShade by collectLastValue(underTest.isOnLockscreenWithoutShade)
@@ -1472,20 +1494,24 @@
}
private fun TestScope.showCommunalScene() {
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(CommunalScenes.Communal)
- )
- communalSceneRepository.setTransitionState(transitionState)
+ val targetScene =
+ if (SceneContainerFlag.isEnabled) {
+ Scenes.Communal
+ } else {
+ CommunalScenes.Communal
+ }
+ kosmos.communalSceneInteractor.changeScene(targetScene, "test")
runCurrent()
}
private fun TestScope.hideCommunalScene() {
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(CommunalScenes.Blank)
- )
- communalSceneRepository.setTransitionState(transitionState)
+ val targetScene =
+ if (SceneContainerFlag.isEnabled) {
+ Scenes.Lockscreen
+ } else {
+ CommunalScenes.Blank
+ }
+ kosmos.communalSceneInteractor.changeScene(targetScene, "test")
runCurrent()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index d17abd7..6066a38 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
import android.os.ParcelUuid
+import android.platform.test.annotations.EnableFlags
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING
import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
@@ -33,9 +34,12 @@
import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.core.NewStatusBarIcons
+import com.android.systemui.statusbar.core.StatusBarRootModernization
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fake
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepositoryLogbufferName
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
@@ -897,6 +901,71 @@
assertThat(latest).isEqualTo(2)
}
+ @Test
+ @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
+ fun isStackable_tracksNumberOfSubscriptions() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.isStackable)
+
+ connectionsRepository.setSubscriptions(listOf(SUB_1))
+ assertThat(latest).isFalse()
+
+ connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
+ assertThat(latest).isTrue()
+
+ connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2, SUB_3_OPP))
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
+ fun isStackable_checksForTerrestrialConnections() =
+ kosmos.runTest {
+ val exclusivelyNonTerrestrialSub =
+ SubscriptionModel(
+ isExclusivelyNonTerrestrial = true,
+ subscriptionId = 5,
+ carrierName = "Carrier 5",
+ profileClass = PROFILE_CLASS_UNSET,
+ )
+
+ val latest by collectLastValue(underTest.isStackable)
+
+ connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
+ assertThat(latest).isTrue()
+
+ connectionsRepository.setSubscriptions(listOf(SUB_1, exclusivelyNonTerrestrialSub))
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
+ fun isStackable_checksForNumberOfBars() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.isStackable)
+
+ // Number of levels is the same for both
+ connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
+ setNumberOfLevelsForSubId(SUB_1_ID, 5)
+ setNumberOfLevelsForSubId(SUB_2_ID, 5)
+
+ assertThat(latest).isTrue()
+
+ // Change the number of levels to be different than SUB_2
+ setNumberOfLevelsForSubId(SUB_1_ID, 6)
+
+ assertThat(latest).isFalse()
+ }
+
+ private fun setNumberOfLevelsForSubId(subId: Int, numberOfLevels: Int) {
+ with(kosmos) {
+ (fakeMobileConnectionsRepository.getRepoForSubId(subId)
+ as FakeMobileConnectionRepository)
+ .numberOfLevels
+ .value = numberOfLevels
+ }
+ }
+
/**
* Convenience method for creating a pair of subscriptions to test the filteredSubscriptions
* flow.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelTest.kt
new file mode 100644
index 0000000..20bdebd
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelTest.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
+
+import android.platform.test.annotations.EnableFlags
+import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.statusbar.core.NewStatusBarIcons
+import com.android.systemui.statusbar.core.StatusBarRootModernization
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.fakeMobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class StackedMobileIconViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val testScope = kosmos.testScope
+
+ private val Kosmos.underTest: StackedMobileIconViewModel by Fixture {
+ stackedMobileIconViewModel
+ }
+
+ @Before
+ fun setUp() {
+ kosmos.fakeFeatureFlagsClassic.set(Flags.NEW_NETWORK_SLICE_UI, false)
+ kosmos.underTest.activateIn(testScope)
+ }
+
+ @Test
+ @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
+ fun dualSim_filtersOutNonDualConnections() =
+ kosmos.runTest {
+ fakeMobileIconsInteractor.filteredSubscriptions.value = listOf()
+ assertThat(underTest.dualSim).isNull()
+
+ fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1)
+ assertThat(underTest.dualSim).isNull()
+
+ fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2, SUB_3)
+ assertThat(underTest.dualSim).isNull()
+
+ fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+ assertThat(underTest.dualSim).isNotNull()
+ }
+
+ @Test
+ @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
+ fun dualSim_filtersOutNonCellularIcons() =
+ kosmos.runTest {
+ fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1)
+ assertThat(underTest.dualSim).isNull()
+
+ fakeMobileIconsInteractor
+ .getInteractorForSubId(SUB_1.subscriptionId)!!
+ .signalLevelIcon
+ .value =
+ SignalIconModel.Satellite(
+ level = 0,
+ icon = Icon.Resource(res = 0, contentDescription = null),
+ )
+ fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+ assertThat(underTest.dualSim).isNull()
+ }
+
+ @Test
+ @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
+ fun dualSim_tracksActiveSubId() =
+ kosmos.runTest {
+ // Active sub id is null, order is unchanged
+ fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+ setIconLevel(SUB_1.subscriptionId, 1)
+ setIconLevel(SUB_2.subscriptionId, 2)
+
+ assertThat(underTest.dualSim!!.primary.level).isEqualTo(1)
+ assertThat(underTest.dualSim!!.secondary.level).isEqualTo(2)
+
+ // Active sub is 2, order is swapped
+ fakeMobileIconsInteractor.activeMobileDataSubscriptionId.value = SUB_2.subscriptionId
+
+ assertThat(underTest.dualSim!!.primary.level).isEqualTo(2)
+ assertThat(underTest.dualSim!!.secondary.level).isEqualTo(1)
+ }
+
+ private fun setIconLevel(subId: Int, level: Int) {
+ with(kosmos.fakeMobileIconsInteractor.getInteractorForSubId(subId)!!) {
+ signalLevelIcon.value =
+ (signalLevelIcon.value as SignalIconModel.Cellular).copy(level = level)
+ }
+ }
+
+ companion object {
+ private val SUB_1 =
+ SubscriptionModel(
+ subscriptionId = 1,
+ isOpportunistic = false,
+ carrierName = "Carrier 1",
+ profileClass = PROFILE_CLASS_UNSET,
+ )
+ private val SUB_2 =
+ SubscriptionModel(
+ subscriptionId = 2,
+ isOpportunistic = false,
+ carrierName = "Carrier 2",
+ profileClass = PROFILE_CLASS_UNSET,
+ )
+ private val SUB_3 =
+ SubscriptionModel(
+ subscriptionId = 3,
+ isOpportunistic = false,
+ carrierName = "Carrier 3",
+ profileClass = PROFILE_CLASS_UNSET,
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
index eb961bd..f91e3a6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
@@ -64,6 +64,8 @@
override val isHomeStatusBarAllowedByScene = MutableStateFlow(false)
+ override val canShowOngoingActivityChips: Flow<Boolean> = MutableStateFlow(false)
+
override val batteryViewModelFactory: BatteryViewModel.Factory =
object : BatteryViewModel.Factory {
override fun create(): BatteryViewModel = mock(BatteryViewModel::class.java)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
index 51484ba..8a796fc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
@@ -678,6 +678,60 @@
}
@Test
+ fun canShowOngoingActivityChips_statusBarHidden_noSecureCamera_noHun_false() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.canShowOngoingActivityChips)
+
+ // home status bar not allowed
+ kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen)
+ kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(false, taskInfo = null)
+
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ fun canShowOngoingActivityChips_statusBarNotHidden_noSecureCamera_noHun_true() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.canShowOngoingActivityChips)
+
+ transitionKeyguardToGone()
+
+ assertThat(latest).isTrue()
+ }
+
+ @Test
+ fun canShowOngoingActivityChips_statusBarNotHidden_secureCamera_noHun_false() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.canShowOngoingActivityChips)
+
+ fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.OCCLUDED,
+ testScope = testScope,
+ )
+ kosmos.keyguardInteractor.onCameraLaunchDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
+
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ fun canShowOngoingActivityChips_statusBarNotHidden_noSecureCamera_hun_false() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.canShowOngoingActivityChips)
+
+ transitionKeyguardToGone()
+
+ headsUpNotificationRepository.setNotifications(
+ UnconfinedFakeHeadsUpRowRepository(
+ key = "key",
+ pinnedStatus = MutableStateFlow(PinnedStatus.PinnedByUser),
+ )
+ )
+
+ assertThat(latest).isFalse()
+ }
+
+ @Test
fun isClockVisible_allowedByDisableFlags_visible() =
kosmos.runTest {
val latest by collectLastValue(underTest.isClockVisible)
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
index d3218ad..a0bf7f0 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
@@ -203,6 +203,8 @@
*/
void setIsNotificationPanelFullWidth(boolean isFullWidth);
+ default void setQSExpandingOrCollapsing(boolean isQSExpandingOrCollapsing) {}
+
/**
* Callback for when QSPanel container is scrolled
*/
diff --git a/packages/SystemUI/res/anim/volume_dialog_ringer_close.xml b/packages/SystemUI/res/anim/volume_dialog_ringer_close.xml
new file mode 100644
index 0000000..e7ba52c
--- /dev/null
+++ b/packages/SystemUI/res/anim/volume_dialog_ringer_close.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<pathInterpolator
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:controlX1="0.20"
+ android:controlY1="0.00"
+ android:controlX2="0.00"
+ android:controlY2="1.00" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/volume_dialog_ringer_open.xml b/packages/SystemUI/res/anim/volume_dialog_ringer_open.xml
new file mode 100644
index 0000000..3b18eef
--- /dev/null
+++ b/packages/SystemUI/res/anim/volume_dialog_ringer_open.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2025 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<pathInterpolator
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:controlX1="0.05"
+ android:controlY1="0.70"
+ android:controlX2="0.10"
+ android:controlY2="1.00" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/bindable_status_bar_compose_icon.xml b/packages/SystemUI/res/layout/bindable_status_bar_compose_icon.xml
index fa9318b..6b55ac2 100644
--- a/packages/SystemUI/res/layout/bindable_status_bar_compose_icon.xml
+++ b/packages/SystemUI/res/layout/bindable_status_bar_compose_icon.xml
@@ -27,7 +27,6 @@
android:layout_height="@dimen/status_bar_bindable_icon_size"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
- android:padding="4sp"
/>
</com.android.systemui.statusbar.pipeline.shared.ui.view.SingleBindableStatusBarComposeIconView>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 538d9e2..b4c5eed 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Invoer"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Gehoortoestelle"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Skakel tans aan …"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Kan nie helderheid verstel nie omdat dit\n deur die topapp beheer word"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Kan nie helderheid verstel nie omdat dit deur die topapp beheer word"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Outodraai"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Outodraai skerm"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Ligging"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index f1b9fca..8a1ba62 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ግቤት"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"መስሚያ አጋዥ መሣሪያዎች"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"በማብራት ላይ..."</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"ከላይ ባለው መተግበሪያ ቁጥጥር ላይ ስለሆነ\n ብሩህነትን ማስተካከል አልተቻለም"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"ከላይ ባለው መተግበሪያ ቁጥጥር ላይ ስለሆነ ብሩህነትን ማስተካከል አልተቻለም"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"በራስ ሰር አሽከርክር"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ማያ ገጽን በራስ-አሽከርክር"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"አካባቢ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 7d55b85..538c961 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"الإدخال"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"سماعات الأذن الطبية"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"جارٍ التفعيل…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"لا يمكن ضبط مستوى السطوع لأنّ\n التطبيق الأول يتحكّم فيه"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"لا يمكن ضبط مستوى السطوع لأنّ التطبيق الأول يتحكّم فيه"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"التدوير التلقائي"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"التدوير التلقائي للشاشة"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"الموقع الجغرافي"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 4b316b6..e6b3023 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Giriş"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Eşitmə aparatları"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aktiv edilir..."</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Yuxarıdakı tətbiq tərəfindən idarə olunduğu üçün\n parlaqlığı tənzimləmək mümkün deyil"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Yuxarıdakı tətbiq tərəfindən idarə olunduğu üçün parlaqlığı tənzimləmək mümkün deyil"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Avtodönüş"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ekranın avtomatik dönməsi"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Məkan"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 9a5bc49..19e4076 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Unos"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Slušni aparati"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Uključuje se..."</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Ne možete da prilagodite osvetljenost jer je\n kontroliše aplikacija u vrhu"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Ne možete da prilagodite osvetljenost jer je kontroliše aplikacija u vrhu"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatska rotacija"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatsko rotiranje ekrana"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 5fc56bc..aaed55b 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Увод"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слыхавыя апараты"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Уключэнне…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Не ўдаецца адрэгуляваць яркасць, бо яна\nкантралюецца асноўнай праграмай"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Не ўдаецца адрэгуляваць яркасць, бо янакантралюецца асноўнай праграмай"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Аўтапаварот"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Аўтаматычны паварот экрана"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Месцазнаходжанне"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 339d751..22e30e2 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Вход"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слухови апарати"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Включва се..."</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Яркостта не може да се коригира, защото се контролира\n от приложението на екрана"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Яркостта не може да се коригира, защото се контролира от приложението на екрана"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Авт. ориентация"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматично завъртане на екрана"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Местоположение"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index ef1253b..f0ae2ae 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ইনপুট"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"হিয়ারিং এড"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"চালু করা হচ্ছে…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"উজ্জ্বলতা টপ অ্যাপ নিয়ন্ত্রণ করায়\n এটিকে অ্যাডজাস্ট করা যাচ্ছে না"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"উজ্জ্বলতা টপ অ্যাপ নিয়ন্ত্রণ করায় এটিকে অ্যাডজাস্ট করা যাচ্ছে না"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"নিজে থেকে ঘুরবে"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"অটো-রোটেট স্ক্রিন"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"লোকেশন"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index aa036d4..a2df671 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Ulaz"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Slušni aparati"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Uključivanje…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Nije moguće podesiti osvijetljenost\n jer njome upravlja aplikacija pri vrhu"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Nije moguće podesiti osvijetljenost jer njome upravlja aplikacija pri vrhu"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatsko rotiranje"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatsko rotiranje ekrana"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 15d9283..933ac96 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audiòfons"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"S\'està activant…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"No es pot ajustar la brillantor perquè\n està controlada per l\'aplicació superior"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"No es pot ajustar la brillantor perquè està controlada per l\'aplicació superior"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Gira automàticament"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Gira la pantalla automàticament"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicació"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 031e59d..6c66fee 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Vstup"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Naslouchátka"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Zapínání…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Jas nelze upravit, protože ho\n řídí hlavní aplikace"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Jas nelze upravit, protože ho řídí hlavní aplikace"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. otáčení"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatické otáčení obrazovky"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Poloha"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 1ddaf8c..35c07a7 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Høreapparater"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aktiverer…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Lysstyrken kan ikke justeres, fordi den\n styres af den øverste app"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Lysstyrken kan ikke justeres, fordi den styres af den øverste app"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Roter automatisk"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Roter skærmen automatisk"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokation"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 16bbea1..badceeb 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Eingabe"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hörgerät"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Wird aktiviert…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Die Helligkeit kann nicht angepasst werden, weil sie\n von der obersten App gesteuert wird"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Die Helligkeit kann nicht angepasst werden, weil sie von der obersten App gesteuert wird"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. drehen"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Bildschirm automatisch drehen"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Standort"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 89c2cd2..e41c366 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Είσοδος"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Βοηθήματα ακοής"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Ενεργοποίηση…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Δεν είναι δυνατή η προσαρμογή της φωτεινότητας, επειδή\n ελέγχεται από την εφαρμογή στην κορυφή"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Δεν είναι δυνατή η προσαρμογή της φωτεινότητας, επειδή ελέγχεται από την εφαρμογή στην κορυφή"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Αυτόματη περιστροφή"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Αυτόματη περιστροφή οθόνης"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Τοποθεσία"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index af0703c..ac1f233 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hearing aids"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Turning on…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Can\'t adjust brightness because it\'s being\n controlled by the top app"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Can\'t adjust brightness because it\'s being controlled by the top app"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index f171a78..e9e4e6e 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hearing aids"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Turning on…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Can\'t adjust brightness because it\'s being\n controlled by the top app"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Can\'t adjust brightness because it\'s being controlled by the top app"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index af0703c..ac1f233 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hearing aids"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Turning on…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Can\'t adjust brightness because it\'s being\n controlled by the top app"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Can\'t adjust brightness because it\'s being controlled by the top app"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index af0703c..ac1f233 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hearing aids"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Turning on…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Can\'t adjust brightness because it\'s being\n controlled by the top app"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Can\'t adjust brightness because it\'s being controlled by the top app"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 0e15e45..7b66997 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audífonos"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activando…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"La app superior controla el brillo,\npor lo que no se puede ajustar"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"La app superior controla el brillo,por lo que no se puede ajustar"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Girar la pantalla automáticamente"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicación"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 1670ce6..e3dbc11 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audífonos"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activando…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"No se puede ajustar el brillo porque la aplicación superior lo está\n controlando"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"No se puede ajustar el brillo porque la aplicación superior lo está controlando"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Girar pantalla automáticamente"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicación"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 445b540..f13295a 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Sisend"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Kuuldeaparaadid"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Sisselülitamine …"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Heledust ei saa reguleerida, kuna seda\n juhib ülemine rakendus"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Heledust ei saa reguleerida, kuna seda juhib ülemine rakendus"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. pööramine"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Kuva automaatne pööramine"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Asukoht"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 1f7d697..7c047c0 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Sarrera"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audifonoak"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aktibatzen…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Ezin da doitu argitasuna,\ngaineko aplikazioak kontrolatzen duelako"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Ezin da doitu argitasuna,gaineko aplikazioak kontrolatzen duelako"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Biratze automatikoa"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Biratu pantaila automatikoki"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Kokapena"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index c9392e3..e837ed3 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ورودی"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"سمعک"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"روشن کردن…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"نمیتوان روشنایی را تنظیم کرد زیرا\n برنامه بالایی آن را کنترل میکند"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"نمیتوان روشنایی را تنظیم کرد زیرا برنامه بالایی آن را کنترل میکند"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"چرخش خودکار"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"چرخش خودکار صفحهنمایش"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"مکان"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 96162b2..ab551b3 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -330,7 +330,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Syöttölaite"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Kuulolaitteet"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Otetaan käyttöön…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Kirkkautta ei voi säätää, koska \n ensisijainen sovellus ohjaa sitä"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Kirkkautta ei voi säätää, koska ensisijainen sovellus ohjaa sitä"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automaattinen kääntö"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Käännä näyttöä automaattisesti."</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Sijainti"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 8fd2ed2..bd3a7628 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrée"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Prothèses auditives"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activation en cours…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Impossible de régler la luminosité, car elle est\n contrôlée par l\'appli principale"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Impossible de régler la luminosité, car elle est contrôlée par l\'appli principale"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotation auto"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotation automatique de l\'écran"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localisation"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 89002d4..be21ba9 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrée"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Appareils auditifs"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activation…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Impossible d\'ajuster la luminosité, car celle-ci\n est contrôlée par l\'appli principale"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Impossible d\'ajuster la luminosité, car celle-ci est contrôlée par l\'appli principale"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotation auto"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotation automatique de l\'écran"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localisation"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 4e801ee..462b07a 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audiófonos"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activando…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Non se pode axustar o brillo\n porque o controla a aplicación principal"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Non se pode axustar o brillo porque o controla a aplicación principal"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Xirar automaticamente"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Xirar pantalla automaticamente"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localización"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 08e1398..5b86eab 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ઇનપુટ"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"સાંભળવામાં મદદ આપતા યંત્રો"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ચાલુ કરી રહ્યાં છીએ…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"બ્રાઇટનેસ ગોઠવી શકતા નથી કારણ કે તે\n લોકપ્રિય ઍપ દ્વારા નિયંત્રિત કરવામાં આવી રહી છે"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"બ્રાઇટનેસ ગોઠવી શકતા નથી કારણ કે તે લોકપ્રિય ઍપ દ્વારા નિયંત્રિત કરવામાં આવી રહી છે"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ઑટો રોટેટ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ઑટો રોટેટ સ્ક્રીન"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"લોકેશન"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index d781585..b45d3e9 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"इनपुट"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"कान की मशीनें"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ब्लूटूथ चालू हो रहा है…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"स्क्रीन की रोशनी को एडजस्ट नहीं किया जा सकता, क्योंकि\n इसे टॉप ऐप्लिकेशन कंट्रोल कर रहा है"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"स्क्रीन की रोशनी को एडजस्ट नहीं किया जा सकता, क्योंकि इसे टॉप ऐप्लिकेशन कंट्रोल कर रहा है"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ऑटो-रोटेट"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"स्क्रीन का अपने-आप दिशा बदलना (ऑटो-रोटेट)"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"जगह की जानकारी"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 3a985a9..1b351bd 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Unos"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Slušna pomagala"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Uključivanje…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Svjetlina se ne može prilagoditi jer njome\n upravlja aplikacija pri vrhu"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Svjetlina se ne može prilagoditi jer njome upravlja aplikacija pri vrhu"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatsko zakretanje"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatsko zakretanje zaslona"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 9b17567..ca32832 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Bevitel"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hallókészülék"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Bekapcsolás…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Nem lehet módosítani a fényerőt, mert a felső alkalmazás\n vezérli"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Nem lehet módosítani a fényerőt, mert a felső alkalmazás vezérli"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatikus elforgatás"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatikus képernyőforgatás"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Tartózkodási hely"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 2aa72cc..b619708 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Մուտքագրում"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Լսողական սարք"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Միացում…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Հնարավոր չէ կարգավորել պայծառությունը, քանի որ այն\n կառավարվում է գլխավոր հավելվածի կողմից"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Հնարավոր չէ կարգավորել պայծառությունը, քանի որ այն կառավարվում է գլխավոր հավելվածի կողմից"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Ինքնապտտում"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ավտոմատ պտտել էկրանը"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Տեղորոշում"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index c57f40f..2a24134 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Alat bantu dengar"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Mengaktifkan…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Tidak dapat menyesuaikan kecerahan karena sedang\n dikontrol oleh aplikasi atas"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Tidak dapat menyesuaikan kecerahan karena sedang dikontrol oleh aplikasi atas"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Putar Otomatis"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Putar layar otomatis"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokasi"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index ec284c2..5b33fdd 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Inntak"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Heyrnartæki"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Kveikir…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Ekki er hægt að breyta birtustiginu vegna þess að \n efsta forritið stjórnar því"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Ekki er hægt að breyta birtustiginu vegna þess að efsta forritið stjórnar því"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Sjálfvirkur snúningur"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Snúa skjá sjálfkrafa"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Staðsetning"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 2391fe2..4858cb0 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Ingresso"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Apparecchi acustici"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Attivazione…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Impossibile regolare la luminosità perché è\n controllata dall\'app in primo piano"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Impossibile regolare la luminosità perché è controllata dall\'app in primo piano"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotazione automatica"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotazione automatica dello schermo"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Posizione"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 0d7a620..ad5fe65 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"קלט"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"מכשירי שמיעה"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ההפעלה מתבצעת…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"לא ניתן להתאים את הבהירות כי היא\n נשלטת על ידי האפליקציה העליונה"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"לא ניתן להתאים את הבהירות כי היא נשלטת על ידי האפליקציה העליונה"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"סיבוב אוטומטי"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"סיבוב אוטומטי של המסך"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"מיקום"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 7867633..bc3555a 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"入力"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"補聴器"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ON にしています…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"明るさはトップ アプリによって\n制御されているため、調整できません"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"明るさはトップ アプリによって制御されているため、調整できません"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自動回転"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"画面を自動回転します"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"位置情報"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index bf12aca..5c1b563 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Кіріс"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Есту аппараттары"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Қосылып жатыр…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Жарықтықты реттеу мүмкін емес, себебі ол\n жетекші қолданба арқылы басқарылады."</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Жарықтықты реттеу мүмкін емес, себебі ол жетекші қолданба арқылы басқарылады."</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматты түрде бұру"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматты айналатын экран"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Локация"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index e12fd13..0c2e9c2 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"បញ្ចូល"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ឧបករណ៍ជំនួយការស្ដាប់"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"កំពុងបើក..."</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"មិនអាចកែតម្រូវកម្រិតពន្លឺបានទេ ដោយសារវាកំពុងស្ថិតក្រោម\nការគ្រប់គ្រងរបស់កម្មវិធីខាងលើគេ"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"មិនអាចកែតម្រូវកម្រិតពន្លឺបានទេ ដោយសារវាកំពុងស្ថិតក្រោមការគ្រប់គ្រងរបស់កម្មវិធីខាងលើគេ"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"បង្វិលស្វ័យប្រវត្តិ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"បង្វិលអេក្រង់ស្វ័យប្រវត្តិ"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ទីតាំង"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 3bb4e4d..2136599 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ಇನ್ಪುಟ್"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ಶ್ರವಣ ಸಾಧನಗಳು"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ಆನ್ ಮಾಡಲಾಗುತ್ತಿದೆ..."</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"ಬ್ರೈಟ್ನೆಸ್ ಅನ್ನು ಅಡ್ಜಸ್ಟ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ,\n ಏಕೆಂದರೆ ಅದನ್ನು ಟಾಪ್ ಆ್ಯಪ್ ನಿಯಂತ್ರಿಸುತ್ತಿದೆ"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"ಬ್ರೈಟ್ನೆಸ್ ಅನ್ನು ಅಡ್ಜಸ್ಟ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ, ಏಕೆಂದರೆ ಅದನ್ನು ಟಾಪ್ ಆ್ಯಪ್ ನಿಯಂತ್ರಿಸುತ್ತಿದೆ"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ಸ್ವಯಂ-ತಿರುಗುವಿಕೆ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ಪರದೆಯನ್ನು ಸ್ವಯಂ-ತಿರುಗಿಸಿ"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ಸ್ಥಳ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 33f54b6..9b26e97 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"입력"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"보청기"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"켜는 중..."</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"상위 앱에서 밝기를 제어하고 있으므로\n 밝기를 조절할 수 없습니다."</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"상위 앱에서 밝기를 제어하고 있으므로 밝기를 조절할 수 없습니다."</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"자동 회전"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"화면 자동 회전"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"위치"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index e764bad..c1d5201 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Киргизүү"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Угуу аппараттары"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Күйгүзүлүүдө…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Жарыктыкты тууралоого болбойт, анткени аны\n жогорку колдонмо көзөмөлдөйт"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Жарыктыкты тууралоого болбойт, анткени аны жогорку колдонмо көзөмөлдөйт"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Авто буруу"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Экранды авто буруу"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Жайгашкан жер"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 3236b2e2..702c04f 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ການປ້ອນຂໍ້ມູນ"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ເຄື່ອງຊ່ວຍຟັງ"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ກຳລັງເປີດ..."</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"ບໍ່ສາມາດປັບຄວາມແຈ້ງເນື່ອງຈາກມັນ\n ຖືກຄວບຄຸມໂດຍແອັບຍອດນິຍົມ"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"ບໍ່ສາມາດປັບຄວາມແຈ້ງເນື່ອງຈາກມັນ ຖືກຄວບຄຸມໂດຍແອັບຍອດນິຍົມ"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ໝຸນອັດຕະໂນມັດ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ໝຸນໜ້າຈໍອັດຕະໂນມັດ"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ສະຖານທີ່"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 25208b0..36b2b99 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Įvestis"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Klausos aparatai"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Įjungiama…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Negalima koreguoti ryškumo, nes jį valdo\n viršuje esanti programa"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Negalima koreguoti ryškumo, nes jį valdo viršuje esanti programa"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatinis pasukimas"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatiškai sukti ekraną"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Vietovė"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index bf1ef96..dcb405b 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Ievade"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Dzirdes aparāti"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Notiek ieslēgšana…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Nevar mainīt spilgtumu, jo to kontrolē\n aktīvā lietotne."</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Nevar mainīt spilgtumu, jo to kontrolē aktīvā lietotne."</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automātiska pagriešana"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automātiska ekrāna pagriešana"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Atrašanās vieta"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index e34d9d8..10435b8 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Влез"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слушни помагала"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Се вклучува…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Не може да се приспособи осветленоста бидејќи е\n контролирана од горната апликација"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Не може да се приспособи осветленоста бидејќи е контролирана од горната апликација"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматско ротирање"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматско ротирање на екранот"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Локација"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 226822a..489f1ad 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ഇൻപുട്ട്"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ശ്രവണ സഹായികൾ"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ഓണാക്കുന്നു…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"തെളിച്ചം അഡ്ജസ്റ്റ് ചെയ്യാനാകില്ല, അത്\n നിയന്ത്രിക്കുന്നത് ഏറ്റവും മുകളിലുള്ള ആപ്പാണ്"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"തെളിച്ചം അഡ്ജസ്റ്റ് ചെയ്യാനാകില്ല, അത് നിയന്ത്രിക്കുന്നത് ഏറ്റവും മുകളിലുള്ള ആപ്പാണ്"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"സ്ക്രീൻ സ്വയമേവ തിരിയൽ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"സ്ക്രീൻ സ്വയമേവ തിരിക്കുക"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ലൊക്കേഷൻ"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 06b62e9..776f1e9 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Оролт"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Сонсголын төхөөрөмж"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Асааж байна…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Гэрэлтүүлгийг\nдавуу эрхтэй аппаас хянаж байгаа тул тохируулах боломжгүй"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Гэрэлтүүлгийгдавуу эрхтэй аппаас хянаж байгаа тул тохируулах боломжгүй"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматаар эргэх"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Дэлгэцийг автоматаар эргүүлэх"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Байршил"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 4c2fd29..5691410 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"इनपुट"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"श्रवणयंत्रे"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"सुरू करत आहे…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"ब्राइटनेस ॲडजस्ट करू शकत नाही, कारण तो\n टॉप ॲपद्वारे नियंत्रित केला जात आहे"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"ब्राइटनेस ॲडजस्ट करू शकत नाही, कारण तो टॉप ॲपद्वारे नियंत्रित केला जात आहे"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ऑटो-रोटेट"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ऑटो-रोटेट स्क्रीन"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"स्थान"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 93f25ff..997e4a0 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Alat bantu pendengaran"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Menghidupkan…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Tidak dapat melaraskan kecerahan kerana peranti\n dikawal oleh apl popular"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Tidak dapat melaraskan kecerahan kerana peranti dikawal oleh apl popular"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autoputar"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Autoputar skrin"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokasi"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index bed07ef..e4f7154 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"အဝင်"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"နားကြားကိရိယာ"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ဖွင့်နေသည်…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"၎င်းကို ထိပ်ဆုံးရှိအက်ပ်က\n ထိန်းချုပ်နေသဖြင့် တောက်ပမှုကို ပြင်ဆင်၍မရပါ"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"၎င်းကို ထိပ်ဆုံးရှိအက်ပ်က ထိန်းချုပ်နေသဖြင့် တောက်ပမှုကို ပြင်ဆင်၍မရပါ"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"အော်တို-လည်"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"မျက်နှာပြင်အား အလိုအလျောက်လှည့်ခြင်း"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"တည်နေရာ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 7e87efc..6ee6cc2 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Innenhet"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Høreapparater"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Slår på …"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Kan ikke justere lysstyrken, fordi den\n kontrolleres av appen på toppen"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Kan ikke justere lysstyrken, fordi den kontrolleres av appen på toppen"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotér automatisk"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotér skjermen automatisk"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Sted"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index cc07b7f..90a1923 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"इनपुट"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"हियरिङ डिभाइसहरू"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"सक्रिय गर्दै…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"टप एपले चमक नियन्त्रण गरिरहेकाले\n चमक मिलाउन मिल्दैन"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"टप एपले चमक नियन्त्रण गरिरहेकाले चमक मिलाउन मिल्दैन"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"अटो रोटेट"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"स्क्रिन स्वतःघुम्ने"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"लोकेसन"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index b778cec..ee0ef9d 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Invoer"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hoortoestellen"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aanzetten…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Kan de helderheid niet aanpassen omdat deze wordt\n beheerd door de bovenste app"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Kan de helderheid niet aanpassen omdat deze wordt beheerd door de bovenste app"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatisch draaien"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Scherm automatisch draaien"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Locatie"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 0ef622b..d5ac685 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ଇନପୁଟ୍"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ଶ୍ରବଣ ଯନ୍ତ୍ର"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ଅନ୍ ହେଉଛି…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"ଟପ ଆପ ଦ୍ୱାରା ଉଜ୍ଜ୍ୱଳତା ନିୟନ୍ତ୍ରିତ\nହେଉଥିବା ଯୋଗୁଁ ଏହାକୁ ଆଡଜଷ୍ଟ କରିପାରିବେ ନାହିଁ"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"ଟପ ଆପ ଦ୍ୱାରା ଉଜ୍ଜ୍ୱଳତା ନିୟନ୍ତ୍ରିତହେଉଥିବା ଯୋଗୁଁ ଏହାକୁ ଆଡଜଷ୍ଟ କରିପାରିବେ ନାହିଁ"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ଅଟୋ-ରୋଟେଟ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ଅଟୋ-ରୋଟେଟ ସ୍କ୍ରିନ"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ଲୋକେସନ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 4be78a3..2499518 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ਇਨਪੁੱਟ"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ਸੁਣਨ ਦੇ ਸਾਧਨ"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ਚਾਲੂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"ਚਮਕ ਨੂੰ ਵਿਵਸਥਿਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ ਕਿਉਂਕਿ ਇਹ ਪਹਿਲਾਂ ਚੱਲ ਰਹੀ ਐਪ ਵੱਲੋਂ ਕੰਟਰੋਲ ਕਰਨ ਕਰਕੇ \n ਹੋ ਰਿਹਾ ਹੈ"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"ਚਮਕ ਨੂੰ ਵਿਵਸਥਿਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ ਕਿਉਂਕਿ ਇਹ ਪਹਿਲਾਂ ਚੱਲ ਰਹੀ ਐਪ ਵੱਲੋਂ ਕੰਟਰੋਲ ਕਰਨ ਕਰਕੇ ਹੋ ਰਿਹਾ ਹੈ"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ਸਵੈ-ਘੁਮਾਓ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ਸਕ੍ਰੀਨ ਨੂੰ ਆਪਣੇ ਆਪ ਘੁੰਮਾਓ"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ਟਿਕਾਣਾ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index d358b9f..71b5ace 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Wejście"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparaty słuchowe"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Włączam…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Nie można dostosować jasności, ponieważ jest ona\nkontrolowana przez aplikację na pierwszym planie"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Nie można dostosować jasności, ponieważ jest onakontrolowana przez aplikację na pierwszym planie"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autoobracanie"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Autoobracanie ekranu"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokalizacja"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 3a7d1f2..2a8aabe 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparelhos auditivos"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Ativando…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Não é possível ajustar o brilho, porque ele está sendo\n controlado pelo app principal"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Não é possível ajustar o brilho, porque ele está sendo controlado pelo app principal"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Giro automático da tela"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index ff0ba36..bb68610 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparelhos auditivos"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"A ativar..."</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Não é possível ajustar o brilho porque está a ser\n controlado pela app principal"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Não é possível ajustar o brilho porque está a ser controlado pela app principal"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotação auto."</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rodar o ecrã automaticamente"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 3a7d1f2..2a8aabe 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparelhos auditivos"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Ativando…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Não é possível ajustar o brilho, porque ele está sendo\n controlado pelo app principal"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Não é possível ajustar o brilho, porque ele está sendo controlado pelo app principal"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Giro automático da tela"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 2417c84..6221d49 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Intrare"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparate auditive"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Se activează..."</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Nu se poate ajusta luminozitatea deoarece este\n controlată de aplicația de top"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Nu se poate ajusta luminozitatea deoarece este controlată de aplicația de top"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotire automată"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotirea automată a ecranului"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Locație"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index f6968d5..4c14616 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Устройство ввода"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слуховые аппараты"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Включение…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Невозможно изменить яркость,\nтак как она регулируется общими настройками."</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Невозможно изменить яркость,так как она регулируется общими настройками."</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоповорот"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоповорот экрана"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Геолокация"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index eb816b8..fb8ce18 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ආදානය"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ශ්රවණාධාරක"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ක්රියාත්මක කරමින්…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"ඉහළ යෙදුම මඟින් එය පාලනය වන නිසා\nදීප්තිය ගැළපුම් කළ නොහැක"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"ඉහළ යෙදුම මඟින් එය පාලනය වන නිසාදීප්තිය ගැළපුම් කළ නොහැක"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ස්වයංක්රීය කරකැවීම"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ස්වයංක්රීයව-භ්රමණය වන තිරය"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ස්ථානය"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index fcb281d..2dba462 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Vstup"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Načúvadlá"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Zapína sa…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Jas sa nedá upraviť, pretože ho \n ovláda horná aplikácia"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Jas sa nedá upraviť, pretože ho ovláda horná aplikácia"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatické otáčanie"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatické otáčanie obrazovky"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Poloha"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index e999065..dd86710 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Vhodna naprava"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Slušni aparati"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Vklapljanje …"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Svetlosti ni mogoče prilagoditi, ker jo\n nadzoruje aplikacija na vrhu"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Svetlosti ni mogoče prilagoditi, ker jo nadzoruje aplikacija na vrhu"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Samodejno sukanje"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Samodejno sukanje zaslona"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 7e2b698..2532c03 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Hyrja"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparatet e dëgjimit"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Po aktivizohet…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Ndriçimi nuk mund të rregullohet pasi\n po kontrollohet nga aplikacioni i sipërm"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Ndriçimi nuk mund të rregullohet pasi po kontrollohet nga aplikacioni i sipërm"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rrotullim automatik"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rrotullimi automatik i ekranit"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Vendndodhja"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 1e44e0c..3ccad52 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Унос"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слушни апарати"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Укључује се..."</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Не можете да прилагодите осветљеност јер је\n контролише апликација у врху"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Не можете да прилагодите осветљеност јер је контролише апликација у врху"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Аутоматска ротација"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Аутоматско ротирање екрана"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Локација"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index d613922..cab91b1 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Ingång"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hörapparater"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aktiverar …"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Det går inte att justera ljusstyrkan eftersom den\n styrs av den översta appen"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Det går inte att justera ljusstyrkan eftersom den styrs av den översta appen"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotera automatiskt"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotera skärmen automatiskt"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Plats"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 33d8511..6cb3abf 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Vifaa vya kuingiza sauti"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Visaidizi vya kusikia"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Inawasha..."</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Imeshindwa kurekebisha mwangaza kwa sababu\n inadhibitiwa na programu inayotumika"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Imeshindwa kurekebisha mwangaza kwa sababu inadhibitiwa na programu inayotumika"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Zungusha kiotomatiki"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Skrini ijizungushe kiotomatiki"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Mahali"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index aa312c7..32fa794 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"உள்ளீடு"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"செவித்துணைக் கருவி"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ஆன் செய்கிறது…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"சிறந்த ஆப்ஸால் ஒளிர்வு கட்டுப்படுத்தப்படுவதால்\n இதைச் சரிசெய்ய முடியவில்லை"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"சிறந்த ஆப்ஸால் ஒளிர்வு கட்டுப்படுத்தப்படுவதால் இதைச் சரிசெய்ய முடியவில்லை"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"தானாகச் சுழற்று"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"திரையைத் தானாகச் சுழற்று"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"இருப்பிடம்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index fc5361b..f3084a9 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ఇన్పుట్"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"వినికిడి పరికరాలు"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ఆన్ చేస్తోంది…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"ఇది టాప్ యాప్ ద్వారా\n కంట్రోల్ చేయబడుతున్నందున బ్రైట్నెస్ను సర్దుబాటు చేయడం సాధ్యం కాదు"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"ఇది టాప్ యాప్ ద్వారా కంట్రోల్ చేయబడుతున్నందున బ్రైట్నెస్ను సర్దుబాటు చేయడం సాధ్యం కాదు"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ఆటో-రొటేట్"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"స్క్రీన్ ఆటో-రొటేట్"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"లొకేషన్"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 873635d..3fac78f 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"อินพุต"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"เครื่องช่วยฟัง"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"กำลังเปิด..."</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"ปรับความสว่างไม่ได้เนื่องจาก\nควบคุมโดยแอปที่อยู่ด้านบน"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"ปรับความสว่างไม่ได้เนื่องจากควบคุมโดยแอปที่อยู่ด้านบน"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"หมุนอัตโนมัติ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"หมุนหน้าจออัตโนมัติ"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"ตำแหน่ง"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index e6aa410..8e87e84 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Mga hearing aid"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Ino-on…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Hindi ma-adjust ang liwanag dahil\n kinokontrol ito ng nangingibabaw na app"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Hindi ma-adjust ang liwanag dahil kinokontrol ito ng nangingibabaw na app"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"I-auto rotate"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Awtomatikong i-rotate ang screen"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Lokasyon"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 05fc1f2..474c53f 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Giriş"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"İşitme cihazları"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Açılıyor…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Parlaklık ayarlanamıyor, çünkü bu özellik\n en üstteki uygulama tarafından kontrol ediliyor"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Parlaklık ayarlanamıyor, çünkü bu özellik en üstteki uygulama tarafından kontrol ediliyor"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Otomatik döndür"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ekranı otomatik döndür"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Konum"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 0b0d6dc..27c2e53 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Джерело сигналу"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слухові апарати"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Увімкнення…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Не вдається змінити яскравість, оскільки\n нею керує основний додаток"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Не вдається змінити яскравість, оскільки нею керує основний додаток"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автообертання"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматично обертати екран"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Геодані"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index ab4dd77..28572b9 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ان پٹ"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"سماعتی آلات"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"آن ہو رہا ہے…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"چمک کو ایڈجسٹ نہیں کیا جا سکتا کیونکہ اسے سرفہرست ایپ کے ذریعے \n کنٹرول کیا جا رہا ہے"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"چمک کو ایڈجسٹ نہیں کیا جا سکتا کیونکہ اسے سرفہرست ایپ کے ذریعے کنٹرول کیا جا رہا ہے"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"خود کار طور پر گھمائیں"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"اسکرین کو خود کار طور پر گھمائیں"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"مقام"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 55caf25..54c1412d 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Kirish"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Eshitish moslamalari"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Yoqilmoqda…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Yorqinlik umumiy sozlamalar orqali boshqariladi.\nUni moslash imkonsiz"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Yorqinlik umumiy sozlamalar orqali boshqariladi.Uni moslash imkonsiz"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Avto-burilish"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ekranning avtomatik burilishi"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Joylashuv"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 35fe2f2..4414e09 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Thiết bị đầu vào"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Thiết bị trợ thính"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Đang bật…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Không điều chỉnh được độ sáng vì độ sáng đang được\n ứng dụng trên cùng điều khiển"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Không điều chỉnh được độ sáng vì độ sáng đang được ứng dụng trên cùng điều khiển"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Tự động xoay"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Tự động xoay màn hình"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Vị trí"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 530017c..f45ca2d 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"输入"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"助听器"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"正在开启…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"亮度无法调整,因为它正在被\n顶层应用控制"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"亮度无法调整,因为它正在被顶层应用控制"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自动屏幕旋转"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"自动旋转屏幕"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"位置信息"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 496cbdb..ebf07ee 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"輸入"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"助聽器"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"正在開啟…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"無法調整亮度,因為\n目前是由上層應用程式控制亮度"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"無法調整亮度,因為目前是由上層應用程式控制亮度"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自動旋轉"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"自動旋轉螢幕"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"位置"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index c63473f..2e2a930 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -328,7 +328,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"輸入"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"助聽器"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"開啟中…"</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"無法調整亮度,因為\n目前是由上層應用程式控制亮度"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"無法調整亮度,因為目前是由上層應用程式控制亮度"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自動旋轉"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"自動旋轉螢幕"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"定位"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 2aad788..818b6e3 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -330,7 +330,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Okokufaka"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Imishini yendlebe"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Iyavula..."</string>
- <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Ayikwazi ukulungisa ukukhanya ngoba ilawulwa\n yi-app ephezulu"</string>
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Ayikwazi ukulungisa ukukhanya ngoba ilawulwa yi-app ephezulu"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Ukuphenduka okuzenzakalelayo"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Phendula iskrini ngokuzenzakalela"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"Indawo"</string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 17a89b3..640e1fa 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -491,6 +491,9 @@
<!-- some constraints use a negative margin. must be aligned with overlay_border_width, above;
overlay_border_width_neg = overlay_border_width * -1 -->
<dimen name="overlay_border_width_neg">-4dp</dimen>
+ <dimen name="overlay_shade_panel_shape_radius">
+ @dimen/aux_spacing_overlay_panel_shape_radius
+ </dimen>
<dimen name="clipboard_preview_size">@dimen/overlay_x_scale</dimen>
<dimen name="clipboard_overlay_min_font">10sp</dimen>
@@ -2215,4 +2218,8 @@
<dimen name="rear_display_progress_width">231dp</dimen>
<!-- Rear display mode end -->
+ <!-- Spacing attributes to overwrite -->
+ <dimen name="aux_spacing_overlay_panel_shape_radius">46dp</dimen>
+ <!-- Spacing attributes to overwrite end -->
+
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3fc46ed..e97919e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -802,7 +802,7 @@
<!-- QuickSettings: Bluetooth secondary label shown when bluetooth is being enabled [CHAR LIMIT=NONE] -->
<string name="quick_settings_bluetooth_secondary_label_transient">Turning on…</string>
<!-- QuickSettings: Brightness [CHAR LIMIT=NONE] -->
- <string name="quick_settings_brightness_unable_adjust_msg">Can\'t adjust brightness because it\'s being\n controlled by the top app</string>
+ <string name="quick_settings_brightness_unable_adjust_msg">Can\'t adjust brightness because it\'s being controlled by the top app</string>
<!-- QuickSettings: Rotation Unlocked [CHAR LIMIT=NONE] -->
<string name="quick_settings_rotation_unlocked_label">Auto-rotate</string>
<!-- Accessibility label for Auto-ratate QuickSettings tile [CHAR LIMIT=NONE] -->
@@ -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/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 9ebb15f..c822439 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -19,6 +19,7 @@
import static android.app.StatusBarManager.NAVBAR_BACK_DISMISS_IME;
import static android.app.StatusBarManager.NAVBAR_IME_SWITCHER_BUTTON_VISIBLE;
import static android.app.StatusBarManager.NAVBAR_IME_VISIBLE;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import android.annotation.TargetApi;
import android.app.StatusBarManager.NavbarFlags;
@@ -35,6 +36,8 @@
import android.view.Surface;
import android.view.WindowManager;
+import com.android.systemui.shared.recents.model.Task;
+
/* Common code */
public class Utilities {
@@ -165,4 +168,10 @@
float densityRatio = (float) densityDpi / DisplayMetrics.DENSITY_DEFAULT;
return (size / densityRatio);
}
+
+ /** Whether a task is in freeform mode. */
+ public static boolean isFreeformTask(Task task) {
+ return task != null && task.getKey() != null
+ && task.getKey().windowingMode == WINDOWING_MODE_FREEFORM;
+ }
}
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..487d1ce 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,10 +324,23 @@
}
/**
+ * 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) {
return info.configuration.windowConfiguration.getActivityType()
== WindowConfiguration.ACTIVITY_TYPE_HOME;
}
+
+ public boolean isRunningInTestHarness() {
+ return ActivityManager.isRunningInTestHarness()
+ || ActivityManager.isRunningInUserTestHarness();
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
index 5e36539..a7bb11e 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
@@ -156,7 +156,7 @@
mMainExecutor.execute(() -> mView.updateEmergencyCallButton(
/* isInCall= */ isInCall,
/* hasTelephonyRadio= */ getContext().getPackageManager()
- .hasSystemFeature(PackageManager.FEATURE_TELEPHONY),
+ .hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING),
/* simLocked= */ mKeyguardUpdateMonitor.isSimPinVoiceSecure(),
/* isSecure= */ isSecure));
});
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/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 97ad2d7..6b1248b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -247,14 +247,13 @@
@Provides
@SysUISingleton
static BlurConfig provideBlurConfig(@Main Resources resources) {
- int minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius);
int maxBlurRadius =
Flags.notificationShadeBlur() || Flags.bouncerUiRevamp()
|| Flags.glanceableHubBlurredBackground()
? resources.getDimensionPixelSize(R.dimen.max_shade_window_blur_radius)
: resources.getDimensionPixelSize(R.dimen.max_window_blur_radius);
- return new BlurConfig(minBlurRadius, maxBlurRadius);
+ return new BlurConfig(0.0f, maxBlurRadius);
}
/** */
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/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
index b64cb3d..f06c4cc 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
@@ -58,6 +58,7 @@
import com.android.systemui.statusbar.CrossFadeHelper
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.featurepods.popups.StatusBarPopupChips
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -229,7 +230,7 @@
else result.setIntersect(animationStartClipping, targetClipping)
}
- private val mediaHosts = arrayOfNulls<MediaHost>(LOCATION_COMMUNAL_HUB + 1)
+ private val mediaHosts = arrayOfNulls<MediaHost>(LOCATION_STATUS_BAR_POPUP + 1)
/**
* The last location where this view was at before going to the desired location. This is useful
@@ -374,6 +375,15 @@
}
}
+ /** Is the Media Control StatusBarPopup showing */
+ var isMediaControlPopupShowing: Boolean = false
+ set(value) {
+ if (field != value && StatusBarPopupChips.isEnabled) {
+ field = value
+ updateDesiredLocation(forceNoAnimation = true)
+ }
+ }
+
/** Are location changes currently blocked? */
private val blockLocationChanges: Boolean
get() {
@@ -1225,6 +1235,7 @@
// Keep the current location until we're allowed to again
return desiredLocation
}
+
val onLockscreen =
(!bypassController.bypassEnabled && (statusbarState == StatusBarState.KEYGUARD))
@@ -1234,6 +1245,8 @@
(onCommunalNotDreaming && qsExpansion == 0.0f) || onCommunalDreamingAndShadeExpanding
val location =
when {
+ isMediaControlPopupShowing && StatusBarPopupChips.isEnabled ->
+ LOCATION_STATUS_BAR_POPUP
dreamOverlayActive && dreamMediaComplicationActive -> LOCATION_DREAM_OVERLAY
onCommunal -> LOCATION_COMMUNAL_HUB
(qsExpansion > 0.0f || inSplitShade) && !onLockscreen -> LOCATION_QS
@@ -1377,6 +1390,9 @@
/** Attached to a view in the communal UI grid */
const val LOCATION_COMMUNAL_HUB = 4
+ /** Attached to a popup that is shown with a media control chip in the status bar */
+ const val LOCATION_STATUS_BAR_POPUP = 5
+
/** Attached at the root of the hierarchy in an overlay */
const val IN_OVERLAY = -1000
@@ -1422,6 +1438,7 @@
MediaHierarchyManager.LOCATION_LOCKSCREEN,
MediaHierarchyManager.LOCATION_DREAM_OVERLAY,
MediaHierarchyManager.LOCATION_COMMUNAL_HUB,
+ MediaHierarchyManager.LOCATION_STATUS_BAR_POPUP,
MediaHierarchyManager.LOCATION_UNKNOWN,
],
)
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/media/controls/util/MediaUiEventLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt
index 09f973c..f0da226 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt
@@ -45,7 +45,7 @@
uid: Int,
packageName: String,
instanceId: InstanceId,
- playbackLocation: Int
+ playbackLocation: Int,
) {
val event =
when (playbackLocation) {
@@ -61,7 +61,7 @@
uid: Int,
packageName: String,
instanceId: InstanceId,
- playbackLocation: Int
+ playbackLocation: Int,
) {
val event =
when (playbackLocation) {
@@ -112,7 +112,7 @@
MediaUiEvent.OPEN_SETTINGS_LONG_PRESS,
uid,
packageName,
- instanceId
+ instanceId,
)
}
@@ -156,6 +156,8 @@
MediaUiEvent.MEDIA_CAROUSEL_LOCATION_DREAM
MediaHierarchyManager.LOCATION_COMMUNAL_HUB ->
MediaUiEvent.MEDIA_CAROUSEL_LOCATION_COMMUNAL
+ MediaHierarchyManager.LOCATION_STATUS_BAR_POPUP ->
+ MediaUiEvent.MEDIA_CAROUSEL_LOCATION_STATUS_BAR_POPUP
else -> throw IllegalArgumentException("Unknown media carousel location $location")
}
logger.log(event)
@@ -166,7 +168,7 @@
MediaUiEvent.MEDIA_RECOMMENDATION_ADDED,
0,
packageName,
- instanceId
+ instanceId,
)
}
@@ -175,7 +177,7 @@
MediaUiEvent.MEDIA_RECOMMENDATION_REMOVED,
0,
packageName,
- instanceId
+ instanceId,
)
}
@@ -184,7 +186,7 @@
MediaUiEvent.MEDIA_RECOMMENDATION_ACTIVATED,
uid,
packageName,
- instanceId
+ instanceId,
)
}
@@ -194,7 +196,7 @@
0,
packageName,
instanceId,
- position
+ position,
)
}
@@ -203,7 +205,7 @@
MediaUiEvent.MEDIA_RECOMMENDATION_CARD_TAP,
0,
packageName,
- instanceId
+ instanceId,
)
}
@@ -212,7 +214,7 @@
MediaUiEvent.MEDIA_OPEN_BROADCAST_DIALOG,
uid,
packageName,
- instanceId
+ instanceId,
)
}
@@ -221,7 +223,7 @@
MediaUiEvent.MEDIA_CAROUSEL_SINGLE_PLAYER,
uid,
packageName,
- instanceId
+ instanceId,
)
}
@@ -230,7 +232,7 @@
MediaUiEvent.MEDIA_CAROUSEL_MULTIPLE_PLAYERS,
uid,
packageName,
- instanceId
+ instanceId,
)
}
}
@@ -280,6 +282,8 @@
MEDIA_CAROUSEL_LOCATION_DREAM(1040),
@UiEvent(doc = "The media carousel moved to the communal hub UI")
MEDIA_CAROUSEL_LOCATION_COMMUNAL(1520),
+ @UiEvent(doc = "The media carousel moved to the status bar popup")
+ MEDIA_CAROUSEL_LOCATION_STATUS_BAR_POPUP(2170),
@UiEvent(doc = "A media recommendation card was added to the media carousel")
MEDIA_RECOMMENDATION_ADDED(1041),
@UiEvent(doc = "A media recommendation card was removed from the media carousel")
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index 45a3a8c..662a9c7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -49,6 +49,7 @@
String KEYGUARD = "media_keyguard";
String DREAM = "dream";
String COMMUNAL_HUB = "communal_Hub";
+ String POPUP = "popup";
/** */
@Provides
@@ -102,7 +103,26 @@
@Provides
@SysUISingleton
@Named(COMMUNAL_HUB)
- static MediaHost providesCommunalMediaHost(MediaHost.MediaHostStateHolder stateHolder,
+ static MediaHost providesCommunalMediaHost(
+ MediaHost.MediaHostStateHolder stateHolder,
+ MediaHierarchyManager hierarchyManager,
+ MediaDataManager dataManager,
+ MediaHostStatesManager statesManager,
+ MediaCarouselController carouselController,
+ MediaCarouselControllerLogger logger) {
+ return new MediaHost(
+ stateHolder,
+ hierarchyManager,
+ dataManager,
+ statesManager,
+ carouselController,
+ logger);
+ }
+
+ @Provides
+ @SysUISingleton
+ @Named(POPUP)
+ static MediaHost providesPopupMediaHost(MediaHost.MediaHostStateHolder stateHolder,
MediaHierarchyManager hierarchyManager, MediaDataManager dataManager,
MediaHostStatesManager statesManager, MediaCarouselController carouselController,
MediaCarouselControllerLogger logger) {
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/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index 6ad8bae..5930a24 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -306,6 +306,7 @@
sceneState,
viewModel.containerViewModel.editModeViewModel.isEditing,
snapshotFlow { viewModel.expansionState }.map { it.progress },
+ snapshotFlow { viewModel.isQSExpandingOrCollapsing },
)
}
@@ -537,6 +538,10 @@
return qqsVisible.value
}
+ override fun setQSExpandingOrCollapsing(isQSExpandingOrCollapsing: Boolean) {
+ viewModel.isQSExpandingOrCollapsing = isQSExpandingOrCollapsing
+ }
+
private fun setListenerCollections() {
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
@@ -877,6 +882,7 @@
state: MutableSceneTransitionLayoutState,
editMode: Flow<Boolean>,
expansion: Flow<Float>,
+ isQSExpandingOrCollapsing: Flow<Boolean>,
) {
coroutineScope {
val animationScope = this
@@ -888,31 +894,46 @@
currentTransition = null
}
- editMode.combine(expansion, ::Pair).collectLatest { (editMode, progress) ->
+ var lastValidProgress = 0f
+ combine(editMode, expansion, isQSExpandingOrCollapsing, ::Triple).collectLatest {
+ (editMode, progress, isQSExpandingOrCollapsing) ->
if (editMode && state.currentScene != SceneKeys.EditMode) {
state.setTargetScene(SceneKeys.EditMode, animationScope)?.second?.join()
} else if (!editMode && state.currentScene == SceneKeys.EditMode) {
state.setTargetScene(SceneKeys.QuickSettings, animationScope)?.second?.join()
}
- if (!editMode) {
- when (progress) {
- 0f -> snapTo(QuickQuickSettings)
- 1f -> snapTo(QuickSettings)
- else -> {
- val transition = currentTransition
- if (transition != null) {
- transition.progress = progress
- return@collectLatest
- }
- val newTransition =
- ExpansionTransition(progress).also { currentTransition = it }
- state.startTransitionImmediately(
- animationScope = animationScope,
- transition = newTransition,
- )
+ if (!editMode) {
+ if (!isQSExpandingOrCollapsing) {
+ if (progress == 0f) {
+ snapTo(QuickQuickSettings)
+ return@collectLatest
+ }
+
+ if (progress == 1f) {
+ snapTo(QuickSettings)
+ return@collectLatest
}
}
+
+ var progress = progress
+ if (progress >= 0f || progress <= 1f) {
+ lastValidProgress = progress
+ } else {
+ progress = lastValidProgress
+ }
+
+ val transition = currentTransition
+ if (transition != null) {
+ transition.progress = progress
+ return@collectLatest
+ }
+
+ val newTransition = ExpansionTransition(progress).also { currentTransition = it }
+ state.startTransitionImmediately(
+ animationScope = animationScope,
+ transition = newTransition,
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
index ff84479..b829bbc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
@@ -306,6 +306,8 @@
val animateTilesExpansion: Boolean
get() = inFirstPage && !mediaSuddenlyAppearingInLandscape
+ var isQSExpandingOrCollapsing by mutableStateOf(false)
+
private val inFirstPage: Boolean
get() = inFirstPageViewModel.inFirstPage
@@ -539,6 +541,7 @@
println("proposedTranslation", proposedTranslation)
println("expansionState", expansionState)
println("forceQS", forceQs)
+ println("isShadeExpandingOrCollapsing", isQSExpandingOrCollapsing)
printSection("Derived values") {
println("headerTranslation", headerTranslation)
println("translationScaleY", translationScaleY)
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/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index fd5861f..7daac45 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -61,6 +61,8 @@
import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.BluetoothController;
+import dagger.Lazy;
+
import kotlinx.coroutines.Job;
import java.util.List;
@@ -84,7 +86,7 @@
private final Executor mExecutor;
- private final BluetoothDetailsContentViewModel mDetailsContentViewModel;
+ private final Lazy<BluetoothDetailsContentViewModel> mDetailsContentViewModel;
private final FeatureFlags mFeatureFlags;
@Nullable
@@ -104,7 +106,7 @@
QSLogger qsLogger,
BluetoothController bluetoothController,
FeatureFlags featureFlags,
- BluetoothDetailsContentViewModel detailsContentViewModel
+ Lazy<BluetoothDetailsContentViewModel> detailsContentViewModel
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
@@ -133,7 +135,7 @@
callback.accept(new BluetoothDetailsViewModel(() -> {
longClick(null);
return null;
- }, mDetailsContentViewModel))
+ }, mDetailsContentViewModel.get()))
);
return true;
}
@@ -158,7 +160,7 @@
private void handleClickEvent(@Nullable Expandable expandable) {
if (mFeatureFlags.isEnabled(Flags.BLUETOOTH_QS_TILE_DIALOG)) {
- mDetailsContentViewModel.showDetailsContent(expandable, /* view= */ null);
+ mDetailsContentViewModel.get().showDetailsContent(expandable, /* view= */ null);
} else {
// Secondary clicks are header clicks, just toggle.
toggleBluetooth();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 30c2adf..c60e3da 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -54,6 +54,7 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.res.R;
+import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor;
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.statusbar.connectivity.WifiIndicators;
@@ -89,6 +90,7 @@
private final Callback mCallback = new Callback();
private final TileJavaAdapter mJavaAdapter;
private final FeatureFlags mFeatureFlags;
+ private final ShadeDialogContextInteractor mShadeDialogContextInteractor;
private boolean mCastTransportAllowed;
private boolean mHotspotConnected;
@@ -110,7 +112,8 @@
DialogTransitionAnimator dialogTransitionAnimator,
ConnectivityRepository connectivityRepository,
TileJavaAdapter javaAdapter,
- FeatureFlags featureFlags
+ FeatureFlags featureFlags,
+ ShadeDialogContextInteractor shadeDialogContextInteractor
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
@@ -120,6 +123,7 @@
mDialogTransitionAnimator = dialogTransitionAnimator;
mJavaAdapter = javaAdapter;
mFeatureFlags = featureFlags;
+ mShadeDialogContextInteractor = shadeDialogContextInteractor;
mController.observe(this, mCallback);
mKeyguard.observe(this, mCallback);
if (!mFeatureFlags.isEnabled(SIGNAL_CALLBACK_DEPRECATION)) {
@@ -220,7 +224,7 @@
mUiHandler.post(() -> {
final DialogHolder holder = new DialogHolder();
final Dialog dialog = MediaRouteDialogPresenter.createDialog(
- mContext,
+ mShadeDialogContextInteractor.getContext(),
ROUTE_TYPE_REMOTE_DISPLAY,
v -> {
ActivityTransitionAnimator.Controller controller =
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
index d058372..34b3324 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
@@ -105,15 +105,14 @@
import dalvik.annotation.optimization.NeverCompile;
-import dagger.Lazy;
-
-import kotlin.Unit;
-
import java.io.PrintWriter;
import javax.inject.Inject;
import javax.inject.Provider;
+import dagger.Lazy;
+import kotlin.Unit;
+
/** Handles QuickSettings touch handling, expansion and animation state. */
@SysUISingleton
public class QuickSettingsControllerImpl implements QuickSettingsController, Dumpable {
@@ -2366,8 +2365,16 @@
return;
}
if (startTracing) {
+ if (mQs != null) {
+ mQs.setQSExpandingOrCollapsing(true);
+ }
+
monitor.begin(mPanelView, Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
} else {
+ if (mQs != null) {
+ mQs.setQSExpandingOrCollapsing(false);
+ }
+
if (wasCancelled) {
monitor.cancel(Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
} else {
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/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index f30043e..f45971b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -45,7 +45,7 @@
private val crossWindowBlurListeners: CrossWindowBlurListeners,
dumpManager: DumpManager
) : Dumpable {
- val minBlurRadius = blurConfig.minBlurRadiusPx
+ val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius).toFloat();
val maxBlurRadius = if (Flags.notificationShadeBlur()) {
blurConfig.maxBlurRadiusPx
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
index c1b8d9d..6ebe024 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
+import static android.app.Flags.notificationsRedesignTemplates;
+
import android.app.Flags;
import android.app.Notification;
import android.graphics.drawable.Drawable;
@@ -427,7 +429,8 @@
@Override
public void apply(View parent, View view, boolean apply, boolean reset) {
- if (reset && parent instanceof ConversationLayout) {
+ if (!notificationsRedesignTemplates()
+ && reset && parent instanceof ConversationLayout) {
ConversationLayout layout = (ConversationLayout) parent;
apply = layout.shouldHideAppName();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index f06565f..32da6ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -24,7 +24,7 @@
import static android.os.Flags.allowPrivateProfile;
import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_NULL;
-import static android.provider.Settings.Secure.REDACT_OTP_NOTIFICATION_IMMEDIATELY;
+import static android.provider.Settings.Secure.OTP_NOTIFICATION_REDACTION_LOCK_TIME;
import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS;
import static android.provider.Settings.Secure.REDACT_OTP_NOTIFICATION_WHILE_CONNECTED_TO_WIFI;
@@ -124,10 +124,10 @@
private static final Uri REDACT_OTP_ON_WIFI =
Settings.Secure.getUriFor(REDACT_OTP_NOTIFICATION_WHILE_CONNECTED_TO_WIFI);
- private static final Uri REDACT_OTP_IMMEDIATELY =
- Settings.Secure.getUriFor(REDACT_OTP_NOTIFICATION_IMMEDIATELY);
+ private static final Uri OTP_REDACTION_LOCK_TIME =
+ Settings.Secure.getUriFor(OTP_NOTIFICATION_REDACTION_LOCK_TIME);
- private static final long LOCK_TIME_FOR_SENSITIVE_REDACTION_MS =
+ private static final long DEFAULT_LOCK_TIME_FOR_SENSITIVE_REDACTION_MS =
TimeUnit.MINUTES.toMillis(10);
private final Lazy<NotificationVisibilityProvider> mVisibilityProviderLazy;
private final Lazy<CommonNotifCollection> mCommonNotifCollectionLazy;
@@ -316,7 +316,8 @@
protected final AtomicBoolean mConnectedToWifi = new AtomicBoolean(false);
protected final AtomicBoolean mRedactOtpOnWifi = new AtomicBoolean(true);
- protected final AtomicBoolean mRedactOtpImmediately = new AtomicBoolean(false);
+ protected final AtomicLong mOtpRedactionRequiredLockTimeMs =
+ new AtomicLong(DEFAULT_LOCK_TIME_FOR_SENSITIVE_REDACTION_MS);
protected int mCurrentUserId = 0;
@@ -375,7 +376,7 @@
mLockScreenUris.add(SHOW_LOCKSCREEN);
mLockScreenUris.add(SHOW_PRIVATE_LOCKSCREEN);
mLockScreenUris.add(REDACT_OTP_ON_WIFI);
- mLockScreenUris.add(REDACT_OTP_IMMEDIATELY);
+ mLockScreenUris.add(OTP_REDACTION_LOCK_TIME);
dumpManager.registerDumpable(this);
@@ -447,8 +448,8 @@
changed |= updateUserShowPrivateSettings(user.getIdentifier());
} else if (REDACT_OTP_ON_WIFI.equals(uri)) {
changed |= updateRedactOtpOnWifiSetting();
- } else if (REDACT_OTP_IMMEDIATELY.equals(uri)) {
- changed |= updateRedactOtpImmediatelySetting();
+ } else if (OTP_REDACTION_LOCK_TIME.equals(uri)) {
+ changed |= updateOtpLockTimeSetting();
}
}
@@ -487,7 +488,7 @@
mLockscreenSettingsObserver
);
mSecureSettings.registerContentObserverAsync(
- REDACT_OTP_IMMEDIATELY,
+ OTP_REDACTION_LOCK_TIME,
mLockscreenSettingsObserver
);
@@ -638,13 +639,13 @@
}
@WorkerThread
- private boolean updateRedactOtpImmediatelySetting() {
- boolean originalValue = mRedactOtpImmediately.get();
- boolean newValue = mSecureSettings.getIntForUser(
- REDACT_OTP_NOTIFICATION_IMMEDIATELY,
- 0,
- Process.myUserHandle().getIdentifier()) != 0;
- mRedactOtpImmediately.set(newValue);
+ private boolean updateOtpLockTimeSetting() {
+ long originalValue = mOtpRedactionRequiredLockTimeMs.get();
+ long newValue = mSecureSettings.getLongForUser(
+ OTP_NOTIFICATION_REDACTION_LOCK_TIME,
+ DEFAULT_LOCK_TIME_FOR_SENSITIVE_REDACTION_MS,
+ Process.myUserHandle().getIdentifier());
+ mOtpRedactionRequiredLockTimeMs.set(newValue);
return originalValue != newValue;
}
@@ -832,14 +833,9 @@
return false;
}
- long latestTimeForRedaction;
- if (mRedactOtpImmediately.get()) {
- latestTimeForRedaction = mLastLockTime.get();
- } else {
- // If the lock screen was not already locked for LOCK_TIME_FOR_SENSITIVE_REDACTION_MS
- // when this notification arrived, do not redact
- latestTimeForRedaction = mLastLockTime.get() + LOCK_TIME_FOR_SENSITIVE_REDACTION_MS;
- }
+ // If the lock screen was not already locked for at least mOtpRedactionRequiredLockTimeMs
+ // when this notification arrived, do not redact
+ long latestTimeForRedaction = mLastLockTime.get() + mOtpRedactionRequiredLockTimeMs.get();
if (ent.getSbn().getPostTime() < latestTimeForRedaction) {
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 6aa2fe2..84266e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -38,13 +38,13 @@
import com.android.systemui.Flags.spatialModelAppPushback
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.ShadeExpansionChangeEvent
import com.android.systemui.shade.ShadeExpansionListener
-import com.android.systemui.shared.Flags.ambientAod
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
import com.android.systemui.statusbar.phone.DozeParameters
@@ -53,8 +53,11 @@
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.policy.SplitShadeStateController
import com.android.systemui.util.WallpaperController
+import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor
import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
import com.android.wm.shell.appzoomout.AppZoomOut
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
import java.io.PrintWriter
import java.util.Optional
import javax.inject.Inject
@@ -76,12 +79,14 @@
private val keyguardInteractor: KeyguardInteractor,
private val choreographer: Choreographer,
private val wallpaperController: WallpaperController,
+ private val wallpaperInteractor: WallpaperInteractor,
private val notificationShadeWindowController: NotificationShadeWindowController,
private val dozeParameters: DozeParameters,
@ShadeDisplayAware private val context: Context,
private val splitShadeStateController: SplitShadeStateController,
private val windowRootViewBlurInteractor: WindowRootViewBlurInteractor,
private val appZoomOutOptional: Optional<AppZoomOut>,
+ @Application private val applicationScope: CoroutineScope,
dumpManager: DumpManager,
configurationController: ConfigurationController,
) : ShadeExpansionListener, Dumpable {
@@ -110,6 +115,12 @@
private var prevTimestamp: Long = -1
private var prevShadeDirection = 0
private var prevShadeVelocity = 0f
+ private var prevDozeAmount: Float = 0f
+ @VisibleForTesting var wallpaperSupportsAmbientMode: Boolean = false
+ // tracks whether app launch transition is in progress. This involves two independent factors
+ // that control blur, shade expansion and app launch animation from outside sysui.
+ // They can complete out of order, this flag will be reset by the animation that finishes later.
+ private var appLaunchTransitionIsInProgress = false
// Only for dumpsys
private var lastAppliedBlur = 0
@@ -158,6 +169,18 @@
if (field == value) {
return
}
+ // Set this to true now, this will be reset when the next shade expansion finishes or
+ // when the app launch finishes, whichever happens later.
+ if (value) {
+ appLaunchTransitionIsInProgress = true
+ } else {
+ // App was launching and now it has finished launching
+ if (shadeExpansion == 0.0f) {
+ // this means shade expansion finished before app launch was done.
+ // reset the flag here
+ appLaunchTransitionIsInProgress = false
+ }
+ }
field = value
scheduleUpdate()
@@ -172,6 +195,12 @@
shadeAnimation.animateTo(0)
shadeAnimation.finishIfRunning()
}
+ @Deprecated(
+ message =
+ "This might get reset to false before shade expansion is fully done, " +
+ "consider using areBlursDisabledForAppLaunch"
+ )
+ get() = field
private var zoomOutCalculatedFromShadeRadius: Float = 0.0f
@@ -183,6 +212,11 @@
scheduleUpdate()
}
+ private val areBlursDisabledForAppLaunch: Boolean
+ get() =
+ blursDisabledForAppLaunch ||
+ (Flags.bouncerUiRevamp() && appLaunchTransitionIsInProgress)
+
/** Force stop blur effect when necessary. */
private var scrimsVisible: Boolean = false
set(value) {
@@ -192,7 +226,15 @@
}
/** Blur radius of the wake-up animation on this frame. */
- private var wakeAndUnlockBlurRadius = 0f
+ private var wakeBlurRadius = 0f
+ set(value) {
+ if (field == value) return
+ field = value
+ scheduleUpdate()
+ }
+
+ /** Blur radius of the unlock animation on this frame. */
+ private var unlockBlurRadius = 0f
set(value) {
if (field == value) return
field = value
@@ -219,14 +261,16 @@
ShadeInterpolation.getNotificationScrimAlpha(qsPanelExpansion) * shadeExpansion
combinedBlur = max(combinedBlur, blurUtils.blurRadiusOfRatio(qsExpandedRatio))
combinedBlur = max(combinedBlur, blurUtils.blurRadiusOfRatio(transitionToFullShadeProgress))
- var shadeRadius = max(combinedBlur, wakeAndUnlockBlurRadius)
+ var shadeRadius = max(combinedBlur, max(wakeBlurRadius, unlockBlurRadius))
- if (blursDisabledForAppLaunch || blursDisabledForUnlock) {
+ if (areBlursDisabledForAppLaunch || blursDisabledForUnlock) {
shadeRadius = 0f
}
var blur = shadeRadius.toInt()
- val zoomOut = blurRadiusToZoomOut(blurRadius = shadeRadius)
+ // If the blur comes from waking up, we don't want to zoom out the background
+ val zoomOut =
+ if (shadeRadius != wakeBlurRadius) blurRadiusToZoomOut(blurRadius = shadeRadius) else 0f
// Make blur be 0 if it is necessary to stop blur effect.
if (scrimsVisible) {
if (!Flags.notificationShadeBlur()) {
@@ -259,7 +303,7 @@
private val shouldBlurBeOpaque: Boolean
get() =
if (Flags.notificationShadeBlur()) false
- else scrimsVisible && !blursDisabledForAppLaunch
+ else scrimsVisible && !areBlursDisabledForAppLaunch
/** Callback that updates the window blur value and is called only once per frame. */
@VisibleForTesting
@@ -311,14 +355,14 @@
startDelay = keyguardStateController.keyguardFadingAwayDelay
interpolator = Interpolators.FAST_OUT_SLOW_IN
addUpdateListener { animation: ValueAnimator ->
- wakeAndUnlockBlurRadius =
+ unlockBlurRadius =
blurUtils.blurRadiusOfRatio(animation.animatedValue as Float)
}
addListener(
object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
keyguardAnimator = null
- wakeAndUnlockBlurRadius = 0f
+ unlockBlurRadius = 0f
}
}
)
@@ -354,15 +398,20 @@
}
override fun onDozeAmountChanged(linear: Float, eased: Float) {
- wakeAndUnlockBlurRadius =
- if (ambientAod()) {
- 0f
- } else {
- blurUtils.blurRadiusOfRatio(eased)
- }
+ prevDozeAmount = eased
+ updateWakeBlurRadius(prevDozeAmount)
}
}
+ private fun updateWakeBlurRadius(ratio: Float) {
+ wakeBlurRadius =
+ if (!wallpaperSupportsAmbientMode) {
+ 0f
+ } else {
+ blurUtils.blurRadiusOfRatio(ratio)
+ }
+ }
+
init {
dumpManager.registerCriticalDumpable(javaClass.name, this)
if (WAKE_UP_ANIMATION_ENABLED) {
@@ -384,6 +433,12 @@
}
}
)
+ applicationScope.launch {
+ wallpaperInteractor.wallpaperSupportsAmbientMode.collect { supported ->
+ wallpaperSupportsAmbientMode = supported
+ updateWakeBlurRadius(prevDozeAmount)
+ }
+ }
initBlurListeners()
}
@@ -442,6 +497,13 @@
val shadeDirection = sign(diff).toInt()
val shadeVelocity =
MathUtils.constrain(VELOCITY_SCALE * diff / deltaTime, MIN_VELOCITY, MAX_VELOCITY)
+ if (expansion == 0.0f && appLaunchTransitionIsInProgress && !blursDisabledForAppLaunch) {
+ // Shade expansion finished but the app launch is already done, then this should mark
+ // the transition as done.
+ Log.d(TAG, "appLaunchTransitionIsInProgress is now false from shade expansion event")
+ appLaunchTransitionIsInProgress = false
+ }
+
updateShadeAnimationBlur(expansion, tracking, shadeVelocity, shadeDirection)
prevShadeDirection = shadeDirection
@@ -551,8 +613,10 @@
it.println("shouldApplyShadeBlur: ${shouldApplyShadeBlur()}")
it.println("shadeAnimation: ${shadeAnimation.radius}")
it.println("brightnessMirrorRadius: ${brightnessMirrorSpring.radius}")
- it.println("wakeAndUnlockBlur: $wakeAndUnlockBlurRadius")
+ it.println("wakeBlur: $wakeBlurRadius")
+ it.println("unlockBlur: $wakeBlurRadius")
it.println("blursDisabledForAppLaunch: $blursDisabledForAppLaunch")
+ it.println("appLaunchTransitionIsInProgress: $appLaunchTransitionIsInProgress")
it.println("qsPanelExpansion: $qsPanelExpansion")
it.println("transitionToFullShadeProgress: $transitionToFullShadeProgress")
it.println("lastAppliedBlur: $lastAppliedBlur")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt
index 2eae3eb..7548f6ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt
@@ -22,9 +22,9 @@
import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad
import com.android.systemui.statusbar.chips.StatusBarChipsLog
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
import com.android.systemui.statusbar.phone.ongoingcall.domain.interactor.OngoingCallInteractor
-import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -44,25 +44,17 @@
@StatusBarChipsLog private val logger: LogBuffer,
) {
val ongoingCallState: StateFlow<OngoingCallModel> =
- (if (StatusBarChipsModernization.isEnabled)
- ongoingCallInteractor.ongoingCallState
- else
- repository.ongoingCallState)
+ (if (StatusBarChipsModernization.isEnabled) {
+ ongoingCallInteractor.ongoingCallState
+ } else {
+ repository.ongoingCallState
+ })
.onEach {
- logger.log(
- TAG,
- LogLevel.INFO,
- { str1 = it::class.simpleName },
- { "State: $str1" }
- )
+ logger.log(TAG, LogLevel.INFO, { str1 = it::class.simpleName }, { "State: $str1" })
}
- .stateIn(
- scope,
- SharingStarted.Lazily,
- OngoingCallModel.NoCall
- )
+ .stateIn(scope, SharingStarted.Lazily, OngoingCallModel.NoCall)
companion object {
private val TAG = "OngoingCall".pad()
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/NewStatusBarIcons.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/NewStatusBarIcons.kt
index e3be953..402881d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/NewStatusBarIcons.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/NewStatusBarIcons.kt
@@ -29,10 +29,10 @@
val token: FlagToken
get() = FlagToken(FLAG_NAME, isEnabled)
- /** Is the refactor enabled */
+ /** Is the refactor enabled. Dependency on [StatusBarRootModernization] */
@JvmStatic
inline val isEnabled
- get() = Flags.newStatusBarIcons()
+ get() = Flags.newStatusBarIcons() && StatusBarRootModernization.isEnabled
/**
* Called to ensure code is only run when the flag is enabled. This protects users from the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/ui/compose/MediaControlPopup.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/ui/compose/MediaControlPopup.kt
new file mode 100644
index 0000000..80bdb7f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/ui/compose/MediaControlPopup.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.featurepods.media.ui.compose
+
+import android.widget.FrameLayout
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.viewinterop.AndroidView
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.res.R
+
+/** Displays a popup containing media controls. Embeds the MediaCarousel within a Compose popup. */
+@Composable
+fun MediaControlPopup(mediaHost: MediaHost, modifier: Modifier = Modifier) {
+ AndroidView(
+ modifier =
+ modifier
+ .width(400.dp)
+ .height(200.dp)
+ .clip(
+ shape =
+ RoundedCornerShape(dimensionResource(R.dimen.notification_corner_radius))
+ ),
+ factory = { _ ->
+ mediaHost.hostView.apply {
+ layoutParams =
+ FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ )
+ }
+ mediaHost.hostView
+ },
+ onReset = {},
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopup.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopup.kt
index 8a66904..ead5148 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopup.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopup.kt
@@ -28,7 +28,9 @@
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Popup
import androidx.compose.ui.window.PopupProperties
+import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.res.R
+import com.android.systemui.statusbar.featurepods.media.ui.compose.MediaControlPopup
import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipId
import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
@@ -37,7 +39,7 @@
* status bar.
*/
@Composable
-fun StatusBarPopup(viewModel: PopupChipModel.Shown) {
+fun StatusBarPopup(viewModel: PopupChipModel.Shown, mediaHost: MediaHost) {
val density = Density(LocalContext.current)
Popup(
properties =
@@ -56,7 +58,7 @@
Box(modifier = Modifier.padding(8.dp).wrapContentSize()) {
when (viewModel.chipId) {
is PopupChipId.MediaControl -> {
- // TODO(b/385202114): Populate MediaControlPopup contents.
+ MediaControlPopup(mediaHost = mediaHost)
}
}
// Future popup types will be handled here.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChip.kt
index eb85d2f..c77decd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChip.kt
@@ -16,33 +16,49 @@
package com.android.systemui.statusbar.featurepods.popups.ui.compose
-import androidx.compose.animation.animateContentSize
+import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.background
+import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
-import androidx.compose.foundation.hoverable
+import androidx.compose.foundation.indication
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsHoveredAsState
import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.contentColorFor
+import androidx.compose.material3.minimumInteractiveComponentSize
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.draw.drawWithCache
+import androidx.compose.ui.graphics.BlendMode
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.CompositingStrategy
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.semantics.Role
-import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.text.rememberTextMeasurer
+import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.android.compose.modifiers.thenIf
import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.res.R
import com.android.systemui.statusbar.featurepods.popups.shared.model.HoverBehavior
import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
@@ -54,31 +70,48 @@
@Composable
fun StatusBarPopupChip(viewModel: PopupChipModel.Shown, modifier: Modifier = Modifier) {
val hasHoverBehavior = viewModel.hoverBehavior !is HoverBehavior.None
- val hoverInteractionSource = remember { MutableInteractionSource() }
- val isHovered by hoverInteractionSource.collectIsHoveredAsState()
+ val interactionSource = remember { MutableInteractionSource() }
+ val hoveredState by interactionSource.collectIsHoveredAsState()
+ val isHovered = hasHoverBehavior && hoveredState
val isPopupShown = viewModel.isPopupShown
-
+ val indication = if (hoveredState) null else LocalIndication.current
+ val chipShape =
+ RoundedCornerShape(dimensionResource(id = R.dimen.ongoing_activity_chip_corner_radius))
val chipBackgroundColor =
if (isPopupShown) {
- MaterialTheme.colorScheme.primaryContainer
+ MaterialTheme.colorScheme.primary
} else {
- MaterialTheme.colorScheme.surfaceContainerHighest
+ MaterialTheme.colorScheme.surfaceDim
}
- Surface(
- shape = RoundedCornerShape(16.dp),
+
+ // Use a Box with `fillMaxHeight` to create a larger click surface for the chip. The visible
+ // height of the chip is determined by the height of the background of the Row below. The
+ // `indication` for Clicks is applied in the Row below as well.
+ Box(
+ contentAlignment = Alignment.Center,
modifier =
- modifier
- .widthIn(max = 120.dp)
- .padding(vertical = 4.dp)
- .animateContentSize()
- .thenIf(hasHoverBehavior) { Modifier.hoverable(hoverInteractionSource) }
- .thenIf(!isPopupShown) { Modifier.clickable { viewModel.showPopup() } },
- color = chipBackgroundColor,
+ modifier.minimumInteractiveComponentSize().thenIf(!isPopupShown) {
+ Modifier.clickable(
+ onClick = { viewModel.showPopup() },
+ indication = null,
+ interactionSource = interactionSource,
+ )
+ },
) {
Row(
- modifier = Modifier.padding(start = 4.dp, end = 8.dp),
- verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ modifier =
+ Modifier.height(dimensionResource(R.dimen.ongoing_appops_chip_height))
+ .clip(chipShape)
+ .background(chipBackgroundColor)
+ .border(
+ width = dimensionResource(id = R.dimen.ongoing_activity_chip_outline_width),
+ color = MaterialTheme.colorScheme.outlineVariant,
+ shape = chipShape,
+ )
+ .indication(interactionSource, indication)
+ .padding(start = 4.dp, end = 8.dp),
) {
val iconColor =
if (isHovered) chipBackgroundColor else contentColorFor(chipBackgroundColor)
@@ -92,9 +125,11 @@
else -> viewModel.icon
},
modifier =
- Modifier.thenIf(isHovered) {
- Modifier.padding(3.dp)
- .background(color = iconBackgroundColor, shape = CircleShape)
+ Modifier.height(20.dp)
+ .width(20.dp)
+ .thenIf(isHovered) {
+ Modifier.background(color = iconBackgroundColor, shape = CircleShape)
+ .padding(2.dp)
}
.thenIf(hoverBehavior is HoverBehavior.Button) {
Modifier.clickable(
@@ -102,18 +137,67 @@
onClick = (hoverBehavior as HoverBehavior.Button).onIconPressed,
indication = ripple(),
interactionSource = iconInteractionSource,
+ enabled = isHovered,
)
- }
- .padding(3.dp),
+ },
tint = iconColor,
)
+ val text = viewModel.chipText
+ val textStyle = MaterialTheme.typography.labelLarge
+ val textMeasurer = rememberTextMeasurer()
+ var textOverflow by remember { mutableStateOf(false) }
+
Text(
- text = viewModel.chipText,
- style = MaterialTheme.typography.labelLarge,
+ text = text,
+ style = textStyle,
softWrap = false,
- overflow = TextOverflow.Ellipsis,
+ modifier =
+ Modifier.widthIn(
+ max =
+ dimensionResource(id = R.dimen.ongoing_activity_chip_max_text_width)
+ )
+ .layout { measurables, constraints ->
+ val placeable = measurables.measure(constraints)
+ val intrinsicWidth =
+ textMeasurer.measure(text, textStyle, softWrap = false).size.width
+ textOverflow = intrinsicWidth > constraints.maxWidth
+
+ layout(placeable.width, placeable.height) {
+ if (textOverflow) {
+ placeable.placeWithLayer(0, 0) {
+ compositingStrategy = CompositingStrategy.Offscreen
+ }
+ } else {
+ placeable.place(0, 0)
+ }
+ }
+ }
+ .overflowFadeOut(
+ hasOverflow = { textOverflow },
+ fadeLength =
+ dimensionResource(
+ id = R.dimen.ongoing_activity_chip_text_fading_edge_length
+ ),
+ ),
)
}
}
}
+
+private fun Modifier.overflowFadeOut(hasOverflow: () -> Boolean, fadeLength: Dp): Modifier {
+ return drawWithCache {
+ val width = size.width
+ val start = (width - fadeLength.toPx()).coerceAtLeast(0f)
+ val gradient =
+ Brush.horizontalGradient(
+ colors = listOf(Color.Black, Color.Transparent),
+ startX = start,
+ endX = width,
+ )
+ onDrawWithContent {
+ drawContent()
+ if (hasOverflow()) drawRect(brush = gradient, blendMode = BlendMode.DstIn)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChipsContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChipsContainer.kt
index 16538c9..f5f1f20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChipsContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChipsContainer.kt
@@ -20,14 +20,37 @@
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipId
import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
/** Container view that holds all right hand side chips in the status bar. */
@Composable
-fun StatusBarPopupChipsContainer(chips: List<PopupChipModel.Shown>, modifier: Modifier = Modifier) {
+fun StatusBarPopupChipsContainer(
+ chips: List<PopupChipModel.Shown>,
+ mediaHost: MediaHost,
+ onMediaControlPopupVisibilityChanged: (Boolean) -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ if (!SceneContainerFlag.isEnabled) {
+ val isMediaControlPopupShown =
+ remember(chips) {
+ chips.any { it.chipId == PopupChipId.MediaControl && it.isPopupShown }
+ }
+
+ LaunchedEffect(isMediaControlPopupShown) {
+ onMediaControlPopupVisibilityChanged(isMediaControlPopupShown)
+ }
+ }
+
// TODO(b/385353140): Add padding and spacing for this container according to UX specs.
Box {
Row(
@@ -37,7 +60,7 @@
chips.forEach { chip ->
StatusBarPopupChip(chip)
if (chip.isPopupShown) {
- StatusBarPopup(chip)
+ StatusBarPopup(viewModel = chip, mediaHost = mediaHost)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt
index 383227d..ab40582 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt
@@ -21,6 +21,7 @@
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.TransitionAnimator
+import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil
@@ -157,8 +158,8 @@
private val headsUpNotificationRow: ExpandableNotificationRow?
get() {
- val summaryEntry = notificationEntry.parent?.summary
-
+ val pipelineParent = notificationEntry.parent
+ val summaryEntry = (pipelineParent as? GroupEntry)?.summary
return when {
headsUpManager.isHeadsUpEntry(notificationKey) -> notification
summaryEntry == null -> null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
index c6775d6..31bcf2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
@@ -26,6 +26,7 @@
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.util.Compile;
@@ -80,7 +81,7 @@
private static final boolean INCLUDE_HASH_CODE_IN_LIST_ENTRY_LOG_KEY = false;
/** Get the notification key, reformatted for logging, for the (optional) entry */
- public static String logKey(ListEntry entry) {
+ public static String logKey(PipelineEntry entry) {
if (entry == null) {
return "null";
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.kt
index 432bac4..2c29e30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.kt
@@ -17,10 +17,10 @@
package com.android.systemui.statusbar.notification
import android.service.notification.StatusBarNotification
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
/** Get the notification key, reformatted for logging, for the (optional) entry */
-val ListEntry?.logKey: String?
+val PipelineEntry?.logKey: String?
get() = this?.let { NotificationUtils.logKey(it) }
/** Get the notification key, reformatted for logging, for the (optional) sbn */
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 24ab695..35a2828 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
@@ -29,26 +29,54 @@
import androidx.annotation.VisibleForTesting;
import com.android.systemui.statusbar.notification.icon.IconPack;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import java.util.List;
/**
- * Abstract class to represent notification section bundled by AI.
+ * Class to represent notifications bundled by classification.
*/
public class BundleEntry extends PipelineEntry {
- private final String mKey;
private final BundleEntryAdapter mEntryAdapter;
// TODO (b/389839319): implement the row
private ExpandableNotificationRow mRow;
public BundleEntry(String key) {
- mKey = key;
+ super(key);
mEntryAdapter = new BundleEntryAdapter();
}
+ @Nullable
+ @Override
+ public NotificationEntry getRepresentativeEntry() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public NotifSection getSection() {
+ return null;
+ }
+
+ @Override
+ public int getSectionIndex() {
+ return 0;
+ }
+
+ @Nullable
+ @Override
+ public PipelineEntry getParent() {
+ return null;
+ }
+
+ @Override
+ public boolean wasAttachedInPreviousPass() {
+ return false;
+ }
+
@VisibleForTesting
public BundleEntryAdapter getEntryAdapter() {
return mEntryAdapter;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
index 918843c..8726e83 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
@@ -75,6 +75,7 @@
return mChildren;
}
+ // TODO(b/394483200) Change ROOT_ENTRY to PipelineEntry
public static final GroupEntry ROOT_ENTRY = new GroupEntry("<root>", 0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
index b5fce41..4a1b956 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
@@ -21,14 +21,14 @@
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
/**
- * Stores the state that [ShadeListBuilder] assigns to this [ListEntry]
+ * Stores the state that [ShadeListBuilder] assigns to this [PipelineEntry]
*/
data class ListAttachState private constructor(
/**
* Null if not attached to the current shade list. If top-level, then the shade list root. If
* part of a group, then that group's GroupEntry.
*/
- var parent: GroupEntry?,
+ var parent: PipelineEntry?,
/**
* The section that this ListEntry was sorted into. If the child of the group, this will be the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
index f6a572e..60b75b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
@@ -39,14 +39,14 @@
* entry to be in its current state (ie: filter, lifeExtender)
*/
public static String dumpTree(
- List<ListEntry> entries,
+ List<PipelineEntry> entries,
NotificationInteractionTracker interactionTracker,
boolean includeRecordKeeping,
String indent) {
StringBuilder sb = new StringBuilder();
final String childEntryIndent = indent + INDENT;
for (int topEntryIndex = 0; topEntryIndex < entries.size(); topEntryIndex++) {
- ListEntry entry = entries.get(topEntryIndex);
+ PipelineEntry entry = entries.get(topEntryIndex);
dumpEntry(entry,
Integer.toString(topEntryIndex),
indent,
@@ -106,7 +106,7 @@
}
private static void dumpEntry(
- ListEntry entry,
+ PipelineEntry entry,
String index,
String indent,
StringBuilder sb,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
index c8e3be4..697d0a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
@@ -21,28 +21,18 @@
import androidx.annotation.Nullable;
-import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
-
/**
* Abstract superclass for top-level entries, i.e. things that can appear in the final notification
* list shown to users. In practice, this means either GroupEntries or NotificationEntries.
*/
public abstract class ListEntry extends PipelineEntry {
- private final String mKey;
private final long mCreationTime;
- private final ListAttachState mPreviousAttachState = ListAttachState.create();
- private final ListAttachState mAttachState = ListAttachState.create();
-
protected ListEntry(String key, long creationTime) {
- mKey = key;
+ super(key);
mCreationTime = creationTime;
}
- public String getKey() {
- return mKey;
- }
-
/**
* The SystemClock.uptimeMillis() when this object was created. In general, this means the
* moment when NotificationManager notifies our listener about the existence of this entry.
@@ -64,34 +54,22 @@
*/
public abstract @Nullable NotificationEntry getRepresentativeEntry();
- @Nullable public GroupEntry getParent() {
+ @Nullable public PipelineEntry getParent() {
return mAttachState.getParent();
}
- void setParent(@Nullable GroupEntry parent) {
+ void setParent(@Nullable PipelineEntry parent) {
mAttachState.setParent(parent);
}
- @Nullable public GroupEntry getPreviousParent() {
+ @Nullable public PipelineEntry getPreviousParent() {
return mPreviousAttachState.getParent();
}
- @Nullable public NotifSection getSection() {
- return mAttachState.getSection();
- }
-
public int getSectionIndex() {
return mAttachState.getSection() != null ? mAttachState.getSection().getIndex() : -1;
}
- ListAttachState getAttachState() {
- return mAttachState;
- }
-
- ListAttachState getPreviousAttachState() {
- return mPreviousAttachState;
- }
-
/**
* Stores the current attach state into {@link #getPreviousAttachState()}} and then starts a
* fresh attach state (all entries will be null/default-initialized).
@@ -100,11 +78,4 @@
mPreviousAttachState.clone(mAttachState);
mAttachState.reset();
}
-
- /**
- * True if this entry was attached in the last pass, else false.
- */
- public boolean wasAttachedInPreviousPass() {
- return getPreviousAttachState().getParent() != null;
- }
}
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 698a563..e5b72d4 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
@@ -305,8 +305,10 @@
if (isTopLevelEntry() || getParent() == null) {
return null;
}
- if (NotificationEntry.this.getParent().getSummary() != null) {
- return NotificationEntry.this.getParent().getSummary().mEntryAdapter;
+ if (NotificationEntry.this.getParent() instanceof GroupEntry parentGroupEntry) {
+ if (parentGroupEntry.getSummary() != null) {
+ return parentGroupEntry.getSummary().mEntryAdapter;
+ }
}
return null;
}
@@ -588,7 +590,7 @@
* Get the children that are actually attached to this notification's row.
*
* TODO: Seems like most callers here should probably be using
- * {@link GroupMembershipManager#getChildren(ListEntry)}
+ * {@link GroupMembershipManager#getChildren(PipelineEntry)}
*/
public @Nullable List<NotificationEntry> getAttachedNotifChildren() {
if (row == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java
index c5a4791..78652cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java
@@ -16,8 +16,74 @@
package com.android.systemui.statusbar.notification.collection;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
+
/**
* Class to represent a notification, group, or bundle in the pipeline.
*/
public abstract class PipelineEntry {
+
+ final String mKey;
+ final ListAttachState mAttachState = ListAttachState.create();
+ final ListAttachState mPreviousAttachState = ListAttachState.create();
+
+ public PipelineEntry(String key) {
+ this.mKey = key;
+ }
+
+ /**
+ * Key of the representative entry.
+ */
+ public @NonNull String getKey() {
+ return mKey;
+ }
+
+ /**
+ * @return The representative NotificationEntry:
+ * for NotificationEntry, return itself
+ * for GroupEntry, return the summary NotificationEntry, or null if it does not exist
+ * for BundleEntry, return null
+ */
+ public abstract @Nullable NotificationEntry getRepresentativeEntry();
+
+ /**
+ * @return NotifSection that ShadeListBuilder assigned to this PipelineEntry.
+ */
+ @Nullable public NotifSection getSection() {
+ return mAttachState.getSection();
+ }
+
+ /**
+ * @return True if this entry was attached in the last pass, else false.
+ */
+ public boolean wasAttachedInPreviousPass() {
+ return getPreviousAttachState().getParent() != null;
+ }
+
+ /**
+ * @return Index of section assigned to this entry.
+ */
+ public abstract int getSectionIndex();
+
+ /**
+ * @return Parent PipelineEntry
+ */
+ public abstract @Nullable PipelineEntry getParent();
+
+ /**
+ * @return Current state that ShadeListBuilder assigned to this PipelineEntry.
+ */
+ final ListAttachState getAttachState() {
+ return mAttachState;
+ }
+
+ /**
+ * @return Previous state that ShadeListBuilder assigned to this PipelineEntry.
+ */
+ final ListAttachState getPreviousAttachState() {
+ return mPreviousAttachState;
+ }
}
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 ac11c15..bb84ab8 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
@@ -100,15 +100,15 @@
private final DumpManager mDumpManager;
// used exclusivly by ShadeListBuilder#notifySectionEntriesUpdated
// TODO replace temp with collection pool for readability
- private final ArrayList<ListEntry> mTempSectionMembers = new ArrayList<>();
+ private final ArrayList<PipelineEntry> mTempSectionMembers = new ArrayList<>();
private NotifPipelineFlags mFlags;
private final boolean mAlwaysLogList;
- private List<ListEntry> mNotifList = new ArrayList<>();
- private List<ListEntry> mNewNotifList = new ArrayList<>();
+ private List<PipelineEntry> mNotifList = new ArrayList<>();
+ private List<PipelineEntry> mNewNotifList = new ArrayList<>();
private final SemiStableSort mSemiStableSort = new SemiStableSort();
- private final StableOrder<ListEntry> mStableOrder = this::getStableOrderRank;
+ private final StableOrder<PipelineEntry> mStableOrder = this::getStableOrderRank;
private final PipelineState mPipelineState = new PipelineState();
private final Map<String, GroupEntry> mGroups = new ArrayMap<>();
private Collection<NotificationEntry> mAllEntries = Collections.emptyList();
@@ -133,8 +133,8 @@
mOnBeforeRenderListListeners = new NamedListenerSet<>();
@Nullable private OnRenderListListener mOnRenderListListener;
- private List<ListEntry> mReadOnlyNotifList = Collections.unmodifiableList(mNotifList);
- private List<ListEntry> mReadOnlyNewNotifList = Collections.unmodifiableList(mNewNotifList);
+ private List<PipelineEntry> mReadOnlyNotifList = Collections.unmodifiableList(mNotifList);
+ private List<PipelineEntry> mReadOnlyNewNotifList = Collections.unmodifiableList(mNewNotifList);
private final NotifPipelineChoreographer mChoreographer;
private int mConsecutiveReentrantRebuilds = 0;
@@ -308,7 +308,7 @@
}
}
- List<ListEntry> getShadeList() {
+ List<PipelineEntry> getShadeList() {
Assert.isMainThread();
// NOTE: Accessing this method when the pipeline is running is generally going to provide
// incorrect results, and indicates a poorly behaved component of the pipeline.
@@ -493,7 +493,7 @@
Trace.beginSection("ShadeListBuilder.notifySectionEntriesUpdated");
mTempSectionMembers.clear();
for (NotifSection section : mNotifSections) {
- for (ListEntry entry : mNotifList) {
+ for (PipelineEntry entry : mNotifList) {
if (section == entry.getSection()) {
mTempSectionMembers.add(entry);
}
@@ -514,11 +514,11 @@
*/
private void applyNewNotifList() {
mNotifList.clear();
- List<ListEntry> emptyList = mNotifList;
+ List<PipelineEntry> emptyList = mNotifList;
mNotifList = mNewNotifList;
mNewNotifList = emptyList;
- List<ListEntry> readOnlyNotifList = mReadOnlyNotifList;
+ List<PipelineEntry> readOnlyNotifList = mReadOnlyNotifList;
mReadOnlyNotifList = mReadOnlyNewNotifList;
mReadOnlyNewNotifList = readOnlyNotifList;
}
@@ -538,12 +538,12 @@
}
private void filterNotifs(
- Collection<? extends ListEntry> entries,
- List<ListEntry> out,
+ Collection<? extends PipelineEntry> entries,
+ List<PipelineEntry> out,
List<NotifFilter> filters) {
Trace.beginSection("ShadeListBuilder.filterNotifs");
final long now = mSystemClock.uptimeMillis();
- for (ListEntry entry : entries) {
+ for (PipelineEntry entry : entries) {
if (entry instanceof GroupEntry) {
final GroupEntry groupEntry = (GroupEntry) entry;
@@ -576,11 +576,11 @@
Trace.endSection();
}
- private void groupNotifs(List<ListEntry> entries, List<ListEntry> out) {
+ private void groupNotifs(List<PipelineEntry> entries, List<PipelineEntry> out) {
Trace.beginSection("ShadeListBuilder.groupNotifs");
- for (ListEntry listEntry : entries) {
+ for (PipelineEntry PipelineEntry : entries) {
// since grouping hasn't happened yet, all notifs are NotificationEntries
- NotificationEntry entry = (NotificationEntry) listEntry;
+ NotificationEntry entry = (NotificationEntry) PipelineEntry;
if (entry.getSbn().isGroup()) {
final String topLevelKey = entry.getSbn().getGroupKey();
@@ -631,14 +631,14 @@
Trace.endSection();
}
- private void stabilizeGroupingNotifs(List<ListEntry> topLevelList) {
+ private void stabilizeGroupingNotifs(List<PipelineEntry> topLevelList) {
if (getStabilityManager().isEveryChangeAllowed()) {
return;
}
Trace.beginSection("ShadeListBuilder.stabilizeGroupingNotifs");
for (int i = 0; i < topLevelList.size(); i++) {
- final ListEntry tle = topLevelList.get(i);
+ final PipelineEntry tle = topLevelList.get(i);
if (tle instanceof GroupEntry) {
// maybe put children back into their old group (including moving back to top-level)
GroupEntry groupEntry = (GroupEntry) tle;
@@ -667,13 +667,13 @@
/**
* Returns true if the group change was suppressed, else false
*/
- private boolean maybeSuppressGroupChange(NotificationEntry entry, List<ListEntry> out) {
- final GroupEntry prevParent = entry.getPreviousAttachState().getParent();
+ private boolean maybeSuppressGroupChange(NotificationEntry entry, List<PipelineEntry> out) {
+ final PipelineEntry prevParent = entry.getPreviousAttachState().getParent();
if (prevParent == null) {
// New entries are always allowed.
return false;
}
- final GroupEntry assignedParent = entry.getParent();
+ final PipelineEntry assignedParent = entry.getParent();
if (prevParent == assignedParent) {
// Nothing to change.
return false;
@@ -691,21 +691,22 @@
entry.setParent(prevParent);
if (prevParent == ROOT_ENTRY) {
out.add(entry);
- } else {
- prevParent.addChild(entry);
+ } else if (prevParent instanceof GroupEntry) {
+ ((GroupEntry) prevParent).addChild(entry);
if (!mGroups.containsKey(prevParent.getKey())) {
- mGroups.put(prevParent.getKey(), prevParent);
+ mGroups.put(prevParent.getKey(), (GroupEntry) prevParent);
}
}
+ // TODO(b/394483200): Revisit group stability for BundleEntry
return true;
}
return false;
}
- private void promoteNotifs(List<ListEntry> list) {
+ private void promoteNotifs(List<PipelineEntry> list) {
Trace.beginSection("ShadeListBuilder.promoteNotifs");
for (int i = 0; i < list.size(); i++) {
- final ListEntry tle = list.get(i);
+ final PipelineEntry tle = list.get(i);
if (tle instanceof GroupEntry) {
final GroupEntry group = (GroupEntry) tle;
@@ -725,7 +726,7 @@
Trace.endSection();
}
- private void pruneIncompleteGroups(List<ListEntry> shadeList) {
+ private void pruneIncompleteGroups(List<PipelineEntry> shadeList) {
Trace.beginSection("ShadeListBuilder.pruneIncompleteGroups");
// Any group which lost a child on this run to stability is exempt from being pruned or
// having its summary promoted, regardless of how many children it has
@@ -742,7 +743,7 @@
// Iterate backwards, so that we can remove elements without affecting indices of
// yet-to-be-accessed entries.
for (int i = shadeList.size() - 1; i >= 0; i--) {
- final ListEntry tle = shadeList.get(i);
+ final PipelineEntry tle = shadeList.get(i);
if (tle instanceof GroupEntry) {
final GroupEntry group = (GroupEntry) tle;
@@ -793,7 +794,7 @@
Trace.endSection();
}
- private void pruneGroupAtIndexAndPromoteSummary(List<ListEntry> shadeList,
+ private void pruneGroupAtIndexAndPromoteSummary(List<PipelineEntry> shadeList,
GroupEntry group, int index) {
// Validate that the group has no children
checkArgument(group.getChildren().isEmpty(), "group should have no children");
@@ -801,7 +802,7 @@
NotificationEntry summary = group.getSummary();
summary.setParent(ROOT_ENTRY);
// The list may be sorted; replace the group with the summary, in its place
- ListEntry oldEntry = shadeList.set(index, summary);
+ PipelineEntry oldEntry = shadeList.set(index, summary);
// Validate that the replaced entry was the group entry
checkState(oldEntry == group);
@@ -812,10 +813,10 @@
"SUMMARY with no children @ " + mPipelineState.getStateName());
}
- private void pruneGroupAtIndexAndPromoteAnyChildren(List<ListEntry> shadeList,
+ private void pruneGroupAtIndexAndPromoteAnyChildren(List<PipelineEntry> shadeList,
GroupEntry group, int index) {
// REMOVE the GroupEntry at this index
- ListEntry oldEntry = shadeList.remove(index);
+ PipelineEntry oldEntry = shadeList.remove(index);
// Validate that the replaced entry was the group entry
checkState(oldEntry == group);
@@ -868,14 +869,14 @@
* top level (ungrouped) notifications.
*/
@NonNull
- private Set<String> getGroupsWithChildrenLostToStability(List<ListEntry> shadeList) {
+ private Set<String> getGroupsWithChildrenLostToStability(List<PipelineEntry> shadeList) {
if (getStabilityManager().isEveryChangeAllowed()) {
return Collections.emptySet();
}
ArraySet<String> groupsWithChildrenLostToStability = new ArraySet<>();
for (int i = 0; i < shadeList.size(); i++) {
- final ListEntry tle = shadeList.get(i);
- final GroupEntry suppressedParent =
+ final PipelineEntry tle = shadeList.get(i);
+ final PipelineEntry suppressedParent =
tle.getAttachState().getSuppressedChanges().getParent();
if (suppressedParent != null) {
// This top-level-entry was supposed to be attached to this group,
@@ -892,9 +893,10 @@
*
* These groups will be exempt from appearing without any children.
*/
- private void addGroupsWithChildrenLostToPromotion(List<ListEntry> shadeList, Set<String> out) {
+ private void addGroupsWithChildrenLostToPromotion(List<PipelineEntry> shadeList,
+ Set<String> out) {
for (int i = 0; i < shadeList.size(); i++) {
- final ListEntry tle = shadeList.get(i);
+ final PipelineEntry tle = shadeList.get(i);
if (tle.getAttachState().getPromoter() != null) {
// This top-level-entry was part of a group, but was promoted out of it.
final String groupKey = tle.getRepresentativeEntry().getSbn().getGroupKey();
@@ -910,7 +912,7 @@
* These groups will be exempt from appearing without any children.
*/
private void addGroupsWithChildrenLostToFiltering(Set<String> out) {
- for (ListEntry tle : mAllEntries) {
+ for (PipelineEntry tle : mAllEntries) {
StatusBarNotification sbn = tle.getRepresentativeEntry().getSbn();
if (sbn.isGroup()
&& !sbn.getNotification().isGroupSummary()
@@ -921,14 +923,14 @@
}
/**
- * If a ListEntry was added to the shade list and then later removed (e.g. because it was a
+ * If a PipelineEntry was added to the shade list and then later removed (e.g. because it was a
* group that was broken up), this method will erase any bookkeeping traces of that addition
* and/or check that they were already erased.
*
* Before calling this method, the entry must already have been removed from its parent. If
* it's a group, its summary must be null and its children must be empty.
*/
- private void annulAddition(ListEntry entry, List<ListEntry> shadeList) {
+ private void annulAddition(PipelineEntry entry, List<PipelineEntry> shadeList) {
// This function does very little, but if any of its assumptions are violated (and it has a
// lot of them), it will put the system into an inconsistent state. So we check all of them
@@ -956,9 +958,11 @@
throw new IllegalStateException(
"Cannot nullify group " + ge.getKey() + ": still has children");
}
- } else if (entry instanceof NotificationEntry) {
- if (entry == entry.getParent().getSummary()
- || entry.getParent().getChildren().contains(entry)) {
+ } else if (entry instanceof NotificationEntry
+ && entry.getParent() instanceof GroupEntry) {
+ GroupEntry parentGroupEntry = (GroupEntry) entry.getParent();
+ if (entry == parentGroupEntry.getSummary()
+ || parentGroupEntry.getChildren().contains(entry)) {
throw new IllegalStateException("Cannot nullify addition of child "
+ entry.getKey() + ": it's still attached to its parent.");
}
@@ -973,14 +977,14 @@
* This can happen if the entry is removed from a group that was broken up or if the entry was
* filtered out during any of the filtering steps.
*/
- private void annulAddition(ListEntry entry) {
+ private void annulAddition(PipelineEntry entry) {
entry.getAttachState().detach();
}
private void assignSections() {
Trace.beginSection("ShadeListBuilder.assignSections");
// Assign sections to top-level elements and their children
- for (ListEntry entry : mNotifList) {
+ for (PipelineEntry entry : mNotifList) {
NotifSection section = applySections(entry);
if (entry instanceof GroupEntry) {
GroupEntry parent = (GroupEntry) entry;
@@ -1001,7 +1005,7 @@
private void sortWithSemiStableSort() {
// Sort each group's children
boolean allSorted = true;
- for (ListEntry entry : mNotifList) {
+ for (PipelineEntry entry : mNotifList) {
if (entry instanceof GroupEntry) {
GroupEntry parent = (GroupEntry) entry;
allSorted &= sortGroupChildren(parent.getRawChildren());
@@ -1010,7 +1014,7 @@
// Sort each section within the top level list
mNotifList.sort(mTopLevelComparator);
if (!getStabilityManager().isEveryChangeAllowed()) {
- for (List<ListEntry> subList : getSectionSubLists(mNotifList)) {
+ for (List<PipelineEntry> subList : getSectionSubLists(mNotifList)) {
allSorted &= mSemiStableSort.stabilizeTo(subList, mStableOrder, mNewNotifList);
}
applyNewNotifList();
@@ -1022,7 +1026,7 @@
}
}
- private Iterable<List<ListEntry>> getSectionSubLists(List<ListEntry> entries) {
+ private Iterable<List<PipelineEntry>> getSectionSubLists(List<PipelineEntry> entries) {
return ShadeListBuilderHelper.INSTANCE.getSectionSubLists(entries);
}
@@ -1057,12 +1061,12 @@
/**
* Assign the index of each notification relative to the total order
*/
- private void assignIndexes(List<ListEntry> notifList) {
+ private void assignIndexes(List<PipelineEntry> notifList) {
if (notifList.size() == 0) return;
NotifSection currentSection = requireNonNull(notifList.get(0).getSection());
int sectionMemberIndex = 0;
for (int i = 0; i < notifList.size(); i++) {
- final ListEntry entry = notifList.get(i);
+ final PipelineEntry entry = notifList.get(i);
NotifSection section = requireNonNull(entry.getSection());
if (section.getIndex() != currentSection.getIndex()) {
sectionMemberIndex = 0;
@@ -1099,7 +1103,7 @@
Trace.endSection();
}
- private void logAttachStateChanges(ListEntry entry) {
+ private void logAttachStateChanges(PipelineEntry entry) {
final ListAttachState curr = entry.getAttachState();
final ListAttachState prev = entry.getPreviousAttachState();
@@ -1115,8 +1119,8 @@
mLogger.logParentChanged(mIterationCount, prev.getParent(), curr.getParent());
}
- GroupEntry currSuppressedParent = curr.getSuppressedChanges().getParent();
- GroupEntry prevSuppressedParent = prev.getSuppressedChanges().getParent();
+ PipelineEntry currSuppressedParent = curr.getSuppressedChanges().getParent();
+ PipelineEntry prevSuppressedParent = prev.getSuppressedChanges().getParent();
if (currSuppressedParent != null && (prevSuppressedParent == null
|| !prevSuppressedParent.getKey().equals(currSuppressedParent.getKey()))) {
mLogger.logParentChangeSuppressedStarted(
@@ -1210,7 +1214,7 @@
@Nullable
private NotifComparator getSectionComparator(
- @NonNull ListEntry o1, @NonNull ListEntry o2) {
+ @NonNull PipelineEntry o1, @NonNull PipelineEntry o2) {
final NotifSection section = o1.getSection();
if (section != o2.getSection()) {
throw new RuntimeException("Entry ordering should only be done within sections");
@@ -1221,7 +1225,7 @@
return null;
}
- private final Comparator<ListEntry> mTopLevelComparator = (o1, o2) -> {
+ private final Comparator<PipelineEntry> mTopLevelComparator = (o1, o2) -> {
int cmp = Integer.compare(
o1.getSectionIndex(),
o2.getSectionIndex());
@@ -1264,7 +1268,7 @@
};
@Nullable
- private Integer getStableOrderRank(ListEntry entry) {
+ private Integer getStableOrderRank(PipelineEntry entry) {
if (getStabilityManager().isEntryReorderingAllowed(entry)) {
// let the stability manager constrain or allow reordering
return null;
@@ -1323,7 +1327,7 @@
return null;
}
- private NotifSection applySections(ListEntry entry) {
+ private NotifSection applySections(PipelineEntry entry) {
final NotifSection newSection = findSection(entry);
final ListAttachState prevAttachState = entry.getPreviousAttachState();
@@ -1346,7 +1350,7 @@
return finalSection;
}
- private void setEntrySection(ListEntry entry, NotifSection finalSection) {
+ private void setEntrySection(PipelineEntry entry, NotifSection finalSection) {
entry.getAttachState().setSection(finalSection);
NotificationEntry representativeEntry = entry.getRepresentativeEntry();
if (representativeEntry != null) {
@@ -1358,7 +1362,7 @@
}
@NonNull
- private NotifSection findSection(ListEntry entry) {
+ private NotifSection findSection(PipelineEntry entry) {
for (int i = 0; i < mNotifSections.size(); i++) {
NotifSection section = mNotifSections.get(i);
if (section.getSectioner().isInSection(entry)) {
@@ -1428,10 +1432,10 @@
mChoreographer.schedule();
}
- private static int countChildren(List<ListEntry> entries) {
+ private static int countChildren(List<PipelineEntry> entries) {
int count = 0;
for (int i = 0; i < entries.size(); i++) {
- final ListEntry entry = entries.get(i);
+ final PipelineEntry entry = entries.get(i);
if (entry instanceof GroupEntry) {
count += ((GroupEntry) entry).getChildren().size();
}
@@ -1439,7 +1443,7 @@
return count;
}
- private void dispatchOnBeforeTransformGroups(List<ListEntry> entries) {
+ private void dispatchOnBeforeTransformGroups(List<PipelineEntry> entries) {
Trace.beginSection("ShadeListBuilder.dispatchOnBeforeTransformGroups");
mOnBeforeTransformGroupsListeners.forEachTraced(listener -> {
listener.onBeforeTransformGroups(entries);
@@ -1447,7 +1451,7 @@
Trace.endSection();
}
- private void dispatchOnBeforeSort(List<ListEntry> entries) {
+ private void dispatchOnBeforeSort(List<PipelineEntry> entries) {
Trace.beginSection("ShadeListBuilder.dispatchOnBeforeSort");
mOnBeforeSortListeners.forEachTraced(listener -> {
listener.onBeforeSort(entries);
@@ -1455,7 +1459,7 @@
Trace.endSection();
}
- private void dispatchOnBeforeFinalizeFilter(List<ListEntry> entries) {
+ private void dispatchOnBeforeFinalizeFilter(List<PipelineEntry> entries) {
Trace.beginSection("ShadeListBuilder.dispatchOnBeforeFinalizeFilter");
mOnBeforeFinalizeFilterListeners.forEachTraced(listener -> {
listener.onBeforeFinalizeFilter(entries);
@@ -1463,7 +1467,7 @@
Trace.endSection();
}
- private void dispatchOnBeforeRenderList(List<ListEntry> entries) {
+ private void dispatchOnBeforeRenderList(List<PipelineEntry> entries) {
Trace.beginSection("ShadeListBuilder.dispatchOnBeforeRenderList");
mOnBeforeRenderListListeners.forEachTraced(listener -> {
listener.onBeforeRenderList(entries);
@@ -1508,13 +1512,13 @@
* @param entries A read-only view into the current notif list. Note that this list is
* backed by the live list and will change in response to new pipeline runs.
*/
- void onRenderList(@NonNull List<ListEntry> entries);
+ void onRenderList(@NonNull List<PipelineEntry> entries);
}
private static final NotifSectioner DEFAULT_SECTIONER = new NotifSectioner("UnknownSection",
NotificationPriorityBucketKt.BUCKET_UNKNOWN) {
@Override
- public boolean isInSection(ListEntry entry) {
+ public boolean isInSection(PipelineEntry entry) {
return true;
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt
index 584563b..c942954 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt
@@ -19,7 +19,7 @@
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
/**
- * Stores the suppressed state that [ShadeListBuilder] assigned to this [ListEntry] before the
+ * Stores the suppressed state that [ShadeListBuilder] assigned to this [PipelineEntry] before the
* VisualStabilityManager suppressed group and section changes.
*/
data class SuppressedAttachState private constructor(
@@ -35,7 +35,7 @@
* - Root if suppressing group change to top-level
* - GroupEntry if suppressing group change to a different group
*/
- var parent: GroupEntry?,
+ var parent: PipelineEntry?,
/**
* Whether the ListEntry would have been pruned had its group change not been suppressed.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
index 244c594..e6d5f41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
@@ -20,7 +20,7 @@
import android.app.NotificationChannel.PROMOTIONS_ID
import android.app.NotificationChannel.RECS_ID
import android.app.NotificationChannel.SOCIAL_MEDIA_ID
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
@@ -48,7 +48,7 @@
val newsSectioner =
object : NotifSectioner("News", BUCKET_NEWS) {
- override fun isInSection(entry: ListEntry): Boolean {
+ override fun isInSection(entry: PipelineEntry): Boolean {
return entry.representativeEntry?.channel?.id == NEWS_ID
}
@@ -59,7 +59,7 @@
val socialSectioner =
object : NotifSectioner("Social", BUCKET_SOCIAL) {
- override fun isInSection(entry: ListEntry): Boolean {
+ override fun isInSection(entry: PipelineEntry): Boolean {
return entry.representativeEntry?.channel?.id == SOCIAL_MEDIA_ID
}
@@ -70,7 +70,7 @@
val recsSectioner =
object : NotifSectioner("Recommendations", BUCKET_RECS) {
- override fun isInSection(entry: ListEntry): Boolean {
+ override fun isInSection(entry: PipelineEntry): Boolean {
return entry.representativeEntry?.channel?.id == RECS_ID
}
@@ -81,7 +81,7 @@
val promoSectioner =
object : NotifSectioner("Promotions", BUCKET_PROMO) {
- override fun isInSection(entry: ListEntry): Boolean {
+ override fun isInSection(entry: PipelineEntry): Boolean {
return entry.representativeEntry?.channel?.id == PROMOTIONS_ID
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java
index 9df4bf4..afba85b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java
@@ -25,6 +25,7 @@
import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope;
@@ -96,7 +97,7 @@
private final NotifSectioner mNotifSectioner = new NotifSectioner("ColorizedSectioner",
NotificationPriorityBucketKt.BUCKET_FOREGROUND_SERVICE) {
@Override
- public boolean isInSection(ListEntry entry) {
+ public boolean isInSection(PipelineEntry entry) {
NotificationEntry notificationEntry = entry.getRepresentativeEntry();
if (notificationEntry != null) {
return isRichOngoing(notificationEntry);
@@ -117,7 +118,7 @@
private final NotifComparator mOngoingComparator = new NotifComparator(
"OngoingComparator") {
@Override
- public int compare(@NonNull ListEntry o1, @NonNull ListEntry o2) {
+ public int compare(@NonNull PipelineEntry o1, @NonNull PipelineEntry o2) {
return Integer.compare(
getSortKey(o1.getRepresentativeEntry()),
getSortKey(o2.getRepresentativeEntry())
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
index af2c197..248b528 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
@@ -16,7 +16,8 @@
package com.android.systemui.statusbar.notification.collection.coordinator
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.SortBySectionTimeFlag
@@ -61,6 +62,7 @@
originalGroup == null -> null
originalGroup == promoted.parent -> null
originalGroup.parent == null -> null
+ originalGroup !is GroupEntry -> summary.key
originalGroup.summary != summary -> null
originalGroup.children.any { it.channel == summary.channel } -> null
else -> summary.key
@@ -74,7 +76,7 @@
override fun shouldPromoteToTopLevel(entry: NotificationEntry): Boolean {
val shouldPromote = entry.channel?.isImportantConversation == true
if (shouldPromote) {
- val summary = entry.parent?.summary
+ val summary = (entry.parent as? GroupEntry)?.summary
if (summary != null && entry.channel == summary.channel) {
promotedEntriesToSummaryOfSameChannel[entry] = summary
}
@@ -85,14 +87,14 @@
val priorityPeopleSectioner =
object : NotifSectioner("Priority People", BUCKET_PRIORITY_PEOPLE) {
- override fun isInSection(entry: ListEntry): Boolean {
+ override fun isInSection(entry: PipelineEntry): Boolean {
return getPeopleType(entry) == TYPE_IMPORTANT_PERSON
}
}
// TODO(b/330193582): Rename to just "People"
val peopleAlertingSectioner = object : NotifSectioner("People(alerting)", BUCKET_PEOPLE) {
- override fun isInSection(entry: ListEntry): Boolean {
+ override fun isInSection(entry: PipelineEntry): Boolean {
if (SortBySectionTimeFlag.isEnabled) {
return highPriorityProvider.isHighPriorityConversation(entry)
|| isConversation(entry)
@@ -111,7 +113,7 @@
val peopleSilentSectioner = object : NotifSectioner("People(silent)", BUCKET_PEOPLE) {
// Because the peopleAlertingSectioner is above this one, it will claim all conversations that are alerting.
// All remaining conversations must be silent.
- override fun isInSection(entry: ListEntry): Boolean {
+ override fun isInSection(entry: PipelineEntry): Boolean {
SortBySectionTimeFlag.assertInLegacyMode()
return isConversation(entry)
}
@@ -132,17 +134,17 @@
pipeline.addOnBeforeRenderListListener(onBeforeRenderListListener)
}
- private fun isConversation(entry: ListEntry): Boolean =
+ private fun isConversation(entry: PipelineEntry): Boolean =
getPeopleType(entry) != TYPE_NON_PERSON
@PeopleNotificationType
- private fun getPeopleType(entry: ListEntry): Int =
+ private fun getPeopleType(entry: PipelineEntry): Int =
entry.representativeEntry?.let {
peopleNotificationIdentifier.getPeopleNotificationType(it)
} ?: TYPE_NON_PERSON
private val notifComparator: NotifComparator = object : NotifComparator("People") {
- override fun compare(entry1: ListEntry, entry2: ListEntry): Int {
+ override fun compare(entry1: PipelineEntry, entry2: PipelineEntry): Int {
val type1 = getPeopleType(entry1)
val type2 = getPeopleType(entry2)
return type2.compareTo(type1)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt
index 034a4fd..e4ec76c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreImpl
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -43,12 +43,12 @@
d.dump("notifLiveDataStoreImpl", notifLiveDataStoreImpl)
}
- private fun onAfterRenderList(entries: List<ListEntry>) {
+ private fun onAfterRenderList(entries: List<PipelineEntry>) {
val flatEntryList = flattenedEntryList(entries)
notifLiveDataStoreImpl.setActiveNotifList(flatEntryList)
}
- private fun flattenedEntryList(entries: List<ListEntry>) =
+ private fun flattenedEntryList(entries: List<PipelineEntry>) =
mutableListOf<NotificationEntry>().also { list ->
entries.forEach { entry ->
when (entry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinator.kt
index 0a9dddc..6bed0cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinator.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProviderImpl
@@ -37,7 +37,7 @@
pipeline.addOnBeforeRenderListListener(::onBeforeRenderListListener)
}
- private fun onBeforeRenderListListener(entries: List<ListEntry>) {
+ private fun onBeforeRenderListListener(entries: List<PipelineEntry>) {
val isLocked = !keyguardStateController.isUnlocked
val nonDismissableEntryKeys = mutableSetOf<String>()
markNonDismissibleEntries(nonDismissableEntryKeys, entries, isLocked)
@@ -54,7 +54,7 @@
*/
private fun markNonDismissibleEntries(
markedKeys: MutableSet<String>,
- entries: List<ListEntry>,
+ entries: List<PipelineEntry>,
isLocked: Boolean
): Boolean {
var anyNonDismissableEntries = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinator.kt
index 02bf3b3..2f0701f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinator.kt
@@ -18,7 +18,7 @@
import android.util.ArrayMap
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.render.NotifGroupController
@@ -34,7 +34,7 @@
pipeline.addOnAfterRenderGroupListener(::onAfterRenderGroup)
}
- private fun onBeforeFinalizeFilter(entries: List<ListEntry>) {
+ private fun onBeforeFinalizeFilter(entries: List<PipelineEntry>) {
// save untruncated child counts to our internal map
untruncatedChildCounts.clear()
entries.asSequence().filterIsInstance<GroupEntry>().forEach { groupEntry ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinator.kt
index f253100..d0411b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinator.kt
@@ -18,7 +18,7 @@
import android.util.ArrayMap
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
@@ -52,7 +52,7 @@
pipeline.addPreRenderInvalidator(invalidator)
}
- private fun onBeforeFinalizeFilterListener(entries: List<ListEntry>) {
+ private fun onBeforeFinalizeFilterListener(entries: List<PipelineEntry>) {
cancelListInvalidation()
notificationGroupTimes.clear()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt
index b200136..9045aac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt
@@ -18,7 +18,7 @@
import android.util.ArraySet
import com.android.systemui.Dumpable
import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
@@ -113,7 +113,7 @@
}
}
- private fun isCurrentlyShowingGuts(entry: ListEntry) =
+ private fun isCurrentlyShowingGuts(entry: PipelineEntry) =
notifsWithOpenGuts.contains(entry.key)
private fun closeGutsAndEndLifetimeExtension(entry: NotificationEntry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
index eb5a370..611e0ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
@@ -27,7 +27,7 @@
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -103,7 +103,7 @@
mNotifPipeline = pipeline
mHeadsUpManager.addListener(mOnHeadsUpChangedListener)
pipeline.addCollectionListener(mNotifCollectionListener)
- pipeline.addOnBeforeTransformGroupsListener(::onBeforeTransformGroups)
+ pipeline.addOnBeforeTransformGroupsListener { onBeforeTransformGroups() }
pipeline.addOnBeforeFinalizeFilterListener(::onBeforeFinalizeFilter)
pipeline.addPromoter(mNotifPromoter)
pipeline.addNotificationLifetimeExtender(mLifetimeExtender)
@@ -170,7 +170,7 @@
* Once the pipeline starts running, we can look through posted entries and quickly process any
* that don't have groups, and thus will never gave a group heads up edge case.
*/
- fun onBeforeTransformGroups(list: List<ListEntry>) {
+ fun onBeforeTransformGroups() {
mNow = mSystemClock.currentTimeMillis()
if (mPostedEntries.isEmpty()) {
return
@@ -191,7 +191,7 @@
* we know that stability and [NotifPromoter]s have been applied, so we can use the location of
* notifications in this list to determine what kind of group heads up behavior should happen.
*/
- fun onBeforeFinalizeFilter(list: List<ListEntry>) =
+ fun onBeforeFinalizeFilter(list: List<PipelineEntry>) =
mHeadsUpManager.modifyHuns { hunMutator ->
// Nothing to do if there are no other adds/updates
if (mPostedEntries.isEmpty()) {
@@ -410,7 +410,7 @@
)
.firstOrNull()
- private fun getGroupLocationsByKey(list: List<ListEntry>): Map<String, GroupLocation> =
+ private fun getGroupLocationsByKey(list: List<PipelineEntry>): Map<String, GroupLocation> =
mutableMapOf<String, GroupLocation>().also { map ->
list.forEach { topLevelEntry ->
when (topLevelEntry) {
@@ -833,13 +833,13 @@
val sectioner =
object : NotifSectioner("HeadsUp", BUCKET_HEADS_UP) {
- override fun isInSection(entry: ListEntry): Boolean =
+ override fun isInSection(entry: PipelineEntry): Boolean =
// TODO: This check won't notice if a child of the group is going to HUN...
isGoingToShowHunNoRetract(entry)
override fun getComparator(): NotifComparator {
return object : NotifComparator("HeadsUp") {
- override fun compare(o1: ListEntry, o2: ListEntry): Int =
+ override fun compare(o1: PipelineEntry, o2: PipelineEntry): Int =
mHeadsUpManager.compare(o1.representativeEntry, o2.representativeEntry)
}
}
@@ -867,7 +867,7 @@
private fun isSticky(entry: NotificationEntry) = mHeadsUpManager.isSticky(entry.key)
- private fun isEntryBinding(entry: ListEntry): Boolean {
+ private fun isEntryBinding(entry: PipelineEntry): Boolean {
val bindingUntil = mEntriesBindingUntil[entry.key]
return bindingUntil != null && bindingUntil >= mNow
}
@@ -875,12 +875,12 @@
/**
* Whether the notification is already heads up or binding so that it can imminently heads up
*/
- private fun isAttemptingToShowHun(entry: ListEntry) =
+ private fun isAttemptingToShowHun(entry: PipelineEntry) =
mHeadsUpManager.isHeadsUpEntry(entry.key) ||
isEntryBinding(entry) ||
isHeadsUpAnimatingAway(entry)
- private fun isHeadsUpAnimatingAway(entry: ListEntry): Boolean {
+ private fun isHeadsUpAnimatingAway(entry: PipelineEntry): Boolean {
if (!GroupHunAnimationFix.isEnabled) return false
return entry.representativeEntry?.row?.isHeadsUpAnimatingAway ?: false
}
@@ -891,7 +891,7 @@
* returns `true` even if the update would (in isolation of its group) cause the heads up to be
* retracted. This is important for not retracting transferred group heads ups.
*/
- private fun isGoingToShowHunNoRetract(entry: ListEntry) =
+ private fun isGoingToShowHunNoRetract(entry: PipelineEntry) =
mPostedEntries[entry.key]?.calculateShouldBeHeadsUpNoRetract ?: isAttemptingToShowHun(entry)
/**
@@ -900,7 +900,7 @@
* strict because any update which would revoke the heads up supersedes the current heads
* up/binding state.
*/
- private fun isGoingToShowHunStrict(entry: ListEntry) =
+ private fun isGoingToShowHunStrict(entry: PipelineEntry) =
mPostedEntries[entry.key]?.calculateShouldBeHeadsUpStrict ?: isAttemptingToShowHun(entry)
private fun endNotifLifetimeExtensionIfExtended(entry: NotificationEntry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt
index e232849..56deb18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt
@@ -27,7 +27,7 @@
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
@@ -176,7 +176,7 @@
}
}
- private fun pickOutTopUnseenNotifs(list: List<ListEntry>) {
+ private fun pickOutTopUnseenNotifs(list: List<PipelineEntry>) {
if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) return
if (!minimalismEnabled) return
// Only ever elevate a top unseen notification on keyguard, not even locked shade
@@ -224,7 +224,7 @@
val topOngoingSectioner =
object : NotifSectioner("TopOngoing", BUCKET_TOP_ONGOING) {
- override fun isInSection(entry: ListEntry): Boolean {
+ override fun isInSection(entry: PipelineEntry): Boolean {
if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) return false
if (!minimalismEnabled) return false
return entry.anyEntry { notificationEntry ->
@@ -235,7 +235,7 @@
val topUnseenSectioner =
object : NotifSectioner("TopUnseen", BUCKET_TOP_UNSEEN) {
- override fun isInSection(entry: ListEntry): Boolean {
+ override fun isInSection(entry: PipelineEntry): Boolean {
if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) return false
if (!minimalismEnabled) return false
return entry.anyEntry { notificationEntry ->
@@ -244,7 +244,7 @@
}
}
- private fun ListEntry.anyEntry(predicate: (NotificationEntry?) -> Boolean) =
+ private fun PipelineEntry.anyEntry(predicate: (NotificationEntry?) -> Boolean) =
when {
predicate(representativeEntry) -> true
this !is GroupEntry -> false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt
index de6f257..660ee40 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt
@@ -30,6 +30,7 @@
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.expansionChanges
+import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
@@ -313,7 +314,7 @@
unseenNotifications.contains(entry) -> false
// Don't apply the filter to (non-promoted) group summaries
// - summary will be pruned if necessary, depending on if children are filtered
- entry.parent?.summary == entry -> false
+ (entry.parent as? GroupEntry)?.summary == entry -> false
// Check that the entry satisfies certain characteristics that would bypass the
// filter
shouldIgnoreUnseenCheck(entry) -> false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index 2ecce1f..20c6736 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -35,7 +35,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
@@ -232,9 +232,10 @@
*/
@Override
public boolean shouldFilterOut(NotificationEntry entry, long now) {
- final GroupEntry parent = requireNonNull(entry.getParent());
- Boolean isMemberOfDelayedGroup = mIsDelayedGroupCache.get(parent);
- if (isMemberOfDelayedGroup == null) {
+ final PipelineEntry pipelineEntryParent = requireNonNull(entry.getParent());
+ Boolean isMemberOfDelayedGroup = mIsDelayedGroupCache.get(pipelineEntryParent);
+ if (isMemberOfDelayedGroup == null && pipelineEntryParent instanceof GroupEntry) {
+ GroupEntry parent = (GroupEntry) pipelineEntryParent;
isMemberOfDelayedGroup = shouldWaitForGroupToInflate(parent, now);
mIsDelayedGroupCache.put(parent, isMemberOfDelayedGroup);
}
@@ -279,7 +280,7 @@
}
};
- private void purgeCaches(Collection<ListEntry> entries) {
+ private void purgeCaches(Collection<PipelineEntry> entries) {
Set<String> wantedPackages = getPackages(entries);
mAppIconProvider.purgeCache(wantedPackages);
mNotificationIconStyleProvider.purgeCache(wantedPackages);
@@ -288,9 +289,9 @@
/**
* Get all app packages present in {@param entries}.
*/
- private static @NonNull Set<String> getPackages(Collection<ListEntry> entries) {
+ private static @NonNull Set<String> getPackages(Collection<PipelineEntry> entries) {
Set<String> packages = new HashSet<>();
- for (ListEntry entry : entries) {
+ for (PipelineEntry entry : entries) {
NotificationEntry notificationEntry = entry.getRepresentativeEntry();
if (notificationEntry == null) {
Log.wtf(TAG, "notification entry " + entry.getKey()
@@ -302,9 +303,9 @@
return packages;
}
- private void inflateAllRequiredViews(List<ListEntry> entries) {
+ private void inflateAllRequiredViews(List<PipelineEntry> entries) {
for (int i = 0, size = entries.size(); i < size; i++) {
- ListEntry entry = entries.get(i);
+ PipelineEntry entry = entries.get(i);
if (entry instanceof GroupEntry) {
GroupEntry groupEntry = (GroupEntry) entry;
inflateRequiredGroupViews(groupEntry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index 9226450..d1063d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -20,7 +20,7 @@
import android.annotation.Nullable;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope;
@@ -97,7 +97,7 @@
private final NotifSectioner mAlertingNotifSectioner = new NotifSectioner("Alerting",
NotificationPriorityBucketKt.BUCKET_ALERTING) {
@Override
- public boolean isInSection(ListEntry entry) {
+ public boolean isInSection(PipelineEntry entry) {
return mHighPriorityProvider.isHighPriority(entry);
}
@@ -115,7 +115,7 @@
private final NotifSectioner mSilentNotifSectioner = new NotifSectioner("Silent",
NotificationPriorityBucketKt.BUCKET_SILENT) {
@Override
- public boolean isInSection(ListEntry entry) {
+ public boolean isInSection(PipelineEntry entry) {
return !mHighPriorityProvider.isHighPriority(entry)
&& entry.getRepresentativeEntry() != null
&& !entry.getRepresentativeEntry().isAmbient();
@@ -129,7 +129,7 @@
@Nullable
@Override
- public void onEntriesUpdated(@NonNull List<ListEntry> entries) {
+ public void onEntriesUpdated(@NonNull List<PipelineEntry> entries) {
mHasSilentEntries = false;
for (int i = 0; i < entries.size(); i++) {
if (entries.get(i).getRepresentativeEntry().getSbn().isClearable()) {
@@ -145,7 +145,7 @@
private final NotifSectioner mMinimizedNotifSectioner = new NotifSectioner("Minimized",
NotificationPriorityBucketKt.BUCKET_SILENT) {
@Override
- public boolean isInSection(ListEntry entry) {
+ public boolean isInSection(PipelineEntry entry) {
return !mHighPriorityProvider.isHighPriority(entry)
&& entry.getRepresentativeEntry().isAmbient();
}
@@ -158,7 +158,7 @@
@Nullable
@Override
- public void onEntriesUpdated(@NonNull List<ListEntry> entries) {
+ public void onEntriesUpdated(@NonNull List<PipelineEntry> entries) {
mHasMinimizedEntries = false;
for (int i = 0; i < entries.size(); i++) {
if (entries.get(i).getRepresentativeEntry().getSbn().isClearable()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinator.kt
index 4a7b7ca..930e52a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinator.kt
@@ -17,7 +17,7 @@
import android.util.ArrayMap
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
@@ -39,7 +39,7 @@
pipeline.addOnAfterRenderEntryListener(::onAfterRenderEntry)
}
- private fun onBeforeFinalizeFilterListener(entries: List<ListEntry>) {
+ private fun onBeforeFinalizeFilterListener(entries: List<PipelineEntry>) {
latestAlertTimeBySummary.clear()
entries.asSequence().filterIsInstance<GroupEntry>().forEach { groupEntry ->
val summary = checkNotNull(groupEntry.summary)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
index df8e56e..a987c54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
@@ -20,7 +20,7 @@
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.AssistantFeedbackController
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
@@ -63,7 +63,7 @@
pipeline.addOnAfterRenderEntryListener(::onAfterRenderEntry)
}
- private fun onBeforeRenderList(list: List<ListEntry>) {
+ private fun onBeforeRenderList(list: List<PipelineEntry>) {
entryToExpand =
list.firstOrNull()?.representativeEntry?.takeIf { entry ->
!mSectionStyleProvider.isMinimizedSection(entry.section!!)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
index 90916d1..db24e7d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
@@ -32,7 +32,7 @@
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.DynamicPrivacyController
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
@@ -155,7 +155,7 @@
}
}
- override fun onBeforeRenderList(entries: List<ListEntry>) {
+ override fun onBeforeRenderList(entries: List<PipelineEntry>) {
if (
isKeyguardGoingAway ||
statusBarStateController.state == StatusBarState.KEYGUARD &&
@@ -220,13 +220,15 @@
}
}
-private fun extractAllRepresentativeEntries(entries: List<ListEntry>): Sequence<NotificationEntry> =
+private fun extractAllRepresentativeEntries(entries: List<PipelineEntry>): Sequence<NotificationEntry> =
entries.asSequence().flatMap(::extractAllRepresentativeEntries)
-private fun extractAllRepresentativeEntries(listEntry: ListEntry): Sequence<NotificationEntry> =
+private fun extractAllRepresentativeEntries(
+ pipelineEntry: PipelineEntry,
+): Sequence<NotificationEntry> =
sequence {
- listEntry.representativeEntry?.let { yield(it) }
- if (listEntry is GroupEntry) {
- yieldAll(extractAllRepresentativeEntries(listEntry.children))
+ pipelineEntry.representativeEntry?.let { yield(it) }
+ if (pipelineEntry is GroupEntry) {
+ yieldAll(extractAllRepresentativeEntries(pipelineEntry.children))
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinator.kt
index 1c66db7..29a8eb0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinator.kt
@@ -19,7 +19,7 @@
import android.service.notification.NotificationListenerService
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
@@ -67,7 +67,7 @@
mShadeEmptiedCallback = callback
}
- private fun onBeforeRenderList(entries: List<ListEntry>) {
+ private fun onBeforeRenderList(entries: List<PipelineEntry>) {
if (mEntryRemoved && entries.isEmpty()) {
mLogger.logShadeEmptied()
// TODO(b/206023518): This was bad. Do not copy this.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
index 1cb2366..53a73f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
@@ -19,7 +19,7 @@
import com.android.app.tracing.traceSection
import com.android.server.notification.Flags.screenshareNotificationHiding
import com.android.systemui.Flags.screenshareNotificationHidingBugFix
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl
@@ -50,14 +50,14 @@
groupExpansionManagerImpl.attach(pipeline)
}
- private fun onAfterRenderList(entries: List<ListEntry>) =
+ private fun onAfterRenderList(entries: List<PipelineEntry>) =
traceSection("StackCoordinator.onAfterRenderList") {
val notifStats = calculateNotifStats(entries)
activeNotificationsInteractor.setNotifStats(notifStats)
renderListInteractor.setRenderedList(entries)
}
- private fun calculateNotifStats(entries: List<ListEntry>): NotifStats {
+ private fun calculateNotifStats(entries: List<PipelineEntry>): NotifStats {
var hasNonClearableAlertingNotifs = false
var hasClearableAlertingNotifs = false
var hasNonClearableSilentNotifs = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 49d5029..3e5655a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -39,7 +39,7 @@
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
@@ -253,21 +253,20 @@
return false;
}
- final GroupEntry parent = entry.getParent();
+ final PipelineEntry parent = entry.getParent();
if (parent == null) {
return false;
}
-
return isHeadsUpGroup(parent);
}
- private boolean isHeadsUpGroup(GroupEntry groupEntry) {
- if (StabilizeHeadsUpGroup.isUnexpectedlyInLegacyMode()) {
+ private boolean isHeadsUpGroup(PipelineEntry pipelineEntry) {
+ if (!(pipelineEntry instanceof GroupEntry groupEntry)) {
return false;
}
- if (groupEntry == null) {
+ if (StabilizeHeadsUpGroup.isUnexpectedlyInLegacyMode()) {
return false;
}
@@ -370,7 +369,7 @@
}
@Override
- public boolean isEntryReorderingAllowed(@NonNull ListEntry entry) {
+ public boolean isEntryReorderingAllowed(@NonNull PipelineEntry entry) {
if (StabilizeHeadsUpGroup.isEnabled()) {
if (isEveryChangeAllowed()) {
return true;
@@ -403,7 +402,7 @@
private final OnBeforeRenderListListener mOnBeforeRenderListListener =
new OnBeforeRenderListListener() {
@Override
- public void onBeforeRenderList(List<ListEntry> entries) {
+ public void onBeforeRenderList(List<PipelineEntry> entries) {
if (StabilizeHeadsUpGroup.isUnexpectedlyInLegacyMode()) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
index 465bc28..adcc3ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
@@ -124,7 +124,7 @@
val parent = entry.parent ?: error("Entry must have a parent to determine if minimized")
val isMinimizedSection = sectionStyleProvider.isMinimizedSection(section)
val isTopLevelEntry = parent == GroupEntry.ROOT_ENTRY
- val isGroupSummary = parent.summary == entry
+ val isGroupSummary = (parent as? GroupEntry)?.summary == entry
return isMinimizedSection && (isTopLevelEntry || isGroupSummary)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnAfterRenderListListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnAfterRenderListListener.java
index ac450c0..e3ab81c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnAfterRenderListListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnAfterRenderListListener.java
@@ -18,7 +18,7 @@
import androidx.annotation.NonNull;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import java.util.List;
@@ -31,5 +31,5 @@
* @param entries The current list of top-level entries. Note that this is a live view into the
* current list and will change whenever the pipeline is rerun.
*/
- void onAfterRenderList(@NonNull List<ListEntry> entries);
+ void onAfterRenderList(@NonNull List<PipelineEntry> entries);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeFinalizeFilterListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeFinalizeFilterListener.java
index 086661e..3c4f422 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeFinalizeFilterListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeFinalizeFilterListener.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.collection.listbuilder;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import java.util.List;
@@ -30,5 +30,5 @@
* @param entries The current list of top-level entries. Note that this is a live view into the
* current list and will change whenever the pipeline is rerun.
*/
- void onBeforeFinalizeFilter(List<ListEntry> entries);
+ void onBeforeFinalizeFilter(List<PipelineEntry> entries);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeRenderListListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeRenderListListener.java
index 44a27a4..6ceb7d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeRenderListListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeRenderListListener.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.collection.listbuilder;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import java.util.List;
@@ -30,5 +30,5 @@
* @param entries The current list of top-level entries. Note that this is a live view into the
* current list and will change whenever the pipeline is rerun.
*/
- void onBeforeRenderList(List<ListEntry> entries);
+ void onBeforeRenderList(List<PipelineEntry> entries);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeSortListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeSortListener.java
index 56cfe5c..858cec1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeSortListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeSortListener.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.collection.listbuilder;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import java.util.List;
@@ -30,5 +30,5 @@
* @param entries The current list of top-level entries. Note that this is a live view into the
* current list and will change whenever the pipeline is rerun.
*/
- void onBeforeSort(List<ListEntry> entries);
+ void onBeforeSort(List<PipelineEntry> entries);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeTransformGroupsListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeTransformGroupsListener.java
index 0dc4df0..eb525c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeTransformGroupsListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeTransformGroupsListener.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.collection.listbuilder;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
@@ -35,5 +35,5 @@
* a live view into the current notif list and will change as the list moves through
* the pipeline.
*/
- void onBeforeTransformGroups(List<ListEntry> list);
+ void onBeforeTransformGroups(List<PipelineEntry> list);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderHelper.kt
index d8f75f6..0d8a64a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderHelper.kt
@@ -16,10 +16,10 @@
package com.android.systemui.statusbar.notification.collection.listbuilder
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
object ShadeListBuilderHelper {
- fun getSectionSubLists(entries: List<ListEntry>): Iterable<List<ListEntry>> =
+ fun getSectionSubLists(entries: List<PipelineEntry>): Iterable<List<PipelineEntry>> =
getContiguousSubLists(entries, minLength = 1) { it.sectionIndex }
inline fun <T : Any, K : Any> getContiguousSubLists(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
index a8409d0c..8a9548f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
@@ -23,7 +23,7 @@
import com.android.systemui.log.core.LogLevel.WARNING
import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.StateName
import com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.getStateName
@@ -152,9 +152,9 @@
fun logEntryAttachStateChanged(
buildId: Int,
- entry: ListEntry,
- prevParent: GroupEntry?,
- newParent: GroupEntry?
+ entry: PipelineEntry,
+ prevParent: PipelineEntry?,
+ newParent: PipelineEntry?
) {
buffer.log(TAG, INFO, {
long1 = buildId.toLong()
@@ -177,7 +177,7 @@
})
}
- fun logParentChanged(buildId: Int, prevParent: GroupEntry?, newParent: GroupEntry?) {
+ fun logParentChanged(buildId: Int, prevParent: PipelineEntry?, newParent: PipelineEntry?) {
buffer.log(TAG, INFO, {
long1 = buildId.toLong()
str1 = prevParent?.logKey
@@ -195,8 +195,8 @@
fun logParentChangeSuppressedStarted(
buildId: Int,
- suppressedParent: GroupEntry?,
- keepingParent: GroupEntry?
+ suppressedParent: PipelineEntry?,
+ keepingParent: PipelineEntry?
) {
buffer.log(TAG, INFO, {
long1 = buildId.toLong()
@@ -209,8 +209,8 @@
fun logParentChangeSuppressedStopped(
buildId: Int,
- previouslySuppressedParent: GroupEntry?,
- previouslyKeptParent: GroupEntry?
+ previouslySuppressedParent: PipelineEntry?,
+ previouslyKeptParent: PipelineEntry?
) {
buffer.log(TAG, INFO, {
long1 = buildId.toLong()
@@ -224,7 +224,7 @@
fun logGroupPruningSuppressed(
buildId: Int,
- keepingParent: GroupEntry?
+ keepingParent: PipelineEntry?
) {
buffer.log(TAG, INFO, {
long1 = buildId.toLong()
@@ -310,7 +310,7 @@
val logRankInFinalList = Compile.IS_DEBUG && notifPipelineFlags.isDevLoggingEnabled()
- fun logFinalList(entries: List<ListEntry>) {
+ fun logFinalList(entries: List<PipelineEntry>) {
if (entries.isEmpty()) {
buffer.log(TAG, DEBUG, {}, { "(empty list)" })
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java
index f7bbd28..f7c74c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java
@@ -18,7 +18,7 @@
import androidx.annotation.NonNull;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import java.util.Comparator;
@@ -29,7 +29,7 @@
*/
public abstract class NotifComparator
extends Pluggable<NotifComparator>
- implements Comparator<ListEntry> {
+ implements Comparator<PipelineEntry> {
protected NotifComparator(String name) {
super(name);
@@ -41,5 +41,5 @@
* @return a negative integer, zero, or a positive integer as the first argument is less than
* equal to, or greater than the second (same as standard Comparator<> interface).
*/
- public abstract int compare(@NonNull ListEntry o1, @NonNull ListEntry o2);
+ public abstract int compare(@NonNull PipelineEntry o1, @NonNull PipelineEntry o2);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java
index 8c52c53..4a856a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java
@@ -18,7 +18,7 @@
import android.annotation.Nullable;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
import com.android.systemui.statusbar.notification.collection.render.NodeController;
import com.android.systemui.statusbar.notification.collection.render.NodeSpec;
@@ -52,11 +52,11 @@
* However, this doesn't necessarily mean that your section will get called on each top-level
* notification. The first section to return true determines the section of the notification.
*/
- public abstract boolean isInSection(ListEntry entry);
+ public abstract boolean isInSection(PipelineEntry entry);
/**
* Returns an optional {@link NotifComparator} to sort entries only in this section.
- * {@link ListEntry} instances passed to this comparator are guaranteed to have this section,
+ * {@link PipelineEntry} instances passed to this comparator are guaranteed to have this section,
* and this ordering will take precedence over any global comparators supplied to {@link
* com.android.systemui.statusbar.notification.collection.NotifPipeline#setComparators(List)}.
*
@@ -80,5 +80,5 @@
* Notify of children of this section being updated
* @param entries of this section that are borrowed (must clone to store)
*/
- public void onEntriesUpdated(List<ListEntry> entries) {}
+ public void onEntriesUpdated(List<PipelineEntry> entries) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt
index 58bbca8..e1e2bcf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.collection.listbuilder.pluggable
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
/**
@@ -75,7 +75,7 @@
* being suppressed. However, if an order change is suppressed, that will be reported to ths
* implementation by calling [onEntryReorderSuppressed] after ordering is complete.
*/
- abstract fun isEntryReorderingAllowed(entry: ListEntry): Boolean
+ abstract fun isEntryReorderingAllowed(entry: PipelineEntry): Boolean
/**
* Called by the pipeline to determine if every call to the other stability methods would
@@ -100,7 +100,7 @@
override fun isGroupChangeAllowed(entry: NotificationEntry): Boolean = true
override fun isGroupPruneAllowed(entry: GroupEntry): Boolean = true
override fun isSectionChangeAllowed(entry: NotificationEntry): Boolean = true
- override fun isEntryReorderingAllowed(entry: ListEntry): Boolean = true
+ override fun isEntryReorderingAllowed(entry: PipelineEntry): Boolean = true
override fun isEveryChangeAllowed(): Boolean = true
override fun onEntryReorderSuppressed() {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
index 014ac54..ae2c70a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
@@ -27,6 +27,7 @@
import com.android.systemui.log.core.LogLevel.INFO
import com.android.systemui.log.core.LogLevel.WARNING
import com.android.systemui.log.core.LogLevel.WTF
+import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason
import com.android.systemui.statusbar.notification.collection.NotifCollection.FutureDismissal
@@ -421,6 +422,14 @@
})
}
+ private fun getParentLogKey(childEntry: NotificationEntry): String {
+ return if (childEntry.parent is GroupEntry) {
+ (childEntry.parent as? GroupEntry)?.summary?.logKey ?: "(null)"
+ } else {
+ "(null)"
+ }
+ }
+
fun logDismissAlreadyParentDismissedNotif(
childEntry: NotificationEntry,
childIndex: Int,
@@ -430,7 +439,7 @@
str1 = childEntry.logKey
int1 = childIndex
int2 = childCount
- str2 = childEntry.parent?.summary?.logKey ?: "(null)"
+ str2 = getParentLogKey(childEntry)
}, {
"DISMISS Already Parent-Dismissed $str1 ($int1/$int2) with summary $str2"
})
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
index 2e3ab92..051bd3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
@@ -23,7 +23,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
@@ -53,7 +53,7 @@
}
/**
- * @return true if the ListEntry is high priority, else false
+ * @return true if the PipelineEntry is high priority, else false
*
* A NotificationEntry is considered high priority if it:
* - has importance greater than or equal to IMPORTANCE_DEFAULT
@@ -67,12 +67,12 @@
* A GroupEntry is considered high priority if its representativeEntry (summary) or any of its
* children are high priority.
*/
- public boolean isHighPriority(@Nullable ListEntry entry) {
+ public boolean isHighPriority(@Nullable PipelineEntry entry) {
return isHighPriority(entry, /* allowImplicit = */ true);
}
/**
- * @return true if the ListEntry is explicitly high priority, else false
+ * @return true if the PipelineEntry is explicitly high priority, else false
*
* A NotificationEntry is considered explicitly high priority if has importance greater than or
* equal to IMPORTANCE_DEFAULT.
@@ -80,11 +80,11 @@
* A GroupEntry is considered explicitly high priority if its representativeEntry (summary) or
* any of its children are explicitly high priority.
*/
- public boolean isExplicitlyHighPriority(@Nullable ListEntry entry) {
+ public boolean isExplicitlyHighPriority(@Nullable PipelineEntry entry) {
return isHighPriority(entry, /* allowImplicit= */ false);
}
- private boolean isHighPriority(@Nullable ListEntry entry, boolean allowImplicit) {
+ private boolean isHighPriority(@Nullable PipelineEntry entry, boolean allowImplicit) {
if (entry == null) {
return false;
}
@@ -100,9 +100,9 @@
}
/**
- * @return true if the ListEntry is high priority conversation, else false
+ * @return true if the PipelineEntry is high priority conversation, else false
*/
- public boolean isHighPriorityConversation(@NonNull ListEntry entry) {
+ public boolean isHighPriorityConversation(@NonNull PipelineEntry entry) {
final NotificationEntry notifEntry = entry.getRepresentativeEntry();
if (notifEntry == null) {
return false;
@@ -119,7 +119,7 @@
return isNotificationEntryWithAtLeastOneImportantChild(entry);
}
- private boolean isNotificationEntryWithAtLeastOneImportantChild(@NonNull ListEntry entry) {
+ private boolean isNotificationEntryWithAtLeastOneImportantChild(@NonNull PipelineEntry entry) {
if (!(entry instanceof GroupEntry)) {
return false;
}
@@ -134,7 +134,7 @@
* Returns whether the given ListEntry has a high priority child or is in a group with a child
* that's high priority
*/
- private boolean hasHighPriorityChild(ListEntry entry, boolean allowImplicit) {
+ private boolean hasHighPriorityChild(PipelineEntry entry, boolean allowImplicit) {
if (NotificationBundleUi.isEnabled()) {
GroupEntry representativeGroupEntry = null;
if (entry instanceof GroupEntry) {
@@ -142,9 +142,10 @@
} else if (entry instanceof NotificationEntry){
final NotificationEntry notificationEntry = entry.getRepresentativeEntry();
if (notificationEntry.getParent() != null
- && notificationEntry.getParent().getSummary() != null
- && notificationEntry.getParent().getSummary() == notificationEntry) {
- representativeGroupEntry = notificationEntry.getParent();
+ && notificationEntry.getParent() instanceof GroupEntry parent
+ && parent.getSummary() != null
+ && parent.getSummary() == notificationEntry) {
+ representativeGroupEntry = parent;
}
}
return representativeGroupEntry != null &&
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProvider.kt
index ea9f295..f418bb6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProvider.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.collection.provider
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.SortBySectionTimeFlag
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
@@ -53,7 +53,7 @@
* Determine if the given entry is minimized.
*/
@JvmOverloads
- fun isMinimized(entry: ListEntry, ifNotInSection: Boolean = true): Boolean {
+ fun isMinimized(entry: PipelineEntry, ifNotInSection: Boolean = true): Boolean {
val section = entry.section ?: return ifNotInSection
return isMinimizedSection(section)
}
@@ -77,7 +77,7 @@
* Determine if the given entry is silent.
*/
@JvmOverloads
- fun isSilent(entry: ListEntry, ifNotInSection: Boolean = true): Boolean {
+ fun isSilent(entry: PipelineEntry, ifNotInSection: Boolean = true): Boolean {
val section = entry.section ?: return ifNotInSection
if (SortBySectionTimeFlag.isEnabled) {
if (entry.section?.bucket == BUCKET_PEOPLE) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
index 16b98e2..b179a69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
@@ -25,7 +25,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
@@ -81,7 +81,7 @@
}
final Set<NotificationEntry> renderingSummaries = new HashSet<>();
- for (ListEntry entry : entries) {
+ for (PipelineEntry entry : entries) {
if (entry instanceof GroupEntry) {
renderingSummaries.add(entry.getRepresentativeEntry());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java
index 69267e5..3edbfaf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java
@@ -22,6 +22,7 @@
import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import java.util.List;
@@ -78,5 +79,5 @@
* @return list of the children
*/
@Nullable
- List<NotificationEntry> getChildren(@NonNull ListEntry summary);
+ List<NotificationEntry> getChildren(@NonNull PipelineEntry summary);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
index 80a9f8ad..a1a23e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
@@ -24,7 +24,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
@@ -48,8 +48,13 @@
// The entry is not attached, so it doesn't count.
return false;
}
+ PipelineEntry pipelineEntry = entry.getParent();
+ if (!(pipelineEntry instanceof GroupEntry groupEntry)) {
+ return false;
+ }
+
// If entry is a summary, its parent is a GroupEntry with summary = entry.
- return entry.getParent().getSummary() == entry;
+ return groupEntry.getSummary() == entry;
}
@Override
@@ -65,7 +70,10 @@
if (isTopLevelEntry(entry) || entry.getParent() == null) {
return null;
}
- return entry.getParent().getSummary();
+ if (entry.getParent() instanceof GroupEntry parent) {
+ return parent.getSummary();
+ }
+ return null;
}
@Nullable
@@ -91,8 +99,9 @@
@Nullable
@Override
- public List<NotificationEntry> getChildren(@NonNull ListEntry entry) {
+ public List<NotificationEntry> getChildren(@NonNull PipelineEntry entry) {
NotificationBundleUi.assertInLegacyMode();
+
if (entry instanceof GroupEntry) {
return ((GroupEntry) entry).getChildren();
}
@@ -100,8 +109,7 @@
NotificationEntry representativeEntry = entry.getRepresentativeEntry();
if (representativeEntry != null && isGroupSummary(representativeEntry)) {
// maybe we were actually passed the summary
- GroupEntry parent = representativeEntry.getParent();
- if (parent != null) {
+ if (representativeEntry.getParent() instanceof GroupEntry parent) {
return parent.getChildren();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
index 9fc4e40..e173194 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
@@ -18,12 +18,12 @@
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
import com.android.systemui.util.Compile
import com.android.app.tracing.traceSection
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
/**
* Converts a notif list (the output of the ShadeListBuilder) into a NodeSpec, an abstract
@@ -45,7 +45,7 @@
fun buildNodeSpec(
rootController: NodeController,
- notifList: List<ListEntry>
+ notifList: List<PipelineEntry>
): NodeSpec = traceSection("NodeSpecBuilder.buildNodeSpec") {
val root = NodeSpecImpl(null, rootController)
@@ -100,7 +100,7 @@
return@traceSection root
}
- private fun buildNotifNode(parent: NodeSpec, entry: ListEntry): NodeSpec = when (entry) {
+ private fun buildNotifNode(parent: NodeSpec, entry: PipelineEntry): NodeSpec = when (entry) {
is NotificationEntry -> NodeSpecImpl(parent, viewBarn.requireNodeController(entry))
is GroupEntry ->
NodeSpecImpl(parent, viewBarn.requireNodeController(checkNotNull(entry.summary)))
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewBarn.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewBarn.kt
index fd91d5a..56662f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewBarn.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewBarn.kt
@@ -18,18 +18,18 @@
import android.view.textclassifier.Log
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import javax.inject.Inject
/**
- * The ViewBarn is just a map from [ListEntry] to an instance of a [NodeController].
+ * The ViewBarn is just a map from [PipelineEntry] to an instance of a [NodeController].
*/
@SysUISingleton
class NotifViewBarn @Inject constructor() {
private val rowMap = mutableMapOf<String, NotifViewController>()
- fun requireNodeController(entry: ListEntry): NodeController {
+ fun requireNodeController(entry: PipelineEntry): NodeController {
if (DEBUG) {
Log.d(TAG, "requireNodeController: ${entry.key}")
}
@@ -50,14 +50,14 @@
return rowMap[entry.key] ?: error("No view has been registered for entry: ${entry.key}")
}
- fun registerViewForEntry(entry: ListEntry, controller: NotifViewController) {
+ fun registerViewForEntry(entry: PipelineEntry, controller: NotifViewController) {
if (DEBUG) {
Log.d(TAG, "registerViewForEntry: ${entry.key}")
}
rowMap[entry.key] = controller
}
- fun removeViewForEntry(entry: ListEntry) {
+ fun removeViewForEntry(entry: PipelineEntry) {
if (DEBUG) {
Log.d(TAG, "removeViewForEntry: ${entry.key}")
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewRenderer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewRenderer.kt
index 8284022..396d47e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewRenderer.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.collection.render
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
/**
@@ -34,7 +34,7 @@
* also ensure that future calls to [getStackController], [getGroupController], and
* [getRowController] will provide valid results.
*/
- fun onRenderList(notifList: List<ListEntry>)
+ fun onRenderList(notifList: List<PipelineEntry>)
/**
* Provides an interface for the pipeline to update individual groups. This will be called at
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
index 21e6837..93e844d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
@@ -19,7 +19,7 @@
import com.android.app.tracing.traceSection
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.PipelineDumpable
import com.android.systemui.statusbar.notification.collection.PipelineDumper
@@ -46,7 +46,7 @@
listBuilder.setOnRenderListListener(::onRenderList)
}
- private fun onRenderList(notifList: List<ListEntry>) {
+ private fun onRenderList(notifList: List<PipelineEntry>) {
traceSection("RenderStageManager.onRenderList") {
val viewRenderer = viewRenderer ?: return
viewRenderer.onRenderList(notifList)
@@ -85,7 +85,7 @@
dump("onAfterRenderEntryListeners", onAfterRenderEntryListeners)
}
- private fun dispatchOnAfterRenderList(entries: List<ListEntry>) {
+ private fun dispatchOnAfterRenderList(entries: List<PipelineEntry>) {
traceSection("RenderStageManager.dispatchOnAfterRenderList") {
onAfterRenderListListeners.forEach { listener -> listener.onAfterRenderList(entries) }
}
@@ -93,7 +93,7 @@
private fun dispatchOnAfterRenderGroups(
viewRenderer: NotifViewRenderer,
- entries: List<ListEntry>,
+ entries: List<PipelineEntry>,
) {
traceSection("RenderStageManager.dispatchOnAfterRenderGroups") {
if (onAfterRenderGroupListeners.isEmpty()) {
@@ -110,7 +110,7 @@
private fun dispatchOnAfterRenderEntries(
viewRenderer: NotifViewRenderer,
- entries: List<ListEntry>,
+ entries: List<PipelineEntry>,
) {
traceSection("RenderStageManager.dispatchOnAfterRenderEntries") {
if (onAfterRenderEntryListeners.isEmpty()) {
@@ -129,7 +129,7 @@
* Performs a forward, depth-first traversal of the list where the group's summary immediately
* precedes the group's children.
*/
- private inline fun List<ListEntry>.forEachNotificationEntry(
+ private inline fun List<PipelineEntry>.forEachNotificationEntry(
action: (NotificationEntry) -> Unit
) {
forEach { entry ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index 72316bf..9532d6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -22,7 +22,7 @@
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.PipelineDumpable
import com.android.systemui.statusbar.notification.collection.PipelineDumper
@@ -76,7 +76,7 @@
private val viewRenderer =
object : NotifViewRenderer {
- override fun onRenderList(notifList: List<ListEntry>) {
+ override fun onRenderList(notifList: List<PipelineEntry>) {
traceSection("ShadeViewManager.onRenderList") {
viewDiffer.applySpec(specBuilder.buildNodeSpec(rootController, notifList))
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
index bde3c4d..adc049e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
@@ -29,7 +29,7 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
@@ -57,11 +57,11 @@
/**
* Sets the current list of rendered notification entries as displayed in the notification list.
*/
- fun setRenderedList(entries: List<ListEntry>) {
+ fun setRenderedList(entries: List<PipelineEntry>) {
traceSection("RenderNotificationListInteractor.setRenderedList") {
repository.activeNotifications.update { existingModels ->
buildActiveNotificationsStore(existingModels, sectionStyleProvider, context) {
- entries.forEach(::addListEntry)
+ entries.forEach(::addPipelineEntry)
setRankingsMap(entries)
}
}
@@ -89,11 +89,11 @@
fun build(): ActiveNotificationsStore = builder.build()
/**
- * Convert a [ListEntry] into [ActiveNotificationEntryModel]s, and add them to the
+ * Convert a [PipelineEntry] into [ActiveNotificationEntryModel]s, and add them to the
* [ActiveNotificationsStore]. Special care is taken to avoid re-allocating models if the result
* would be identical to an existing model (at the expense of additional computations).
*/
- fun addListEntry(entry: ListEntry) {
+ fun addPipelineEntry(entry: PipelineEntry) {
when (entry) {
is GroupEntry -> {
entry.summary?.let { summary ->
@@ -116,11 +116,11 @@
}
}
- fun setRankingsMap(entries: List<ListEntry>) {
+ fun setRankingsMap(entries: List<PipelineEntry>) {
builder.setRankingsMap(flatMapToRankingsMap(entries))
}
- fun flatMapToRankingsMap(entries: List<ListEntry>): Map<String, Int> {
+ fun flatMapToRankingsMap(entries: List<PipelineEntry>): Map<String, Int> {
val result = ArrayMap<String, Int>()
for (entry in entries) {
if (entry is NotificationEntry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
index c7548aa..5096894 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
@@ -19,7 +19,7 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -198,7 +198,7 @@
else -> false
}
- private fun shouldHideIfEntrySilent(entry: ListEntry): Boolean =
+ private fun shouldHideIfEntrySilent(entry: PipelineEntry): Boolean =
when {
// Show if explicitly high priority (not hidden)
highPriorityProvider.isExplicitlyHighPriority(entry) -> false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
index f755dbb..002230e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
@@ -20,6 +20,7 @@
import android.service.notification.NotificationListenerService.Ranking
import android.service.notification.StatusBarNotification
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.PeopleNotificationType
@@ -117,8 +118,8 @@
if (!entry.sbn.notification.isGroupSummary) {
return TYPE_NON_PERSON;
}
-
- return getPeopleTypeForChildList(entry.parent?.children)
+ val parent = entry.parent as? GroupEntry ?: return TYPE_NON_PERSON
+ return getPeopleTypeForChildList(parent.children)
} else {
if (!groupManager.isGroupSummary(entry)) {
return TYPE_NON_PERSON
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
index 9aa5a2e..7959e99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.promoted
import android.app.Flags
+import android.app.Flags.notificationsRedesignTemplates
import android.app.Notification
import android.graphics.PorterDuff
import android.view.LayoutInflater
@@ -166,7 +167,10 @@
private val closeButton: View? = root.findViewById(R.id.close_button)
private val conversationIconBadge: View? = root.findViewById(R.id.conversation_icon_badge)
private val conversationIcon: CachingIconView? = root.findViewById(R.id.conversation_icon)
- private val conversationText: TextView? = root.findViewById(R.id.conversation_text)
+ private val conversationText: TextView? =
+ root.findViewById(
+ if (notificationsRedesignTemplates()) R.id.title else R.id.conversation_text
+ )
private val expandButton: NotificationExpandButton? = root.findViewById(R.id.expand_button)
private val headerText: TextView? = root.findViewById(R.id.header_text)
private val headerTextDivider: View? = root.findViewById(R.id.header_text_divider)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index b077327..4ed9dce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -16,8 +16,10 @@
package com.android.systemui.statusbar.notification.row;
+import static com.android.systemui.Flags.notificationAppearNonlinear;
import static com.android.systemui.Flags.notificationBackgroundTintOptimization;
import static com.android.systemui.Flags.notificationRowTransparency;
+import static com.android.systemui.Flags.physicalNotificationMovement;
import static com.android.systemui.statusbar.notification.row.ExpandableView.ClipSide.BOTTOM;
import static com.android.systemui.statusbar.notification.row.ExpandableView.ClipSide.TOP;
@@ -71,7 +73,8 @@
* #ALPHA_APPEAR_START_FRACTION.
*/
- private static final float ALPHA_APPEAR_START_FRACTION = .7f;
+ private static final float ALPHA_APPEAR_START_FRACTION =
+ notificationAppearNonlinear() ? .55f : .7f;
/**
* The content should show fully with progress at #ALPHA_APPEAR_END_FRACTION
* The start of the animation is at #ALPHA_APPEAR_START_FRACTION
@@ -111,6 +114,7 @@
private float mOverrideAmount;
private boolean mShadowHidden;
private boolean mIsHeadsUpAnimation;
+ private boolean mIsHeadsUpCycling;
/* In order to track headsup longpress coorindate. */
protected Point mTargetPoint;
private boolean mDismissed;
@@ -349,10 +353,12 @@
@Override
public long performRemoveAnimation(long duration, long delay, float translationDirection,
- boolean isHeadsUpAnimation, Runnable onStartedRunnable, Runnable onFinishedRunnable,
- AnimatorListenerAdapter animationListener, ClipSide clipSide) {
+ boolean isHeadsUpAnimation, boolean isHeadsUpCycling, Runnable onStartedRunnable,
+ Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener,
+ ClipSide clipSide) {
enableAppearDrawing(true);
mIsHeadsUpAnimation = isHeadsUpAnimation;
+ mIsHeadsUpCycling = isHeadsUpCycling;
if (mDrawingAppearAnimation) {
startAppearAnimation(false /* isAppearing */, translationDirection,
delay, duration, onStartedRunnable, onFinishedRunnable, animationListener,
@@ -370,9 +376,10 @@
@Override
public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear,
- Runnable onFinishRunnable) {
+ boolean isHeadsUpCycling, Runnable onFinishRunnable) {
enableAppearDrawing(true);
mIsHeadsUpAnimation = isHeadsUpAppear;
+ mIsHeadsUpCycling = isHeadsUpCycling;
if (mDrawingAppearAnimation) {
startAppearAnimation(true /* isAppearing */, isHeadsUpAppear ? 0.0f : -1.0f, delay,
duration, null, null, null, ClipSide.BOTTOM);
@@ -404,14 +411,14 @@
targetValue = 0.0f;
}
- if (NotificationHeadsUpCycling.isEnabled()) {
- // TODO(b/316404716): add avalanche filtering
+ if (NotificationHeadsUpCycling.isEnabled() && !useNonLinearAnimation()) {
mCurrentAppearInterpolator = Interpolators.LINEAR;
}
mAppearAnimator = ValueAnimator.ofFloat(mAppearAnimationFraction,
targetValue);
- mAppearAnimator.setInterpolator(mCurrentAppearInterpolator);
+ mAppearAnimator.setInterpolator(
+ useNonLinearAnimation() ? Interpolators.LINEAR : mCurrentAppearInterpolator);
mAppearAnimator.setDuration(
(long) (duration * Math.abs(mAppearAnimationFraction - targetValue)));
mAppearAnimator.addUpdateListener(animation -> {
@@ -530,7 +537,13 @@
* @param clipSide Which side if view we want to clip from
*/
private void updateAppearRect(ClipSide clipSide) {
- float interpolatedFraction = mAppearAnimationFraction;
+ float interpolatedFraction;
+ if (useNonLinearAnimation()) {
+ interpolatedFraction = mCurrentAppearInterpolator.getInterpolation(
+ mAppearAnimationFraction);
+ } else {
+ interpolatedFraction = mAppearAnimationFraction;
+ }
mAppearAnimationTranslation = (1.0f - interpolatedFraction) * mAnimationTranslationY;
final int fullHeight = getActualHeight();
float height = fullHeight * interpolatedFraction;
@@ -558,6 +571,11 @@
}
}
+ private boolean useNonLinearAnimation() {
+ return notificationAppearNonlinear() && (!mIsHeadsUpCycling
+ || physicalNotificationMovement());
+ }
+
private void updateAppearRect() {
updateAppearRect(ClipSide.BOTTOM);
}
@@ -567,7 +585,7 @@
mAppearAnimationFraction,
ALPHA_APPEAR_START_FRACTION,
ALPHA_APPEAR_END_FRACTION,
- Interpolators.ALPHA_IN
+ notificationAppearNonlinear() ? mCurrentAppearInterpolator : Interpolators.ALPHA_IN
);
}
@@ -813,6 +831,7 @@
pw.print("mDrawingAppearAnimation", mDrawingAppearAnimation);
pw.print("mAppearAnimationFraction", mAppearAnimationFraction);
pw.print("mIsHeadsUpAnimation", mIsHeadsUpAnimation);
+ pw.print("mIsHeadsUpCycling", mIsHeadsUpCycling);
pw.print("mTargetPoint", mTargetPoint);
pw.println();
}
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 473460b..4c74408 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
@@ -2182,7 +2182,7 @@
R.dimen.notification_min_height);
}
mMaxSmallHeightWithSummarization = NotificationUtils.getFontScaledHeight(mContext,
- com.android.internal.R.dimen.notification_collapsed_height_with_summarization);
+ com.android.internal.R.dimen.notification_min_height);
mMaxExpandedHeight = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_max_height);
mMaxExpandedHeightForPromotedOngoing = NotificationUtils.getFontScaledHeight(mContext,
@@ -3494,20 +3494,17 @@
@Override
public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear,
- Runnable onFinishRunnable) {
+ boolean isHeadsUpCycling, Runnable onFinishRunnable) {
mLogger.logStartAppearAnimation(mLoggingKey, /* isAppear = */ true);
- super.performAddAnimation(delay, duration, isHeadsUpAppear, onFinishRunnable);
+ super.performAddAnimation(delay, duration, isHeadsUpAppear, isHeadsUpCycling,
+ onFinishRunnable);
}
@Override
- public long performRemoveAnimation(
- long duration,
- long delay,
- float translationDirection,
- boolean isHeadsUpAnimation,
- Runnable onStartedRunnable,
- Runnable onFinishedRunnable,
- AnimatorListenerAdapter animationListener, ClipSide clipSide) {
+ public long performRemoveAnimation(long duration, long delay, float translationDirection,
+ boolean isHeadsUpAnimation, boolean isHeadsUpCycling, Runnable onStartedRunnable,
+ Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener,
+ ClipSide clipSide) {
mLogger.logStartAppearAnimation(mLoggingKey, /* isAppear = */ false);
if (mMenuRow != null && mMenuRow.isMenuVisible()) {
Animator anim = getTranslateViewAnimator(0f, null /* listener */);
@@ -3522,9 +3519,9 @@
@Override
public void onAnimationEnd(Animator animation) {
- ExpandableNotificationRow.super.performRemoveAnimation(
- duration, delay, translationDirection, isHeadsUpAnimation,
- null, onFinishedRunnable, animationListener, ClipSide.BOTTOM);
+ ExpandableNotificationRow.super.performRemoveAnimation(duration, delay,
+ translationDirection, isHeadsUpAnimation, isHeadsUpCycling, null,
+ onFinishedRunnable, animationListener, ClipSide.BOTTOM);
}
});
anim.start();
@@ -3532,8 +3529,8 @@
}
}
return super.performRemoveAnimation(duration, delay, translationDirection,
- isHeadsUpAnimation, onStartedRunnable, onFinishedRunnable, animationListener,
- clipSide);
+ isHeadsUpAnimation, isHeadsUpCycling, onStartedRunnable, onFinishedRunnable,
+ animationListener, clipSide);
}
@Override
@@ -4265,6 +4262,7 @@
pw.print(", isPinned: " + isPinned());
pw.print(", expandedWhenPinned: " + mExpandedWhenPinned);
pw.print(", isMinimized: " + mIsMinimized);
+ pw.print(", isAboveShelf: " + isAboveShelf());
pw.println();
if (NotificationContentView.INCLUDE_HEIGHTS_TO_DUMP) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index da664f8..292f74a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -467,7 +467,8 @@
* remove animation should be performed upwards,
* such that the child appears to be going away to the top. 1
* Should mean the opposite.
- * @param isHeadsUpAnimation Is this a headsUp animation.
+ * @param isHeadsUpAnimation Is this a headsUp animation
+ * @param isHeadsUpCycling Is this the cycling heads up animation
* @param onFinishedRunnable A runnable which should be run when the animation is finished.
* @param animationListener An animation listener to add to the animation.
* @return The additional delay, in milliseconds, that this view needs to add before the
@@ -475,7 +476,7 @@
*/
public abstract long performRemoveAnimation(long duration,
long delay, float translationDirection, boolean isHeadsUpAnimation,
- Runnable onStartedRunnable,
+ boolean isHeadsUpCycling, Runnable onStartedRunnable,
Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener, ClipSide clipSide);
@@ -485,11 +486,12 @@
}
public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) {
- performAddAnimation(delay, duration, isHeadsUpAppear, null);
+ performAddAnimation(delay, duration, isHeadsUpAppear, false /* isHeadsUpCycling */,
+ null);
}
public abstract void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear,
- Runnable onEndRunnable);
+ boolean isHeadsUpCycling, Runnable onEndRunnable);
public int getPinnedHeadsUpHeight() {
return getIntrinsicHeight();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
index cd228e7..a064d1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -260,7 +260,7 @@
@Override
public long performRemoveAnimation(long duration, long delay,
float translationDirection, boolean isHeadsUpAnimation,
- Runnable onStartedRunnable,
+ boolean isHeadsUpCycling, Runnable onStartedRunnable,
Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener, ClipSide clipSide) {
// TODO: Use duration
@@ -279,7 +279,7 @@
@Override
public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear,
- Runnable endRunnable) {
+ boolean isHeadsUpCycling, Runnable endRunnable) {
// TODO: use delay and duration
setContentVisibleAnimated(true);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
index 2d94694..e887e25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.row.wrapper
+import android.app.Flags.notificationsRedesignTemplates
import android.content.Context
import android.view.View
import com.android.internal.widget.CachingIconView
@@ -25,17 +26,13 @@
import com.android.systemui.statusbar.notification.NotificationUtils
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
-/**
- * Wraps a notification containing a call template
- */
-class NotificationCallTemplateViewWrapper constructor(
- ctx: Context,
- view: View,
- row: ExpandableNotificationRow
-) : NotificationTemplateViewWrapper(ctx, view, row) {
+/** Wraps a notification containing a call template */
+class NotificationCallTemplateViewWrapper
+constructor(ctx: Context, view: View, row: ExpandableNotificationRow) :
+ NotificationTemplateViewWrapper(ctx, view, row) {
private val minHeightWithActions: Int =
- NotificationUtils.getFontScaledHeight(ctx, R.dimen.notification_max_height)
+ NotificationUtils.getFontScaledHeight(ctx, R.dimen.notification_max_height)
private val callLayout: CallLayout = view as CallLayout
private lateinit var conversationIconContainer: View
@@ -48,13 +45,17 @@
private fun resolveViews() {
with(callLayout) {
conversationIconContainer =
- requireViewById(com.android.internal.R.id.conversation_icon_container)
+ requireViewById(com.android.internal.R.id.conversation_icon_container)
conversationIconView = requireViewById(com.android.internal.R.id.conversation_icon)
conversationBadgeBg =
- requireViewById(com.android.internal.R.id.conversation_icon_badge_bg)
+ requireViewById(com.android.internal.R.id.conversation_icon_badge_bg)
expandBtn = requireViewById(com.android.internal.R.id.expand_button)
appName = requireViewById(com.android.internal.R.id.app_name_text)
- conversationTitleView = requireViewById(com.android.internal.R.id.conversation_text)
+ conversationTitleView =
+ requireViewById(
+ if (notificationsRedesignTemplates()) com.android.internal.R.id.title
+ else com.android.internal.R.id.conversation_text
+ )
}
}
@@ -68,20 +69,12 @@
override fun updateTransformedTypes() {
// This also clears the existing types
super.updateTransformedTypes()
- addTransformedViews(
- appName,
- conversationTitleView
- )
- addViewsTransformingToSimilar(
- conversationIconView,
- conversationBadgeBg,
- expandBtn
- )
+ addTransformedViews(appName, conversationTitleView)
+ addViewsTransformingToSimilar(conversationIconView, conversationBadgeBg, expandBtn)
}
override fun disallowSingleClick(x: Float, y: Float): Boolean {
- val isOnExpandButton = expandBtn.visibility == View.VISIBLE &&
- isOnView(expandBtn, x, y)
+ val isOnExpandButton = expandBtn.visibility == View.VISIBLE && isOnView(expandBtn, x, y)
return isOnExpandButton || super.disallowSingleClick(x, y)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index 9d13ab5..6a96fba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -17,10 +17,12 @@
package com.android.systemui.statusbar.notification.row.wrapper
import android.app.Flags
+import android.app.Flags.notificationsRedesignTemplates
import android.content.Context
import android.graphics.drawable.AnimatedImageDrawable
import android.view.View
import android.view.ViewGroup
+import androidx.core.view.isVisible
import com.android.internal.widget.CachingIconView
import com.android.internal.widget.ConversationLayout
import com.android.internal.widget.MessagingGroup
@@ -53,8 +55,8 @@
private lateinit var badgeIconView: NotificationRowIconView
private lateinit var conversationBadgeBg: View
private lateinit var expandBtn: View
- private lateinit var expandBtnContainer: View
- private lateinit var imageMessageContainer: ViewGroup
+ private var expandBtnContainer: View? = null
+ private var imageMessageContainer: ViewGroup? = null
private lateinit var messageContainers: ArrayList<MessagingGroup>
private lateinit var messagingLinearLayout: MessagingLinearLayout
private lateinit var conversationTitleView: View
@@ -78,10 +80,14 @@
conversationBadgeBg =
requireViewById(com.android.internal.R.id.conversation_icon_badge_bg)
expandBtn = requireViewById(com.android.internal.R.id.expand_button)
- expandBtnContainer = requireViewById(com.android.internal.R.id.expand_button_container)
+ expandBtnContainer = findViewById(com.android.internal.R.id.expand_button_container)
importanceRing = requireViewById(com.android.internal.R.id.conversation_icon_badge_ring)
appName = requireViewById(com.android.internal.R.id.app_name_text)
- conversationTitleView = requireViewById(com.android.internal.R.id.conversation_text)
+ conversationTitleView =
+ requireViewById(
+ if (notificationsRedesignTemplates()) com.android.internal.R.id.title
+ else com.android.internal.R.id.conversation_text
+ )
facePileTop = findViewById(com.android.internal.R.id.conversation_face_pile_top)
facePileBottom = findViewById(com.android.internal.R.id.conversation_face_pile_bottom)
facePileBottomBg =
@@ -133,11 +139,21 @@
expandable: Boolean,
onClickListener: View.OnClickListener,
requestLayout: Boolean,
- ) = conversationLayout.updateExpandability(expandable, onClickListener)
+ ) {
+ if (notificationsRedesignTemplates()) {
+ super.updateExpandability(expandable, onClickListener, requestLayout)
+ } else {
+ conversationLayout.updateExpandability(expandable, onClickListener)
+ }
+ }
override fun disallowSingleClick(x: Float, y: Float): Boolean {
val isOnExpandButton =
- expandBtnContainer.visibility == View.VISIBLE && isOnView(expandBtnContainer, x, y)
+ if (notificationsRedesignTemplates()) {
+ expandBtn.isVisible && isOnView(expandBtn, x, y)
+ } else {
+ expandBtnContainer?.visibility == View.VISIBLE && isOnView(expandBtnContainer, x, y)
+ }
return isOnExpandButton || super.disallowSingleClick(x, y)
}
@@ -158,7 +174,8 @@
// and the top level image message container.
val containers =
messageContainers.asSequence().map { it.messageContainer } +
- sequenceOf(imageMessageContainer)
+ if (notificationsRedesignTemplates()) emptySequence()
+ else sequenceOf(imageMessageContainer!!)
val drawables =
containers
.flatMap { it.children }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
index bd7bd59..4d10a52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
@@ -70,14 +70,15 @@
}
override fun performRemoveAnimation(
- duration: Long,
- delay: Long,
- translationDirection: Float,
- isHeadsUpAnimation: Boolean,
- onStartedRunnable: Runnable?,
- onFinishedRunnable: Runnable?,
- animationListener: AnimatorListenerAdapter?,
- clipSide: ClipSide
+ duration: Long,
+ delay: Long,
+ translationDirection: Float,
+ isHeadsUpAnimation: Boolean,
+ isHeadsUpCycling: Boolean,
+ onStartedRunnable: Runnable?,
+ onFinishedRunnable: Runnable?,
+ animationListener: AnimatorListenerAdapter?,
+ clipSide: ClipSide,
): Long {
return 0
}
@@ -86,7 +87,8 @@
delay: Long,
duration: Long,
isHeadsUpAppear: Boolean,
- onEnd: Runnable?
+ isHeadsUpCycling: Boolean,
+ onEnd: Runnable?,
) {
// No animation, it doesn't need it, this would be local
}
@@ -103,9 +105,7 @@
assertMediaContainerVisibility(visibility)
}
- /**
- * visibility should be aligned with MediaContainerView visibility on the keyguard.
- */
+ /** visibility should be aligned with MediaContainerView visibility on the keyguard. */
private fun isVisibilityValid(visibility: Int): Boolean {
val currentViewState = viewState as? MediaContainerViewState ?: return true
val shouldBeGone = !currentViewState.shouldBeVisible
@@ -113,8 +113,7 @@
}
/**
- * b/298213983
- * MediaContainerView's visibility is changed to VISIBLE when it should be GONE.
+ * b/298213983 MediaContainerView's visibility is changed to VISIBLE when it should be GONE.
* This method check this state and logs.
*/
private fun assertMediaContainerVisibility(visibility: Int) {
@@ -122,8 +121,10 @@
if (currentViewState is MediaContainerViewState) {
if (!currentViewState.shouldBeVisible && visibility == VISIBLE) {
- Log.wtf("MediaContainerView", "MediaContainerView should be GONE " +
- "but its visibility changed to VISIBLE")
+ Log.wtf(
+ "MediaContainerView",
+ "MediaContainerView should be GONE " + "but its visibility changed to VISIBLE",
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 06b989a..08692be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -1227,13 +1227,17 @@
float baseZ = ambientState.getBaseZHeight();
if (SceneContainerFlag.isEnabled()) {
- // SceneContainer flags off this logic, and just sets the baseZ because:
+ // SceneContainer simplifies this logic, because:
// - there are no overlapping HUNs anymore, no need for multiplying their shadows
// - shadows for HUNs overlapping with the stack are now set from updateHeadsUpStates
- // - shadows for HUNs overlapping with the shelf are NOT set anymore, because it only
- // happens on AOD/Pulsing, where they're displayed on a black background so a shadow
- // wouldn't be visible.
- childViewState.setZTranslation(baseZ);
+ if (child.isPinned() || ambientState.getTrackedHeadsUpRow() == child) {
+ // set a default elevation on the HUN, which would be overridden
+ // from updateHeadsUpStates if it is displayed in the shade
+ childViewState.setZTranslation(baseZ + mPinnedZTranslationExtra);
+ } else {
+ // set baseZ for every notification
+ childViewState.setZTranslation(baseZ);
+ }
} else {
if (child.mustStayOnScreen() && !childViewState.headsUpIsVisible
&& !ambientState.isDozingAndNotPulsing(child)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 5e0d57e..2b05223 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -506,8 +506,8 @@
}
changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
0 /* delay */, translationDirection, false /* isHeadsUpAppear */,
- startAnimation, postAnimation, getGlobalAnimationFinishedListener(),
- ExpandableView.ClipSide.BOTTOM);
+ false /* isHeadsUpCycling */, startAnimation, postAnimation,
+ getGlobalAnimationFinishedListener(), ExpandableView.ClipSide.BOTTOM);
needsCustomAnimation = true;
} else if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
@@ -538,7 +538,7 @@
};
}
changingView.performAddAnimation(0, ANIMATION_DURATION_HEADS_UP_CYCLING,
- /* isHeadsUpAppear= */ true, onAnimationEnd);
+ /* isHeadsUpAppear= */ true, /* isHeadsUpCycling= */ true, onAnimationEnd);
} else if (event.animationType == ANIMATION_TYPE_HEADS_UP_APPEAR) {
mHeadsUpAppearChildren.add(changingView);
@@ -559,7 +559,7 @@
onAnimationEnd = () -> mLogger.appearAnimationEnded(finalKey);
}
changingView.performAddAnimation(0, ANIMATION_DURATION_HEADS_UP_APPEAR,
- /* isHeadsUpAppear= */ true, onAnimationEnd);
+ /* isHeadsUpAppear= */ true, /* isHeadsUpCycling= */ false, onAnimationEnd);
} else if (event.animationType == ANIMATION_TYPE_HEADS_UP_CYCLING_OUT) {
mHeadsUpDisappearChildren.add(changingView);
Runnable endRunnable = null;
@@ -629,6 +629,7 @@
// translation, the actual translation is in StackScrollAlgorithm.
/* translationDirection= */ 0.0f,
/* isHeadsUpAnimation= */ true,
+ /* isHeadsUpCycling= */ true,
startAnimation, postAnimation,
getGlobalAnimationFinishedListener(), ExpandableView.ClipSide.TOP);
mAnimationProperties.delay += removeAnimationDelay;
@@ -706,6 +707,7 @@
long removeAnimationDelay = changingView.performRemoveAnimation(
ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
0, 0.0f, true /* isHeadsUpAppear */,
+ false /* isHeadsUpCycling */,
startAnimation, postAnimation,
getGlobalAnimationFinishedListener(), ExpandableView.ClipSide.BOTTOM);
mAnimationProperties.delay += removeAnimationDelay;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 5c81c8e..34b6560 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -20,6 +20,7 @@
import android.content.Context
import androidx.annotation.VisibleForTesting
import com.android.app.tracing.coroutines.flow.flowName
+import com.android.systemui.Flags.glanceableHubV2
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
@@ -87,7 +88,9 @@
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
+import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
+import com.android.systemui.util.kotlin.BooleanFlowOperators.not
import com.android.systemui.util.kotlin.FlowDumperImpl
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
import com.android.systemui.util.kotlin.sample
@@ -299,20 +302,40 @@
.distinctUntilChanged()
.dumpWhileCollecting("configurationBasedDimensions")
+ private val isOnAnyBouncer: Flow<Boolean> =
+ anyOf(
+ keyguardTransitionInteractor.transitionValue(ALTERNATE_BOUNCER).map { it > 0f },
+ keyguardTransitionInteractor
+ .transitionValue(
+ scene = Scenes.Bouncer,
+ stateWithoutSceneContainer = PRIMARY_BOUNCER,
+ )
+ .map { it > 0f },
+ )
+
/** If the user is visually on one of the unoccluded lockscreen states. */
val isOnLockscreen: Flow<Boolean> =
- anyOf(
- keyguardTransitionInteractor.transitionValue(AOD).map { it > 0f },
- keyguardTransitionInteractor.transitionValue(DOZING).map { it > 0f },
- keyguardTransitionInteractor.transitionValue(ALTERNATE_BOUNCER).map { it > 0f },
- keyguardTransitionInteractor
- .transitionValue(
- scene = Scenes.Bouncer,
- stateWithoutSceneContainer = PRIMARY_BOUNCER,
- )
- .map { it > 0f },
- keyguardTransitionInteractor.transitionValue(LOCKSCREEN).map { it > 0f },
- )
+ if (glanceableHubV2()) {
+ anyOf(
+ keyguardTransitionInteractor.transitionValue(AOD).map { it > 0f },
+ keyguardTransitionInteractor.transitionValue(DOZING).map { it > 0f },
+ keyguardTransitionInteractor.transitionValue(LOCKSCREEN).map { it > 0f },
+ allOf(
+ // Exclude bouncer showing over communal hub, as this should not be
+ // considered
+ // "lockscreen"
+ not(communalSceneInteractor.isCommunalVisible),
+ isOnAnyBouncer,
+ ),
+ )
+ } else {
+ anyOf(
+ keyguardTransitionInteractor.transitionValue(AOD).map { it > 0f },
+ keyguardTransitionInteractor.transitionValue(DOZING).map { it > 0f },
+ keyguardTransitionInteractor.transitionValue(LOCKSCREEN).map { it > 0f },
+ isOnAnyBouncer,
+ )
+ }
.flowName("isOnLockscreen")
.stateIn(
scope = applicationScope,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index d336903..2efc057 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -20,6 +20,7 @@
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
+import android.content.pm.PackageManager
import android.telephony.CarrierConfigManager
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
@@ -192,6 +193,19 @@
serviceStateChangedEvent
.mapLatest {
val modems = telephonyManager.activeModemCount
+
+ // Assume false for automotive devices which don't have the calling feature.
+ // TODO: b/398045526 to revisit the below.
+ val isAutomotive: Boolean =
+ context.packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ val hasFeatureCalling: Boolean =
+ context.packageManager.hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_CALLING
+ )
+ if (isAutomotive && !hasFeatureCalling) {
+ return@mapLatest false
+ }
+
// Check the service state for every modem. If any state reports emergency calling
// capable, then consider the device to have emergency call capabilities
(0..<modems)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
index c52536d..10821df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
@@ -21,7 +21,6 @@
import android.telephony.SubscriptionManager
import android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING
import com.android.settingslib.SignalIcon.MobileIconGroup
-import com.android.settingslib.flags.Flags
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -29,11 +28,13 @@
import com.android.systemui.flags.Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.statusbar.core.NewStatusBarIcons
import com.android.systemui.statusbar.core.StatusBarRootModernization
import com.android.systemui.statusbar.pipeline.dagger.MobileSummaryLog
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
import com.android.systemui.statusbar.policy.data.repository.UserSetupRepository
@@ -85,6 +86,12 @@
/** Whether the mobile icons can be stacked vertically. */
val isStackable: StateFlow<Boolean>
+ /**
+ * Observable for the subscriptionId of the current mobile data connection. Null if we don't
+ * have a valid subscription id
+ */
+ val activeMobileDataSubscriptionId: StateFlow<Int?>
+
/** True if the active mobile data subscription has data enabled */
val activeDataConnectionHasDataEnabled: StateFlow<Boolean>
@@ -168,6 +175,9 @@
)
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
+ override val activeMobileDataSubscriptionId: StateFlow<Int?> =
+ mobileConnectionsRepo.activeMobileDataSubscriptionId
+
override val activeDataConnectionHasDataEnabled: StateFlow<Boolean> =
mobileConnectionsRepo.activeMobileDataRepository
.flatMapLatest { it?.dataEnabled ?: flowOf(false) }
@@ -298,10 +308,16 @@
.stateIn(scope, SharingStarted.WhileSubscribed(), emptyList())
override val isStackable =
- if (Flags.newStatusBarIcons() && StatusBarRootModernization.isEnabled) {
+ if (NewStatusBarIcons.isEnabled && StatusBarRootModernization.isEnabled) {
icons.flatMapLatest { icons ->
- combine(icons.map { it.isNonTerrestrial }) {
- it.size == 2 && it.none { isNonTerrestrial -> isNonTerrestrial }
+ combine(icons.map { it.signalLevelIcon }) { signalLevelIcons ->
+ // These are only stackable if:
+ // - They are cellular
+ // - There's exactly two
+ // - They have the same number of levels
+ signalLevelIcons.filterIsInstance<SignalIconModel.Cellular>().let {
+ it.size == 2 && it[0].numberOfLevels == it[1].numberOfLevels
+ }
}
}
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
index 6176a3e..288e49e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
@@ -63,6 +63,8 @@
@VisibleForTesting
val reuseCache = ConcurrentHashMap<Int, Pair<MobileIconViewModel, CoroutineScope>>()
+ val activeMobileDataSubscriptionId: StateFlow<Int?> = interactor.activeMobileDataSubscriptionId
+
val subscriptionIdsFlow: StateFlow<List<Int>> =
interactor.filteredSubscriptions
.mapLatest { subscriptions ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModel.kt
index a2c2a3c..2c85a51 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModel.kt
@@ -25,7 +25,7 @@
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
@@ -43,8 +43,14 @@
initialValue = false,
)
- private val iconViewModelFlow: StateFlow<List<MobileIconViewModelCommon>> =
- mobileIconsViewModel.mobileSubViewModels
+ private val iconViewModelFlow: Flow<List<MobileIconViewModelCommon>> =
+ combine(
+ mobileIconsViewModel.mobileSubViewModels,
+ mobileIconsViewModel.activeMobileDataSubscriptionId,
+ ) { viewModels, activeSubId ->
+ // Sort to get the active subscription first, if it's set
+ viewModels.sortedByDescending { it.subscriptionId == activeSubId }
+ }
val dualSim: DualSim? by
hydrator.hydratedStateOf(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
index cd320a1..d734889 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
@@ -31,6 +31,8 @@
import com.android.systemui.statusbar.chips.mediaprojection.domain.model.MediaProjectionStopDialogModel
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.ui.binder.OngoingActivityChipBinder
+import com.android.systemui.statusbar.chips.ui.binder.OngoingActivityChipViewBinding
+import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModelLegacy
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.core.StatusBarRootModernization
@@ -149,6 +151,7 @@
if (!StatusBarNotifChips.isEnabled && !StatusBarChipsModernization.isEnabled) {
val primaryChipViewBinding =
OngoingActivityChipBinder.createBinding(primaryChipView)
+
launch {
viewModel.primaryOngoingActivityChip.collect { primaryChipModel ->
OngoingActivityChipBinder.bind(
@@ -156,18 +159,14 @@
primaryChipViewBinding,
iconViewStore,
)
- if (StatusBarRootModernization.isEnabled) {
- when (primaryChipModel) {
- is OngoingActivityChipModel.Active ->
- primaryChipViewBinding.rootView.show(
- shouldAnimateChange = true
- )
- is OngoingActivityChipModel.Inactive ->
- primaryChipViewBinding.rootView.hide(
- state = View.GONE,
- shouldAnimateChange = primaryChipModel.shouldAnimate,
- )
+ if (StatusBarRootModernization.isEnabled) {
+ launch {
+ bindLegacyPrimaryOngoingActivityChipWithVisibility(
+ viewModel,
+ primaryChipModel,
+ primaryChipViewBinding,
+ )
}
} else {
when (primaryChipModel) {
@@ -213,12 +212,14 @@
)
if (StatusBarRootModernization.isEnabled) {
- primaryChipViewBinding.rootView.adjustVisibility(
- chips.primary.toVisibilityModel()
- )
- secondaryChipViewBinding.rootView.adjustVisibility(
- chips.secondary.toVisibilityModel()
- )
+ launch {
+ bindOngoingActivityChipsWithVisibility(
+ viewModel,
+ chips,
+ primaryChipViewBinding,
+ secondaryChipViewBinding,
+ )
+ }
} else {
listener?.onOngoingActivityStatusChanged(
hasPrimaryOngoingActivity =
@@ -312,6 +313,52 @@
}
}
+ /** Bind the (legacy) single primary ongoing activity chip with the status bar visibility */
+ private suspend fun bindLegacyPrimaryOngoingActivityChipWithVisibility(
+ viewModel: HomeStatusBarViewModel,
+ primaryChipModel: OngoingActivityChipModel,
+ primaryChipViewBinding: OngoingActivityChipViewBinding,
+ ) {
+ viewModel.canShowOngoingActivityChips.collectLatest { visible ->
+ if (!visible) {
+ primaryChipViewBinding.rootView.hide(shouldAnimateChange = false)
+ } else {
+ when (primaryChipModel) {
+ is OngoingActivityChipModel.Active -> {
+ primaryChipViewBinding.rootView.show(shouldAnimateChange = true)
+ }
+
+ is OngoingActivityChipModel.Inactive -> {
+ primaryChipViewBinding.rootView.hide(
+ state = View.GONE,
+ shouldAnimateChange = primaryChipModel.shouldAnimate,
+ )
+ }
+ }
+ }
+ }
+ }
+
+ /** Bind the primary/secondary chips along with the home status bar's visibility */
+ private suspend fun bindOngoingActivityChipsWithVisibility(
+ viewModel: HomeStatusBarViewModel,
+ chips: MultipleOngoingActivityChipsModelLegacy,
+ primaryChipViewBinding: OngoingActivityChipViewBinding,
+ secondaryChipViewBinding: OngoingActivityChipViewBinding,
+ ) {
+ viewModel.canShowOngoingActivityChips.collectLatest { canShow ->
+ if (!canShow) {
+ primaryChipViewBinding.rootView.hide(shouldAnimateChange = false)
+ secondaryChipViewBinding.rootView.hide(shouldAnimateChange = false)
+ } else {
+ primaryChipViewBinding.rootView.adjustVisibility(chips.primary.toVisibilityModel())
+ secondaryChipViewBinding.rootView.adjustVisibility(
+ chips.secondary.toVisibilityModel()
+ )
+ }
+ }
+ }
+
private fun SystemEventAnimationState.isAnimatingChip() =
when (this) {
AnimatingIn,
@@ -374,43 +421,42 @@
if (visibility == View.INVISIBLE || visibility == View.GONE) {
return
}
+ alpha = 0f
visibility = state
}
// See CollapsedStatusBarFragment#hide.
private fun View.hide(state: Int = View.INVISIBLE, shouldAnimateChange: Boolean) {
+ animate().cancel()
if (visibility == View.INVISIBLE || visibility == View.GONE) {
return
}
- val v = this
- v.animate().cancel()
if (!shouldAnimateChange) {
- v.alpha = 0f
- v.visibility = state
+ alpha = 0f
+ visibility = state
return
}
- v.animate()
+ animate()
.alpha(0f)
.setDuration(CollapsedStatusBarFragment.FADE_OUT_DURATION.toLong())
.setStartDelay(0)
.setInterpolator(Interpolators.ALPHA_OUT)
- .withEndAction { v.visibility = state }
+ .withEndAction { visibility = state }
}
// See CollapsedStatusBarFragment#show.
private fun View.show(shouldAnimateChange: Boolean) {
- if (visibility == View.VISIBLE) {
+ animate().cancel()
+ if (visibility == View.VISIBLE && alpha >= 1f) {
return
}
- val v = this
- v.animate().cancel()
- v.visibility = View.VISIBLE
+ visibility = View.VISIBLE
if (!shouldAnimateChange) {
- v.alpha = 1f
+ alpha = 1f
return
}
- v.animate()
+ animate()
.alpha(1f)
.setDuration(CollapsedStatusBarFragment.FADE_IN_DURATION.toLong())
.setInterpolator(Interpolators.ALPHA_IN)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StackedMobileIcon.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StackedMobileIcon.kt
index 465a43f..a51da0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StackedMobileIcon.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StackedMobileIcon.kt
@@ -17,9 +17,12 @@
package com.android.systemui.statusbar.pipeline.shared.ui.composable
import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
import androidx.compose.material3.LocalContentColor
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -27,23 +30,28 @@
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.TextUnit
-import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-import com.android.compose.modifiers.height
-import com.android.compose.modifiers.width
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.StackedMobileIconViewModel
import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.BarBaseHeightFiveBarsSp
import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.BarBaseHeightFourBarsSp
import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.BarsLevelIncrementSp
import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.BarsVerticalPaddingSp
+import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.ExclamationCutoutRadiusSp
+import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.ExclamationDiameterSp
+import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.ExclamationHeightSp
+import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.ExclamationHorizontalOffset
+import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.ExclamationVerticalSpacing
import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.HorizontalPaddingFiveBarsSp
import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.HorizontalPaddingFourBarsSp
import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.IconHeightSp
+import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.IconPaddingSp
+import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.IconSpacingSp
import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.IconWidthFiveBarsSp
import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.IconWidthFourBarsSp
import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.SecondaryBarHeightSp
@@ -58,15 +66,16 @@
val dualSim = viewModel.dualSim ?: return
val contentColor = LocalContentColor.current
+ val padding = with(LocalDensity.current) { IconPaddingSp.toDp() }
+ val horizontalArrangement = with(LocalDensity.current) { spacedBy(IconSpacingSp.toDp()) }
- Row(verticalAlignment = Alignment.CenterVertically, modifier = modifier) {
+ Row(
+ horizontalArrangement = horizontalArrangement,
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = modifier.padding(horizontal = padding),
+ ) {
viewModel.networkTypeIcon?.let {
- Icon(
- it,
- tint = contentColor,
- modifier =
- Modifier.height { IconHeightSp.roundToPx() }.padding(start = 1.dp, end = 2.dp),
- )
+ Icon(it, tint = contentColor, modifier = Modifier.fillMaxHeight())
}
StackedMobileIcon(dualSim, contentColor)
@@ -79,23 +88,23 @@
color: Color,
modifier: Modifier = Modifier,
) {
- val maxNumberOfLevels =
- max(viewModel.primary.numberOfLevels, viewModel.secondary.numberOfLevels)
- val dimensions = if (maxNumberOfLevels == 6) FiveBarsDimensions else FourBarsDimensions
+ // Removing 1 to get the real number of bars
+ val numberOfBars = max(viewModel.primary.numberOfLevels, viewModel.secondary.numberOfLevels) - 1
+ val dimensions = if (numberOfBars == 5) FiveBarsDimensions else FourBarsDimensions
val iconSize =
with(LocalDensity.current) { dimensions.totalWidth.toDp() to IconHeightSp.toDp() }
- Canvas(modifier.size(width = iconSize.first, height = iconSize.second)) {
+ Canvas(modifier.width(iconSize.first).height(iconSize.second)) {
val verticalPaddingPx = BarsVerticalPaddingSp.roundToPx()
val horizontalPaddingPx = dimensions.barsHorizontalPadding.roundToPx()
- val totalPaddingWidthPx = horizontalPaddingPx * (maxNumberOfLevels - 1)
+ val totalPaddingWidthPx = horizontalPaddingPx * (numberOfBars - 1)
- val barWidthPx = (size.width - totalPaddingWidthPx) / maxNumberOfLevels
+ val barWidthPx = (size.width - totalPaddingWidthPx) / numberOfBars
val dotHeightPx = SecondaryBarHeightSp.toPx()
val baseBarHeightPx = dimensions.barBaseHeight.toPx()
var xOffsetPx = 0f
- for (bar in 1..maxNumberOfLevels) {
+ for (bar in 1..numberOfBars) {
// Bottom dots representing secondary sim
val dotYOffsetPx = size.height - dotHeightPx
if (bar <= viewModel.secondary.numberOfLevels) {
@@ -123,6 +132,10 @@
xOffsetPx += barWidthPx + horizontalPaddingPx
}
+
+ if (viewModel.primary.showExclamationMark) {
+ drawExclamationCutout(color)
+ }
}
}
@@ -143,6 +156,39 @@
)
}
+private fun DrawScope.drawExclamationCutout(color: Color) {
+ // Exclamation mark is bottom aligned with the canvas
+ val exclamationDiameterPx = ExclamationDiameterSp.toPx()
+ val exclamationRadiusPx = ExclamationDiameterSp.toPx() / 2
+ val exclamationTotalHeight =
+ ExclamationHeightSp.toPx() + ExclamationVerticalSpacing.toPx() + exclamationDiameterPx
+ val exclamationDotCenter =
+ Offset(size.width - ExclamationHorizontalOffset.toPx(), size.height - exclamationRadiusPx)
+ val exclamationMarkTopLeft =
+ Offset(exclamationDotCenter.x - exclamationRadiusPx, size.height - exclamationTotalHeight)
+ val exclamationCornerRadius = CornerRadius(exclamationRadiusPx)
+ val cutoutCenter = Offset(exclamationDotCenter.x, size.height - (exclamationTotalHeight / 2))
+
+ // Transparent cutout
+ drawCircle(
+ color = Color.Transparent,
+ radius = ExclamationCutoutRadiusSp.toPx(),
+ center = cutoutCenter,
+ blendMode = BlendMode.SrcIn,
+ )
+
+ // Top bar for the exclamation mark
+ drawRoundRect(
+ color = color,
+ topLeft = exclamationMarkTopLeft,
+ size = Size(exclamationDiameterPx, ExclamationHeightSp.toPx()),
+ cornerRadius = exclamationCornerRadius,
+ )
+
+ // Bottom circle for the exclamation mark
+ drawCircle(color = color, center = exclamationDotCenter, radius = exclamationRadiusPx)
+}
+
private abstract class BarsDependentDimensions(
val totalWidth: TextUnit,
val barsHorizontalPadding: TextUnit,
@@ -166,10 +212,19 @@
private object StackedMobileIconDimensions {
// Common dimensions
val IconHeightSp = 12.sp
+ val IconPaddingSp = 4.sp
+ val IconSpacingSp = 2.sp
val BarsVerticalPaddingSp = 1.5.sp
val BarsLevelIncrementSp = 1.sp
val SecondaryBarHeightSp = 3.sp
+ // Exclamation cutout dimensions
+ val ExclamationCutoutRadiusSp = 5.sp
+ val ExclamationDiameterSp = 1.5.sp
+ val ExclamationHeightSp = 4.5.sp
+ val ExclamationVerticalSpacing = 1.sp
+ val ExclamationHorizontalOffset = 1.sp
+
// Dimensions dependant on the number of total bars
val IconWidthFiveBarsSp = 18.5.sp
val IconWidthFourBarsSp = 16.sp
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
index 39a1b46..4189221 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
@@ -37,6 +37,10 @@
import com.android.compose.theme.PlatformTheme
import com.android.keyguard.AlphaOptimizedLinearLayout
import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHostState
+import com.android.systemui.media.dagger.MediaModule.POPUP
import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.ui.compose.OngoingActivityChips
@@ -67,6 +71,7 @@
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.HomeStatusBarViewModelFactory
import javax.inject.Inject
+import javax.inject.Named
/** Factory to simplify the dependency management for [StatusBarRoot] */
class StatusBarRootFactory
@@ -81,6 +86,8 @@
private val ongoingCallController: OngoingCallController,
private val darkIconDispatcherStore: DarkIconDispatcherStore,
private val eventAnimationInteractor: SystemStatusEventAnimationInteractor,
+ private val mediaHierarchyManager: MediaHierarchyManager,
+ @Named(POPUP) private val mediaHost: MediaHost,
) {
fun create(root: ViewGroup, andThen: (ViewGroup) -> Unit): ComposeView {
val composeView = ComposeView(root.context)
@@ -99,6 +106,8 @@
ongoingCallController = ongoingCallController,
darkIconDispatcher = darkIconDispatcher,
eventAnimationInteractor = eventAnimationInteractor,
+ mediaHierarchyManager = mediaHierarchyManager,
+ mediaHost = mediaHost,
onViewCreated = andThen,
)
}
@@ -130,6 +139,8 @@
ongoingCallController: OngoingCallController,
darkIconDispatcher: DarkIconDispatcher,
eventAnimationInteractor: SystemStatusEventAnimationInteractor,
+ mediaHierarchyManager: MediaHierarchyManager,
+ mediaHost: MediaHost,
onViewCreated: (ViewGroup) -> Unit,
) {
val displayId = parent.context.displayId
@@ -237,6 +248,15 @@
// Add a composable container for `StatusBarPopupChip`s
if (StatusBarPopupChips.isEnabled) {
+ with(mediaHost) {
+ expansion = MediaHostState.EXPANDED
+ expandedMatchesParentHeight = true
+ showsOnlyActiveMedia = true
+ falsingProtectionNeeded = false
+ disablePagination = true
+ init(MediaHierarchyManager.LOCATION_STATUS_BAR_POPUP)
+ }
+
val endSideContent =
phoneStatusBarView.requireViewById<AlphaOptimizedLinearLayout>(
R.id.status_bar_end_side_content
@@ -256,7 +276,12 @@
setContent {
StatusBarPopupChipsContainer(
- chips = statusBarViewModel.popupChips
+ chips = statusBarViewModel.popupChips,
+ mediaHost = mediaHost,
+ onMediaControlPopupVisibilityChanged = { popupShowing ->
+ mediaHierarchyManager.isMediaControlPopupShowing =
+ popupShowing
+ },
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
index 9ae2cb2..807e905 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
@@ -149,6 +149,9 @@
*/
val isHomeStatusBarAllowedByScene: StateFlow<Boolean>
+ /** True if the home status bar is showing, and there is no HUN happening */
+ val canShowOngoingActivityChips: Flow<Boolean>
+
/** True if the operator name view is not hidden due to HUN or other visibility state */
val shouldShowOperatorNameView: Flow<Boolean>
val isClockVisible: Flow<VisibilityModel>
@@ -412,6 +415,15 @@
)
.flowOn(bgDispatcher)
+ override val canShowOngoingActivityChips: Flow<Boolean> =
+ combine(
+ isHomeStatusBarAllowed,
+ keyguardInteractor.isSecureCameraActive,
+ headsUpNotificationInteractor.statusBarHeadsUpStatus,
+ ) { isHomeStatusBarAllowed, isSecureCameraActive, headsUpState ->
+ isHomeStatusBarAllowed && !isSecureCameraActive && !headsUpState.isPinned
+ }
+
override val isClockVisible: Flow<VisibilityModel> =
combine(
shouldHomeStatusBarBeVisible,
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLockedInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLockedInteractor.kt
index ef29a38..3bd8af6 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLockedInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLockedInteractor.kt
@@ -18,12 +18,20 @@
import android.os.UserHandle
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.user.data.repository.UserRepository
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOn
@SysUISingleton
-class UserLockedInteractor @Inject constructor(val userRepository: UserRepository) {
+class UserLockedInteractor
+@Inject
+constructor(
+ @Background val backgroundDispatcher: CoroutineDispatcher,
+ val userRepository: UserRepository,
+) {
fun isUserUnlocked(userHandle: UserHandle?): Flow<Boolean> =
- userRepository.isUserUnlocked(userHandle)
+ userRepository.isUserUnlocked(userHandle).flowOn(backgroundDispatcher)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
index 0bdf99e..14eede6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
@@ -24,6 +24,7 @@
import androidx.annotation.LayoutRes
import androidx.compose.ui.util.fastForEachIndexed
import androidx.constraintlayout.motion.widget.MotionLayout
+import androidx.constraintlayout.motion.widget.MotionScene
import androidx.dynamicanimation.animation.FloatValueHolder
import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringForce
@@ -47,6 +48,7 @@
import javax.inject.Inject
import kotlin.properties.Delegates
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.mapLatest
@@ -55,6 +57,7 @@
// Ensure roundness and color of button is updated when progress is changed by a minimum fraction.
private const val BUTTON_MIN_VISIBLE_CHANGE = 0.05F
+@OptIn(ExperimentalCoroutinesApi::class)
@VolumeDialogScope
class VolumeDialogRingerViewBinder
@Inject
@@ -208,6 +211,13 @@
ringerState.orientation,
ringerBackgroundView,
)
+ drawerContainer
+ .getTransition(R.id.close_to_open_transition)
+ .setInterpolatorInfo(
+ MotionScene.Transition.INTERPOLATE_REFERENCE_ID,
+ null,
+ R.anim.volume_dialog_ringer_open,
+ )
drawerContainer.transitionToState(
R.id.volume_dialog_ringer_drawer_open
)
@@ -370,6 +380,12 @@
orientation: Int,
) {
setTransition(R.id.close_to_open_transition)
+ getTransition(R.id.close_to_open_transition)
+ .setInterpolatorInfo(
+ MotionScene.Transition.INTERPOLATE_REFERENCE_ID,
+ null,
+ R.anim.volume_dialog_ringer_close,
+ )
updateCloseState(this, selectedIndex, orientation, ringerBackground)
transitionToState(R.id.volume_dialog_ringer_drawer_close)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
index 2e63439..c2a495d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
@@ -64,6 +64,12 @@
"'wght' 500, 'ital' 0.5, 'GRAD' 450",
interp.lerp(startFont, endFont, 0.5f, 0.5f),
)
+
+ // Ensure axes rounded correctly to nearest step
+ assertSameAxes(
+ "'wght' 490, 'ital' 0.5, 'GRAD' 446",
+ interp.lerp(startFont, endFont, 0.492f, 0.492f),
+ )
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt
index 4d0605f..90a8ad5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt
@@ -18,9 +18,12 @@
import android.animation.ValueAnimator
import android.graphics.Color
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.ui.view.GutsViewHolder
import com.android.systemui.media.controls.ui.view.MediaViewHolder
@@ -33,6 +36,7 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
@@ -49,7 +53,9 @@
class ColorSchemeTransitionTest : SysuiTestCase() {
private interface ExtractCB : (ColorScheme) -> Int
+
private interface ApplyCB : (Int) -> Unit
+
private lateinit var colorTransition: AnimatingColorTransition
private lateinit var colorSchemeTransition: ColorSchemeTransition
@@ -79,7 +85,7 @@
mediaViewHolder,
multiRippleController,
turbulenceNoiseController,
- animatingColorTransitionFactory
+ animatingColorTransitionFactory,
)
colorTransition =
@@ -154,10 +160,21 @@
verify(applyColor).invoke(expectedColor)
}
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_A11Y_COLORS)
@Test
- fun testColorSchemeTransition_update() {
+ fun testColorSchemeTransition_update_legacy() {
colorSchemeTransition.updateColorScheme(colorScheme)
verify(mockAnimatingTransition, times(8)).updateColorScheme(colorScheme)
verify(gutsViewHolder).colorScheme = colorScheme
}
+
+ @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_A11Y_COLORS)
+ @Test
+ fun testColorSchemeTransition_update() {
+ colorSchemeTransition.updateColorScheme(colorScheme)
+ verify(mockAnimatingTransition, times(3)).updateColorScheme(colorScheme)
+ verify(gutsViewHolder).setColors(colorScheme)
+ verify(multiRippleController).updateColor(anyInt())
+ verify(turbulenceNoiseController).updateNoiseColor(anyInt())
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
index 6ca4ae2..14b1412 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.media.controls.ui.controller
import android.graphics.Rect
+import android.platform.test.annotations.EnableFlags
import android.provider.Settings
import android.testing.TestableLooper
import android.view.ViewGroup
@@ -36,7 +37,6 @@
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.data.repository.keyguardRepository
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.kosmos.testScope
@@ -50,6 +50,7 @@
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.featurepods.popups.StatusBarPopupChips
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -581,6 +582,36 @@
}
@Test
+ @EnableFlags(StatusBarPopupChips.FLAG_NAME)
+ fun testStatusBarPopupLocation() =
+ testScope.runTest {
+ mediaHierarchyManager.isMediaControlPopupShowing = true
+ runCurrent()
+
+ verify(mediaCarouselController)
+ .onDesiredLocationChanged(
+ eq(MediaHierarchyManager.LOCATION_STATUS_BAR_POPUP),
+ nullable(),
+ eq(false),
+ anyLong(),
+ anyLong(),
+ )
+ clearInvocations(mediaCarouselController)
+
+ mediaHierarchyManager.isMediaControlPopupShowing = false
+ runCurrent()
+
+ verify(mediaCarouselController)
+ .onDesiredLocationChanged(
+ eq(MediaHierarchyManager.LOCATION_QQS),
+ any<MediaHostState>(),
+ eq(false),
+ anyLong(),
+ anyLong(),
+ )
+ }
+
+ @Test
fun testCommunalLocationVisibilityWithShadeShowing() =
testScope.runTest {
whenever(mediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
index cfe34f4..a7464e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
@@ -39,6 +39,7 @@
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import dagger.Lazy
import kotlin.test.assertTrue
import kotlinx.coroutines.Job
import org.junit.After
@@ -96,8 +97,7 @@
qsLogger,
bluetoothController,
featureFlags,
- bluetoothDetailsContentViewModel,
- )
+ ) { bluetoothDetailsContentViewModel }
tile.initialize()
testableLooper.processAllMessages()
@@ -308,7 +308,7 @@
qsLogger: QSLogger,
bluetoothController: BluetoothController,
featureFlags: FeatureFlagsClassic,
- bluetoothDetailsContentViewModel: BluetoothDetailsContentViewModel,
+ lazyBluetoothDetailsContentViewModel: Lazy<BluetoothDetailsContentViewModel>,
) :
BluetoothTile(
qsHost,
@@ -322,7 +322,7 @@
qsLogger,
bluetoothController,
featureFlags,
- bluetoothDetailsContentViewModel,
+ lazyBluetoothDetailsContentViewModel,
) {
var restrictionChecked: String? = null
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 e8b50d5..81213ca 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
@@ -130,7 +130,7 @@
private CollectionReadyForBuildListener mReadyForBuildListener;
private List<NotificationEntryBuilder> mPendingSet = new ArrayList<>();
private List<NotificationEntry> mEntrySet = new ArrayList<>();
- private List<ListEntry> mBuiltList = new ArrayList<>();
+ private List<PipelineEntry> mBuiltList = new ArrayList<>();
private TestableStabilityManager mStabilityManager;
private TestableNotifFilter mFinalizeFilter;
@@ -723,26 +723,26 @@
@Test
public void testNotifSectionsChildrenUpdated() {
- ArrayList<ListEntry> pkg1Entries = new ArrayList<>();
- ArrayList<ListEntry> pkg2Entries = new ArrayList<>();
- ArrayList<ListEntry> pkg3Entries = new ArrayList<>();
+ ArrayList<PipelineEntry> pkg1Entries = new ArrayList<>();
+ ArrayList<PipelineEntry> pkg2Entries = new ArrayList<>();
+ ArrayList<PipelineEntry> pkg3Entries = new ArrayList<>();
final NotifSectioner pkg1Sectioner = spy(new PackageSectioner(PACKAGE_1) {
@Override
- public void onEntriesUpdated(List<ListEntry> entries) {
+ public void onEntriesUpdated(List<PipelineEntry> entries) {
super.onEntriesUpdated(entries);
pkg1Entries.addAll(entries);
}
});
final NotifSectioner pkg2Sectioner = spy(new PackageSectioner(PACKAGE_2) {
@Override
- public void onEntriesUpdated(List<ListEntry> entries) {
+ public void onEntriesUpdated(List<PipelineEntry> entries) {
super.onEntriesUpdated(entries);
pkg2Entries.addAll(entries);
}
});
final NotifSectioner pkg3Sectioner = spy(new PackageSectioner(PACKAGE_3) {
@Override
- public void onEntriesUpdated(List<ListEntry> entries) {
+ public void onEntriesUpdated(List<PipelineEntry> entries) {
super.onEntriesUpdated(entries);
pkg3Entries.addAll(entries);
}
@@ -2478,7 +2478,7 @@
mBuiltList.size());
for (int i = 0; i < expectedEntries.length; i++) {
- ListEntry outEntry = mBuiltList.get(i);
+ PipelineEntry outEntry = mBuiltList.get(i);
ExpectedEntry expectedEntry = expectedEntries[i];
if (expectedEntry instanceof ExpectedNotif) {
@@ -2653,7 +2653,7 @@
}
@Override
- public int compare(@NonNull ListEntry o1, @NonNull ListEntry o2) {
+ public int compare(@NonNull PipelineEntry o1, @NonNull PipelineEntry o2) {
boolean contains1 = mPreferredPackages.contains(
o1.getRepresentativeEntry().getSbn().getPackageName());
boolean contains2 = mPreferredPackages.contains(
@@ -2691,37 +2691,37 @@
}
@Override
- public boolean isInSection(ListEntry entry) {
+ public boolean isInSection(PipelineEntry entry) {
return mPackages.contains(entry.getRepresentativeEntry().getSbn().getPackageName());
}
}
private static class RecordingOnBeforeTransformGroupsListener
implements OnBeforeTransformGroupsListener {
- List<ListEntry> mEntriesReceived;
+ List<PipelineEntry> mEntriesReceived;
@Override
- public void onBeforeTransformGroups(List<ListEntry> list) {
+ public void onBeforeTransformGroups(List<PipelineEntry> list) {
mEntriesReceived = new ArrayList<>(list);
}
}
private static class RecordingOnBeforeSortListener
implements OnBeforeSortListener {
- List<ListEntry> mEntriesReceived;
+ List<PipelineEntry> mEntriesReceived;
@Override
- public void onBeforeSort(List<ListEntry> list) {
+ public void onBeforeSort(List<PipelineEntry> list) {
mEntriesReceived = new ArrayList<>(list);
}
}
private static class RecordingOnBeforeRenderListener
implements OnBeforeRenderListListener {
- List<ListEntry> mEntriesReceived;
+ List<PipelineEntry> mEntriesReceived;
@Override
- public void onBeforeRenderList(List<ListEntry> list) {
+ public void onBeforeRenderList(List<PipelineEntry> list) {
mEntriesReceived = new ArrayList<>(list);
}
}
@@ -2800,7 +2800,7 @@
}
@Override
- public boolean isEntryReorderingAllowed(@NonNull ListEntry entry) {
+ public boolean isEntryReorderingAllowed(@NonNull PipelineEntry entry) {
return mAllowEntryReodering;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
index a1122c3..a553b17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
@@ -18,10 +18,12 @@
import android.hardware.devicestate.DeviceStateManager
import android.hardware.devicestate.DeviceStateManager.FoldStateListener
+import android.platform.test.annotations.DisableFlags
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.foldedDeviceStateList
import com.android.systemui.halfFoldedDeviceState
@@ -46,6 +48,7 @@
@RunWith(AndroidJUnit4::class)
@SmallTest
+@DisableFlags(Flags.FLAG_UNFOLD_LATENCY_TRACKING_FIX)
class UnfoldLatencyTrackerTest : SysuiTestCase() {
@Mock lateinit var latencyTracker: LatencyTracker
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/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
index 562ac0c..804ec9f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
@@ -56,7 +56,7 @@
private StatusBarNotification mSbn = null;
/* ListEntry properties */
- private GroupEntry mParent;
+ private PipelineEntry mParent;
private NotifSection mNotifSection;
/* If set, use this creation time instead of mClock.uptimeMillis */
@@ -91,7 +91,7 @@
}
/** Update an the parent on an existing entry */
- public static void setNewParent(NotificationEntry entry, GroupEntry parent) {
+ public static void setNewParent(NotificationEntry entry, PipelineEntry parent) {
entry.setParent(parent);
}
@@ -135,7 +135,7 @@
/**
* Sets the parent.
*/
- public NotificationEntryBuilder setParent(@Nullable GroupEntry parent) {
+ public NotificationEntryBuilder setParent(@Nullable PipelineEntry parent) {
mParent = parent;
return this;
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt
index d09d010..8ff7c7d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt
@@ -76,6 +76,7 @@
promotedContent: PromotedNotificationContentModel? = null,
contentIntent: PendingIntent? = null,
uid: Int = DEFAULT_UID,
+ appName: String = "Fake name",
) {
if (StatusBarChipsModernization.isEnabled) {
activeNotificationListRepository.addNotif(
@@ -87,6 +88,7 @@
contentIntent = contentIntent,
promotedContent = promotedContent,
uid = uid,
+ appName = appName,
)
)
} else {
@@ -96,6 +98,7 @@
notificationIcon = statusBarChipIconView,
intent = contentIntent,
notificationKey = key,
+ appName = appName,
promotedContent = promotedContent,
)
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
index 9b6f205..8fa82ca 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
@@ -58,6 +58,8 @@
override val defaultDataSubId: MutableStateFlow<Int?> = MutableStateFlow(DEFAULT_DATA_SUB_ID)
+ override val activeMobileDataSubscriptionId: MutableStateFlow<Int?> = MutableStateFlow(null)
+
private val _activeDataConnectionHasDataEnabled = MutableStateFlow(false)
override val activeDataConnectionHasDataEnabled = _activeDataConnectionHasDataEnabled
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKosmos.kt
new file mode 100644
index 0000000..880ba5e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.stackedMobileIconViewModel: StackedMobileIconViewModel by
+ Kosmos.Fixture { StackedMobileIconViewModel(mobileIconsViewModel) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserLockedInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserLockedInteractorKosmos.kt
index fd95508..933c351 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserLockedInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserLockedInteractorKosmos.kt
@@ -17,7 +17,10 @@
package com.android.systemui.user.domain.interactor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.user.data.repository.userRepository
val Kosmos.userLockedInteractor by
- Kosmos.Fixture { UserLockedInteractor(userRepository = userRepository) }
+ Kosmos.Fixture {
+ UserLockedInteractor(backgroundDispatcher = testDispatcher, userRepository = userRepository)
+ }
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/ravenwood/Android.bp b/ravenwood/Android.bp
index 65550f2..ccbc46f 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -121,6 +121,7 @@
name: "ravenwood-helper-framework-runtime",
srcs: [
"runtime-helper-src/framework/**/*.java",
+ ":framework-graphics-srcs",
],
static_libs: [
"ravenwood-runtime-common",
@@ -278,6 +279,7 @@
cc_library_host_shared {
name: "libravenwood_runtime",
defaults: ["ravenwood_jni_defaults"],
+ header_libs: ["libicuuc_headers"],
srcs: [
"runtime-jni/ravenwood_runtime.cpp",
"runtime-jni/ravenwood_os_constants.cpp",
@@ -372,6 +374,13 @@
visibility: ["//visibility:private"],
}
+java_library {
+ name: "ext-ravenwood",
+ installable: false,
+ static_libs: ["ext"],
+ visibility: ["//visibility:private"],
+}
+
filegroup {
name: "ravenwood-data",
device_common_srcs: [
@@ -637,6 +646,7 @@
libs: [
"100-framework-minus-apex.ravenwood",
"200-kxml2-android",
+ "ext-ravenwood",
"ravenwood-runtime-common-ravenwood",
diff --git a/ravenwood/Framework.bp b/ravenwood/Framework.bp
index 71496b0..e366771 100644
--- a/ravenwood/Framework.bp
+++ b/ravenwood/Framework.bp
@@ -419,11 +419,13 @@
"--out-impl-jar $(location ravenwood.jar) " +
"--in-jar $(location :framework-graphics.impl{.jar}) " +
- "--policy-override-file $(location :ravenwood-common-policies) ",
+ "--policy-override-file $(location :ravenwood-common-policies) " +
+ "--policy-override-file $(location :framework-graphics-ravenwood-policies) ",
srcs: [
":framework-graphics.impl{.jar}",
":ravenwood-common-policies",
+ ":framework-graphics-ravenwood-policies",
":ravenwood-standard-options",
],
out: [
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
index a332633..9e6b12f 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
@@ -230,6 +230,16 @@
return mAppContext;
}
+ @Override
+ public boolean isRestricted() {
+ return false;
+ }
+
+ @Override
+ public boolean canLoadUnsafeResources() {
+ return true;
+ }
+
/**
* Wrap the given {@link Supplier} to become memoized.
*
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java
index a208d6d..7e935d0 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java
@@ -48,6 +48,7 @@
android.content.res.AssetManager.class,
android.content.res.StringBlock.class,
android.content.res.XmlBlock.class,
+ android.text.AndroidCharacter.class,
};
/**
@@ -61,15 +62,49 @@
android.graphics.Path.class,
android.graphics.Color.class,
android.graphics.ColorSpace.class,
+ android.graphics.Bitmap.class,
+ android.graphics.BitmapFactory.class,
+ android.graphics.BitmapRegionDecoder.class,
+ android.graphics.Camera.class,
+ android.graphics.Canvas.class,
+ android.graphics.CanvasProperty.class,
+ android.graphics.ColorFilter.class,
+ android.graphics.DrawFilter.class,
+ android.graphics.FontFamily.class,
+ android.graphics.Gainmap.class,
+ android.graphics.ImageDecoder.class,
+ android.graphics.MaskFilter.class,
+ android.graphics.NinePatch.class,
+ android.graphics.Paint.class,
+ android.graphics.PathEffect.class,
+ android.graphics.PathIterator.class,
+ android.graphics.PathMeasure.class,
+ android.graphics.Picture.class,
+ android.graphics.RecordingCanvas.class,
+ android.graphics.Region.class,
+ android.graphics.RenderNode.class,
+ android.graphics.Shader.class,
+ android.graphics.RenderEffect.class,
+ android.graphics.Typeface.class,
+ android.graphics.YuvImage.class,
+ android.graphics.fonts.Font.class,
+ android.graphics.fonts.FontFamily.class,
+ android.graphics.text.LineBreaker.class,
+ android.graphics.text.MeasuredText.class,
+ android.graphics.text.TextRunShaper.class,
+ android.graphics.text.GraphemeBreak.class,
+ android.util.PathParser.class,
};
/**
* Extra strings needed to pass to register_android_graphics_classes().
*
- * `android.graphics.Graphics` is not actually a class, so we just hardcode it here.
+ * Several entries are not actually a class, so we just hardcode them here.
*/
public final static String[] GRAPHICS_EXTRA_INIT_PARAMS = new String[] {
- "android.graphics.Graphics"
+ "android.graphics.Graphics",
+ "android.graphics.ByteBufferStreamAdaptor",
+ "android.graphics.CreateJavaOutputStreamAdaptor"
};
private RavenwoodNativeLoader() {
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index ae88bb2..f205d23 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -43,6 +43,7 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
+import android.graphics.Typeface;
import android.icu.util.ULocale;
import android.os.Binder;
import android.os.Build;
@@ -246,6 +247,13 @@
// Do the basic set up for the android sysprops.
RavenwoodSystemProperties.initialize();
+ // Set ICU data file
+ String icuData = RavenwoodCommonUtils.getRavenwoodRuntimePath()
+ + "ravenwood-data/"
+ + RavenwoodRuntimeNative.getIcuDataName()
+ + ".dat";
+ RavenwoodRuntimeNative.setSystemProperty("ro.icu.data.path", icuData);
+
// Enable all log levels for native logging, until we'll have a way to change the native
// side log level at runtime.
// Do this after loading RAVENWOOD_NATIVE_RUNTIME_NAME (which backs Os.setenv()),
@@ -268,6 +276,11 @@
Objects.requireNonNull(Build.TYPE);
Objects.requireNonNull(Build.VERSION.SDK);
+ // Fonts can only be initialized once
+ Typeface.init();
+ Typeface.loadPreinstalledSystemFontMap();
+ Typeface.loadNativeSystemFonts();
+
System.setProperty(RAVENWOOD_VERSION_JAVA_SYSPROP, "1");
// This will let AndroidJUnit4 use the original runner.
System.setProperty("android.junit.runner",
diff --git a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodJdkPatch.java b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodJdkPatch.java
index 96aed4b..d5a96dd 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodJdkPatch.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodJdkPatch.java
@@ -20,6 +20,7 @@
import java.io.FileDescriptor;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.regex.Pattern;
/**
* Class to host APIs that exist in libcore, but not in standard JRE.
@@ -46,4 +47,22 @@
final var it = map.entrySet().iterator();
return it.hasNext() ? it.next() : null;
}
+
+ /**
+ * Implements Pattern.compile(String)
+ *
+ * ART always assumes UNICODE_CHARACTER_CLASS is set.
+ */
+ public static Pattern compilePattern(String regex) {
+ return Pattern.compile(regex, Pattern.UNICODE_CHARACTER_CLASS);
+ }
+
+ /**
+ * Implements Pattern.compile(String, int)
+ *
+ * ART always assumes UNICODE_CHARACTER_CLASS is set.
+ */
+ public static Pattern compilePattern(String regex, int flag) {
+ return Pattern.compile(regex, flag | Pattern.UNICODE_CHARACTER_CLASS);
+ }
}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
index acbcdf1..0d82a86 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
@@ -66,6 +66,8 @@
public static native int gettid();
+ public static native String getIcuDataName();
+
public static long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException {
return nLseek(JvmWorkaround.getInstance().getFdInt(fd), offset, whence);
}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/dalvik/system/CloseGuard.java b/ravenwood/runtime-helper-src/libcore-fake/dalvik/system/CloseGuard.java
new file mode 100644
index 0000000..82bab64
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/dalvik/system/CloseGuard.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system;
+
+/**
+ * A no-op copy of libcore/dalvik/src/main/java/dalvik/system/CloseGuard.java
+ */
+public final class CloseGuard {
+
+ /**
+ * Returns a CloseGuard instance. {@code #open(String)} can be used to set
+ * up the instance to warn on failure to close.
+ *
+ * @return {@link CloseGuard} instance.
+ *
+ * @hide
+ */
+ public static CloseGuard get() {
+ return new CloseGuard();
+ }
+
+ /**
+ * Enables/disables stack capture and tracking. A call stack is captured
+ * during open(), and open/close events are reported to the Tracker, only
+ * if enabled is true. If a stack trace was captured, the {@link
+ * #getReporter() reporter} is informed of unclosed resources; otherwise a
+ * one-line warning is logged.
+ *
+ * @param enabled whether stack capture and tracking is enabled.
+ *
+ * @hide
+ */
+ public static void setEnabled(boolean enabled) {
+ }
+
+ /**
+ * True if CloseGuard stack capture and tracking are enabled.
+ *
+ * @hide
+ */
+ public static boolean isEnabled() {
+ return false;
+ }
+
+ /**
+ * Used to replace default Reporter used to warn of CloseGuard
+ * violations when stack tracking is enabled. Must be non-null.
+ *
+ * @param rep replacement for default Reporter.
+ *
+ * @hide
+ */
+ public static void setReporter(Reporter rep) {
+ if (rep == null) {
+ throw new NullPointerException("reporter == null");
+ }
+ }
+
+ /**
+ * Returns non-null CloseGuard.Reporter.
+ *
+ * @return CloseGuard's Reporter.
+ *
+ * @hide
+ */
+ public static Reporter getReporter() {
+ return null;
+ }
+
+ /**
+ * Sets the {@link Tracker} that is notified when resources are allocated and released.
+ * The Tracker is invoked only if CloseGuard {@link #isEnabled()} held when {@link #open()}
+ * was called. A null argument disables tracking.
+ *
+ * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
+ * MUST NOT be used for any other purposes.
+ *
+ * @hide
+ */
+ public static void setTracker(Tracker tracker) {
+ }
+
+ /**
+ * Returns {@link #setTracker(Tracker) last Tracker that was set}, or null to indicate
+ * there is none.
+ *
+ * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
+ * MUST NOT be used for any other purposes.
+ *
+ * @hide
+ */
+ public static Tracker getTracker() {
+ return null;
+ }
+
+ private CloseGuard() {}
+
+ /**
+ * {@code open} initializes the instance with a warning that the caller
+ * should have explicitly called the {@code closer} method instead of
+ * relying on finalization.
+ *
+ * @param closer non-null name of explicit termination method. Printed by warnIfOpen.
+ * @throws NullPointerException if closer is null.
+ *
+ * @hide
+ */
+ public void open(String closer) {
+ openWithCallSite(closer, null /* callsite */);
+ }
+
+ /**
+ * Like {@link #open(String)}, but with explicit callsite string being passed in for better
+ * performance.
+ * <p>
+ * This only has better performance than {@link #open(String)} if {@link #isEnabled()} returns {@code true}, which
+ * usually shouldn't happen on release builds.
+ *
+ * @param closer Non-null name of explicit termination method. Printed by warnIfOpen.
+ * @param callsite Non-null string uniquely identifying the callsite.
+ *
+ * @hide
+ */
+ public void openWithCallSite(String closer, String callsite) {
+ }
+
+ // We keep either an allocation stack containing the closer String or, when
+ // in disabled state, just the closer String.
+ // We keep them in a single field only to minimize overhead.
+ private Object /* String or Throwable */ closerNameOrAllocationInfo;
+
+ /**
+ * Marks this CloseGuard instance as closed to avoid warnings on
+ * finalization.
+ *
+ * @hide
+ */
+ public void close() {
+ }
+
+ /**
+ * Logs a warning if the caller did not properly cleanup by calling an
+ * explicit close method before finalization. If CloseGuard was enabled
+ * when the CloseGuard was created, passes the stacktrace associated with
+ * the allocation to the current reporter. If it was not enabled, it just
+ * directly logs a brief message.
+ *
+ * @hide
+ */
+ public void warnIfOpen() {
+ }
+
+
+ /**
+ * Interface to allow customization of tracking behaviour.
+ *
+ * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
+ * MUST NOT be used for any other purposes.
+ *
+ * @hide
+ */
+ public interface Tracker {
+ void open(Throwable allocationSite);
+ void close(Throwable allocationSite);
+ }
+
+ /**
+ * Interface to allow customization of reporting behavior.
+ * @hide
+ */
+ public interface Reporter {
+ /**
+ *
+ * @hide
+ */
+ void report(String message, Throwable allocationSite);
+
+ /**
+ *
+ * @hide
+ */
+ default void report(String message) {}
+ }
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/io/IoBridge.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/io/IoBridge.java
new file mode 100644
index 0000000..2a1ee25
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/io/IoBridge.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.io;
+
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+public class IoBridge {
+
+ public static void closeAndSignalBlockedThreads(FileDescriptor fd) throws IOException {
+ if (fd == null) {
+ return;
+ }
+ try {
+ Os.close(fd);
+ } catch (ErrnoException errnoException) {
+ throw errnoException.rethrowAsIOException();
+ }
+ }
+
+ public static FileDescriptor open(String path, int flags) throws FileNotFoundException {
+ FileDescriptor fd = null;
+ try {
+ fd = Os.open(path, flags, 0666);
+ // Posix open(2) fails with EISDIR only if you ask for write permission.
+ // Java disallows reading directories too.f
+ if (OsConstants.S_ISDIR(Os.fstat(fd).st_mode)) {
+ throw new ErrnoException("open", OsConstants.EISDIR);
+ }
+ return fd;
+ } catch (ErrnoException errnoException) {
+ try {
+ if (fd != null) {
+ closeAndSignalBlockedThreads(fd);
+ }
+ } catch (IOException ignored) {
+ }
+ FileNotFoundException ex = new FileNotFoundException(path + ": "
+ + errnoException.getMessage());
+ ex.initCause(errnoException);
+ throw ex;
+ }
+ }
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java
index ad86135..cf1a513 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java
@@ -35,6 +35,11 @@
return new NativeAllocationRegistry(classLoader, freeFunction, size);
}
+ public static NativeAllocationRegistry createNonmalloced(
+ Class clazz, long freeFunction, long size) {
+ return new NativeAllocationRegistry(clazz.getClassLoader(), freeFunction, size);
+ }
+
public static NativeAllocationRegistry createMalloced(
ClassLoader classLoader, long freeFunction, long size) {
return new NativeAllocationRegistry(classLoader, freeFunction, size);
@@ -45,6 +50,11 @@
return new NativeAllocationRegistry(classLoader, freeFunction, 0);
}
+ public static NativeAllocationRegistry createMalloced(
+ Class clazz, long freeFunction, long size) {
+ return new NativeAllocationRegistry(clazz.getClassLoader(), freeFunction, size);
+ }
+
public NativeAllocationRegistry(ClassLoader classLoader, long freeFunction, long size) {
if (size < 0) {
throw new IllegalArgumentException("Invalid native allocation size: " + size);
@@ -52,6 +62,37 @@
mFreeFunction = freeFunction;
}
+ private class CleanerThunk implements Runnable {
+ private long nativePtr;
+
+ public CleanerThunk() {
+ nativePtr = 0;
+ }
+
+ public void setNativePtr(long ptr) {
+ nativePtr = ptr;
+ }
+
+ @Override
+ public void run() {
+ if (nativePtr != 0) {
+ applyFreeFunction(mFreeFunction, nativePtr);
+ }
+ }
+ }
+
+ private static class CleanableRunner implements Runnable {
+ private final Cleaner.Cleanable mCleanable;
+
+ public CleanableRunner(Cleaner.Cleanable cleanable) {
+ mCleanable = cleanable;
+ }
+
+ public void run() {
+ mCleanable.clean();
+ }
+ }
+
public Runnable registerNativeAllocation(Object referent, long nativePtr) {
if (referent == null) {
throw new IllegalArgumentException("referent is null");
@@ -60,13 +101,25 @@
throw new IllegalArgumentException("nativePtr is null");
}
- final Runnable releaser = () -> {
- RavenwoodRuntimeNative.applyFreeFunction(mFreeFunction, nativePtr);
- };
- sCleaner.register(referent, releaser);
+ final CleanerThunk thunk;
+ final CleanableRunner result;
+ try {
+ thunk = new CleanerThunk();
+ final var cleanable = sCleaner.register(referent, thunk);
+ result = new CleanableRunner(cleanable);
+ } catch (VirtualMachineError vme /* probably OutOfMemoryError */) {
+ applyFreeFunction(mFreeFunction, nativePtr);
+ throw vme;
+ }
+ // Enable the cleaner only after we can no longer throw anything, including OOME.
+ thunk.setNativePtr(nativePtr);
// Ensure that cleaner doesn't get invoked before we enable it.
Reference.reachabilityFence(referent);
- return releaser;
+ return result;
+ }
+
+ public static void applyFreeFunction(long freeFunction, long nativePtr) {
+ RavenwoodRuntimeNative.applyFreeFunction(freeFunction, nativePtr);
}
}
diff --git a/ravenwood/runtime-jni/ravenwood_runtime.cpp b/ravenwood/runtime-jni/ravenwood_runtime.cpp
index 8d8ed71..01ebdc9 100644
--- a/ravenwood/runtime-jni/ravenwood_runtime.cpp
+++ b/ravenwood/runtime-jni/ravenwood_runtime.cpp
@@ -20,6 +20,7 @@
#include <sys/syscall.h>
#include <unistd.h>
#include <utils/misc.h>
+#include <unicode/utypes.h>
#include <string>
@@ -183,6 +184,10 @@
return syscall(__NR_gettid);
}
+static jstring getIcuDataName(JNIEnv* env, jclass clazz) {
+ return env->NewStringUTF(U_ICUDATA_NAME);
+}
+
// ---- Registration ----
extern void register_android_system_OsConstants(JNIEnv* env);
@@ -201,6 +206,7 @@
{ "setenv", "(Ljava/lang/String;Ljava/lang/String;Z)V", (void*)Linux_setenv },
{ "getpid", "()I", (void*)Linux_getpid },
{ "gettid", "()I", (void*)Linux_gettid },
+ { "getIcuDataName", "()Ljava/lang/String;", (void*)getIcuDataName },
};
extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
index e202d0e..7462cc2 100644
--- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
@@ -259,6 +259,8 @@
android.database.sqlite.SQLiteException
android.text.TextUtils
+android.text.Html
+android.text.HtmlToSpannedConverter
android.accounts.Account
@@ -278,6 +280,10 @@
android.graphics.Rect
android.graphics.RectF
+android.graphics.fonts.SystemFonts
+
+android.graphics.text.LineBreakConfig
+
android.content.ContentProvider
android.app.ActivityManager
@@ -383,3 +389,228 @@
com.android.server.compat.*
com.android.internal.compat.*
android.app.AppCompatCallbacks
+android.graphics.AvoidXfermode
+android.graphics.BLASTBufferQueue
+android.graphics.BaseCanvas
+android.graphics.BaseRecordingCanvas
+android.graphics.Bitmap
+android.graphics.BitmapFactory
+android.graphics.BitmapRegionDecoder
+android.graphics.BitmapShader
+android.graphics.BlendMode
+android.graphics.BlendModeColorFilter
+android.graphics.BlurMaskFilter
+android.graphics.Camera
+android.graphics.Canvas
+android.graphics.CanvasProperty
+android.graphics.ColorFilter
+android.graphics.ColorMatrix
+android.graphics.ColorMatrixColorFilter
+android.graphics.Compatibility
+android.graphics.ComposePathEffect
+android.graphics.ComposeShader
+android.graphics.CornerPathEffect
+android.graphics.DashPathEffect
+android.graphics.DiscretePathEffect
+android.graphics.DrawFilter
+android.graphics.EmbossMaskFilter
+android.graphics.FontFamily
+android.graphics.FontListParser
+android.graphics.ForceDarkType
+android.graphics.FrameInfo
+android.graphics.Gainmap
+android.graphics.GraphicBuffer
+android.graphics.GraphicsProtos
+android.graphics.GraphicsStatsService
+android.graphics.HardwareBufferRenderer
+android.graphics.HardwareRenderer
+android.graphics.HardwareRendererObserver
+android.graphics.ImageDecoder
+android.graphics.ImageFormat
+android.graphics.LayerRasterizer
+android.graphics.LeakyTypefaceStorage
+android.graphics.LightingColorFilter
+android.graphics.LinearGradient
+android.graphics.MaskFilter
+android.graphics.Mesh
+android.graphics.MeshSpecification
+android.graphics.Movie
+android.graphics.NinePatch
+android.graphics.Paint
+android.graphics.PaintFlagsDrawFilter
+android.graphics.PathDashPathEffect
+android.graphics.PathEffect
+android.graphics.PathIterator
+android.graphics.PathMeasure
+android.graphics.Picture
+android.graphics.PixelXorXfermode
+android.graphics.PorterDuff
+android.graphics.PorterDuffColorFilter
+android.graphics.PorterDuffXfermode
+android.graphics.PostProcessor
+android.graphics.RadialGradient
+android.graphics.Rasterizer
+android.graphics.RecordingCanvas
+android.graphics.Region
+android.graphics.RegionIterator
+android.graphics.RenderEffect
+android.graphics.RenderNode
+android.graphics.RuntimeColorFilter
+android.graphics.RuntimeShader
+android.graphics.RuntimeXfermode
+android.graphics.Shader
+android.graphics.SumPathEffect
+android.graphics.SurfaceTexture
+android.graphics.SweepGradient
+android.graphics.TableMaskFilter
+android.graphics.TemporaryBuffer
+android.graphics.TextureLayer
+android.graphics.Typeface
+android.graphics.Xfermode
+android.graphics.YuvImage
+android.graphics.fonts.Font
+android.graphics.fonts.FontCustomizationParser
+android.graphics.fonts.FontFamily
+android.graphics.fonts.FontFamilyUpdateRequest
+android.graphics.fonts.FontFileUpdateRequest
+android.graphics.fonts.FontFileUtil
+android.graphics.fonts.FontStyle
+android.graphics.fonts.FontVariationAxis
+android.graphics.text.GraphemeBreak
+android.graphics.text.LineBreaker
+android.graphics.text.MeasuredText
+android.graphics.text.PositionedGlyphs
+android.graphics.text.TextRunShaper
+android.text.AlteredCharSequence
+android.text.AndroidBidi
+android.text.AndroidCharacter
+android.text.Annotation
+android.text.AutoGrowArray
+android.text.AutoText
+android.text.BidiFormatter
+android.text.BoringLayout
+android.text.CharSequenceCharacterIterator
+android.text.ClipboardManager
+android.text.DynamicLayout
+android.text.Editable
+android.text.Emoji
+android.text.EmojiConsistency
+android.text.FontConfig
+android.text.GetChars
+android.text.GraphemeClusterSegmentFinder
+android.text.GraphicsOperations
+android.text.Highlights
+android.text.Hyphenator
+android.text.InputFilter
+android.text.InputType
+android.text.Layout
+android.text.LoginFilter
+android.text.MeasuredParagraph
+android.text.NoCopySpan
+android.text.PackedIntVector
+android.text.PackedObjectVector
+android.text.ParcelableSpan
+android.text.PrecomputedText
+android.text.SegmentFinder
+android.text.Selection
+android.text.SpanColors
+android.text.SpanSet
+android.text.SpanWatcher
+android.text.Spannable
+android.text.SpannableString
+android.text.SpannableStringBuilder
+android.text.SpannableStringInternal
+android.text.Spanned
+android.text.SpannedString
+android.text.StaticLayout
+android.text.TextDirectionHeuristic
+android.text.TextDirectionHeuristics
+android.text.TextLine
+android.text.TextPaint
+android.text.TextShaper
+android.text.TextWatcher
+android.text.WordSegmentFinder
+android.text.format.DateFormat
+android.text.format.DateIntervalFormat
+android.text.format.DateTimeFormat
+android.text.format.DateUtils
+android.text.format.DateUtilsBridge
+android.text.format.Formatter
+android.text.format.RelativeDateTimeFormatter
+android.text.format.Time
+android.text.format.TimeFormatter
+android.text.format.TimeMigrationUtils
+android.text.method.AllCapsTransformationMethod
+android.text.method.ArrowKeyMovementMethod
+android.text.method.BaseKeyListener
+android.text.method.BaseMovementMethod
+android.text.method.CharacterPickerDialog
+android.text.method.DateKeyListener
+android.text.method.DateTimeKeyListener
+android.text.method.DialerKeyListener
+android.text.method.DigitsKeyListener
+android.text.method.HideReturnsTransformationMethod
+android.text.method.InsertModeTransformationMethod
+android.text.method.KeyListener
+android.text.method.LinkMovementMethod
+android.text.method.MetaKeyKeyListener
+android.text.method.MovementMethod
+android.text.method.MultiTapKeyListener
+android.text.method.NumberKeyListener
+android.text.method.OffsetMapping
+android.text.method.PasswordTransformationMethod
+android.text.method.QwertyKeyListener
+android.text.method.ReplacementTransformationMethod
+android.text.method.ScrollingMovementMethod
+android.text.method.SingleLineTransformationMethod
+android.text.method.TextKeyListener
+android.text.method.TimeKeyListener
+android.text.method.Touch
+android.text.method.TransformationMethod
+android.text.method.TransformationMethod2
+android.text.method.TranslationTransformationMethod
+android.text.method.WordIterator
+android.text.style.AbsoluteSizeSpan
+android.text.style.AccessibilityClickableSpan
+android.text.style.AccessibilityReplacementSpan
+android.text.style.AccessibilityURLSpan
+android.text.style.AlignmentSpan
+android.text.style.BackgroundColorSpan
+android.text.style.BulletSpan
+android.text.style.CharacterStyle
+android.text.style.ClickableSpan
+android.text.style.ForegroundColorSpan
+android.text.style.IconMarginSpan
+android.text.style.LeadingMarginSpan
+android.text.style.LineBackgroundSpan
+android.text.style.LineBreakConfigSpan
+android.text.style.LineHeightSpan
+android.text.style.LocaleSpan
+android.text.style.MaskFilterSpan
+android.text.style.MetricAffectingSpan
+android.text.style.NoWritingToolsSpan
+android.text.style.ParagraphStyle
+android.text.style.QuoteSpan
+android.text.style.RasterizerSpan
+android.text.style.RelativeSizeSpan
+android.text.style.ReplacementSpan
+android.text.style.ScaleXSpan
+android.text.style.SpanUtils
+android.text.style.SpellCheckSpan
+android.text.style.StrikethroughSpan
+android.text.style.StyleSpan
+android.text.style.SubscriptSpan
+android.text.style.SuggestionRangeSpan
+android.text.style.SuggestionSpan
+android.text.style.SuperscriptSpan
+android.text.style.TabStopSpan
+android.text.style.TextAppearanceSpan
+android.text.style.TtsSpan
+android.text.style.TypefaceSpan
+android.text.style.URLSpan
+android.text.style.UnderlineSpan
+android.text.style.UpdateAppearance
+android.text.style.UpdateLayout
+android.text.style.WrapTogetherSpan
+android.text.util.Rfc822Token
+android.text.util.Rfc822Tokenizer
diff --git a/ravenwood/texts/ravenwood-build.prop b/ravenwood/texts/ravenwood-build.prop
index 7cc4454..37c50f1 100644
--- a/ravenwood/texts/ravenwood-build.prop
+++ b/ravenwood/texts/ravenwood-build.prop
@@ -8,7 +8,11 @@
ro.soc.model=Ravenwood
ro.debuggable=1
-# The ones starting with "ro.product" or "ro.bild" will be copied to all "partitions" too.
+# For the graphics stack
+ro.hwui.max_texture_allocation_size=104857600
+persist.sys.locale=en-US
+
+# The ones starting with "ro.product" or "ro.build" will be copied to all "partitions" too.
# See RavenwoodSystemProperties.
ro.product.brand=Android
ro.product.device=Ravenwood
diff --git a/ravenwood/texts/ravenwood-common-policies.txt b/ravenwood/texts/ravenwood-common-policies.txt
index fd4ea6c..f0f4b85 100644
--- a/ravenwood/texts/ravenwood-common-policies.txt
+++ b/ravenwood/texts/ravenwood-common-policies.txt
@@ -21,3 +21,7 @@
method setInt$ @com.android.ravenwood.RavenwoodJdkPatch.setInt$
class java.util.LinkedHashMap # no-pta
method eldest @com.android.ravenwood.RavenwoodJdkPatch.eldest
+
+# Always set flag UNICODE_CHARACTER_CLASS when compiling regex
+class java.util.regex.Pattern keep
+ method compile @com.android.ravenwood.RavenwoodJdkPatch.compilePattern
diff --git a/ravenwood/texts/ravenwood-framework-policies.txt b/ravenwood/texts/ravenwood-framework-policies.txt
index fff9e6a..0695316 100644
--- a/ravenwood/texts/ravenwood-framework-policies.txt
+++ b/ravenwood/texts/ravenwood-framework-policies.txt
@@ -63,3 +63,22 @@
# Just enough to allow ResourcesManager to run
class android.hardware.display.DisplayManagerGlobal keep # no-pta
method getInstance ()Landroid/hardware/display/DisplayManagerGlobal; ignore # no-pta
+
+# Bare minimum to support running ImageDecoderTest
+class android.graphics.drawable.Drawable$ConstantState keepclass # no-pta
+class android.graphics.drawable.BitmapDrawable$BitmapState keepclass # no-pta
+class android.graphics.drawable.BitmapDrawable keep # no-pta
+ method <init> (Landroid/content/res/Resources;Landroid/graphics/Bitmap;)V keep
+ method init * keep
+ method updateLocalState * keep
+ method computeBitmapSize * keep
+ method getIntrinsicWidth * keep
+ method getIntrinsicHeight * keep
+ method getBitmap * keep
+class android.graphics.drawable.Drawable keep # no-pta
+ method <init> ()V keep
+ method resolveDensity * keep
+ method updateBlendModeFilter * ignore
+
+class android.os.StrictMode keep # no-pta
+ method noteSlowCall (Ljava/lang/String;)V ignore
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 5e1fe8a6..b52b3dab 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -145,16 +145,6 @@
}
flag {
- name: "enable_magnification_follows_mouse_bugfix"
- namespace: "accessibility"
- description: "Whether to enable mouse following for fullscreen magnification"
- bug: "354696546"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "enable_magnification_follows_mouse_with_pointer_motion_filter"
namespace: "accessibility"
description: "Whether to enable mouse following using pointer motion filter"
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 9b5f22a..4e41808 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -53,7 +53,6 @@
import com.android.server.accessibility.magnification.FullScreenMagnificationVibrationHelper;
import com.android.server.accessibility.magnification.MagnificationGestureHandler;
import com.android.server.accessibility.magnification.MagnificationKeyHandler;
-import com.android.server.accessibility.magnification.MouseEventHandler;
import com.android.server.accessibility.magnification.WindowMagnificationGestureHandler;
import com.android.server.accessibility.magnification.WindowMagnificationPromptController;
import com.android.server.policy.WindowManagerPolicy;
@@ -899,8 +898,7 @@
triggerable,
new WindowMagnificationPromptController(displayContext, mUserId),
displayId,
- fullScreenMagnificationVibrationHelper,
- new MouseEventHandler(controller));
+ fullScreenMagnificationVibrationHelper);
}
return magnificationGestureHandler;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
index 4fa0d50..aa82df4 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
@@ -101,8 +101,15 @@
}
@Override
- public void toggleAutoclickPause() {
- // TODO(b/388872274): allows users to pause the autoclick.
+ public void toggleAutoclickPause(boolean paused) {
+ if (paused) {
+ if (mClickScheduler != null) {
+ mClickScheduler.cancel();
+ }
+ if (mAutoclickIndicatorScheduler != null) {
+ mAutoclickIndicatorScheduler.cancel();
+ }
+ }
}
};
@@ -133,7 +140,9 @@
mAutoclickIndicatorScheduler);
}
- handleMouseMotion(event, policyFlags);
+ if (!isPaused()) {
+ handleMouseMotion(event, policyFlags);
+ }
} else if (mClickScheduler != null) {
mClickScheduler.cancel();
}
@@ -216,6 +225,11 @@
}
}
+ private boolean isPaused() {
+ // TODO (b/397460424): Unpause when hovering over panel.
+ return Flags.enableAutoclickIndicator() && mAutoclickTypePanel.isPaused();
+ }
+
/**
* Observes autoclick setting values, and updates ClickScheduler delay and indicator size
* whenever the setting value changes.
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickIndicatorView.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickIndicatorView.java
index 01f359f..6beb47a 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickIndicatorView.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickIndicatorView.java
@@ -123,6 +123,7 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Get the screen dimensions.
+ // TODO(b/397944891): Handle device rotation case.
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
int screenWidth = displayMetrics.widthPixels;
int screenHeight = displayMetrics.heightPixels;
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java
index 23c5cc4..342675a 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java
@@ -25,6 +25,7 @@
import android.graphics.drawable.GradientDrawable;
import android.view.Gravity;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
@@ -51,6 +52,16 @@
public static final int CORNER_TOP_LEFT = 2;
public static final int CORNER_TOP_RIGHT = 3;
+ // Distance between panel and screen edge.
+ // TODO(b/396402941): Finalize edge margin.
+ private static final int PANEL_EDGE_MARGIN = 15;
+
+ // Touch point when drag starts, it can be anywhere inside the panel.
+ private float mTouchStartX, mTouchStartY;
+ // Initial panel position in screen coordinates.
+ private int mPanelStartX, mPanelStartY;
+ private boolean mIsDragging = false;
+
// Types of click the AutoclickTypePanel supports.
@IntDef({
AUTOCLICK_TYPE_LEFT_CLICK,
@@ -79,11 +90,20 @@
// An interface exposed to {@link AutoclickController) to handle different actions on the panel,
// including changing autoclick type, pausing/resuming autoclick.
public interface ClickPanelControllerInterface {
- // Allows users to change a different autoclick type.
+ /**
+ * Allows users to change a different autoclick type.
+ *
+ * @param clickType The new autoclick type to use. Should be one of the values defined in
+ * {@link AutoclickType}.
+ */
void handleAutoclickTypeChange(@AutoclickType int clickType);
- // Allows users to pause/resume the autoclick.
- void toggleAutoclickPause();
+ /**
+ * Allows users to pause or resume autoclick.
+ *
+ * @param paused {@code true} to pause autoclick, {@code false} to resume.
+ */
+ void toggleAutoclickPause(boolean paused);
}
private final Context mContext;
@@ -92,6 +112,8 @@
private final WindowManager mWindowManager;
+ private WindowManager.LayoutParams mParams;
+
private final ClickPanelControllerInterface mClickPanelController;
// Whether the panel is expanded or not.
@@ -124,6 +146,7 @@
mContext = context;
mWindowManager = windowManager;
mClickPanelController = clickPanelController;
+ mParams = getDefaultLayoutParams();
mPauseButtonDrawable = mContext.getDrawable(
R.drawable.accessibility_autoclick_pause);
@@ -145,6 +168,91 @@
mPositionButton = mContentView.findViewById(R.id.accessibility_autoclick_position_layout);
initializeButtonState();
+
+ // Set up touch event handling for the panel to allow the user to drag and reposition the
+ // panel by touching and moving it.
+ mContentView.setOnTouchListener(this::onPanelTouch);
+ }
+
+ /**
+ * Handles touch events on the panel, enabling the user to drag and reposition it.
+ * This function supports the draggable panel feature, allowing users to move the panel
+ * to different screen locations for better usability and customization.
+ */
+ private boolean onPanelTouch(View v, MotionEvent event) {
+ // TODO(b/397681794): Make sure this works on multiple screens.
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ // Store initial touch positions.
+ mTouchStartX = event.getRawX();
+ mTouchStartY = event.getRawY();
+
+ // Store initial panel position relative to screen's top-left corner.
+ // getLocationOnScreen provides coordinates relative to the top-left corner of the
+ // screen's display. We are using this coordinate system to consistently track the
+ // panel's position during drag operations.
+ int[] location = new int[2];
+ v.getLocationOnScreen(location);
+ mPanelStartX = location[0];
+ mPanelStartY = location[1];
+ return true;
+ case MotionEvent.ACTION_MOVE:
+ mIsDragging = true;
+
+ // Set panel gravity to TOP|LEFT to match getLocationOnScreen's coordinate system
+ mParams.gravity = Gravity.LEFT | Gravity.TOP;
+
+ if (mIsDragging) {
+ // Calculate touch distance moved from start position.
+ float deltaX = event.getRawX() - mTouchStartX;
+ float deltaY = event.getRawY() - mTouchStartY;
+
+ // Update panel position, based on Top-Left absolute positioning.
+ mParams.x = mPanelStartX + (int) deltaX;
+ mParams.y = mPanelStartY + (int) deltaY;
+ mWindowManager.updateViewLayout(mContentView, mParams);
+ }
+ return true;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ if (mIsDragging) {
+ // When drag ends, snap panel to nearest edge.
+ snapToNearestEdge(mParams);
+ }
+ mIsDragging = false;
+ return true;
+ }
+ return false;
+ }
+
+ private void snapToNearestEdge(WindowManager.LayoutParams params) {
+ // Get screen width to determine which side to snap to.
+ // TODO(b/397944891): Handle device rotation case.
+ int screenWidth = mContext.getResources().getDisplayMetrics().widthPixels;
+ int yPosition = params.y;
+
+ // Determine which half of the screen the panel is on.
+ boolean isOnLeftHalf = params.x < screenWidth / 2;
+
+ if (isOnLeftHalf) {
+ // Snap to left edge. Set params.gravity to make sure x, y offsets from correct anchor.
+ params.gravity = Gravity.START | Gravity.TOP;
+ // Set the current corner to be bottom-left to ensure that the subsequent reposition
+ // action rotates the panel clockwise from bottom-left towards top-left.
+ mCurrentCornerIndex = 1;
+ } else {
+ // Snap to right edge. Set params.gravity to make sure x, y offsets from correct anchor.
+ params.gravity = Gravity.END | Gravity.TOP;
+ // Set the current corner to be top-right to ensure that the subsequent reposition
+ // action rotates the panel clockwise from top-right towards bottom-right.
+ mCurrentCornerIndex = 3;
+ }
+
+ // Apply final position: set params.x to be edge margin, params.y to maintain vertical
+ // position.
+ params.x = PANEL_EDGE_MARGIN;
+ params.y = yPosition;
+ mWindowManager.updateViewLayout(mContentView, params);
}
private void initializeButtonState() {
@@ -200,7 +308,7 @@
}
public void show() {
- mWindowManager.addView(mContentView, getLayoutParams());
+ mWindowManager.addView(mContentView, mParams);
}
public void hide() {
@@ -211,6 +319,10 @@
mWindowManager.removeView(mContentView);
}
+ public boolean isPaused() {
+ return mPaused;
+ }
+
/** Toggles the panel expanded or collapsed state. */
private void togglePanelExpansion(@AutoclickType int clickType) {
final LinearLayout button = getButtonFromClickType(clickType);
@@ -234,6 +346,7 @@
private void togglePause() {
mPaused = !mPaused;
+ mClickPanelController.toggleAutoclickPause(mPaused);
ImageButton imageButton = (ImageButton) mPauseButton.getChildAt(/* index= */ 0);
if (mPaused) {
@@ -277,9 +390,8 @@
@Corner int nextCornerIndex = (mCurrentCornerIndex + 1) % CORNER_ROTATION_ORDER.length;
mCurrentCornerIndex = nextCornerIndex;
- // getLayoutParams() will update the panel position based on current corner.
- WindowManager.LayoutParams params = getLayoutParams();
- mWindowManager.updateViewLayout(mContentView, params);
+ setPanelPositionForCorner(mParams, mCurrentCornerIndex);
+ mWindowManager.updateViewLayout(mContentView, mParams);
}
private void setPanelPositionForCorner(WindowManager.LayoutParams params, @Corner int corner) {
@@ -289,22 +401,22 @@
switch (corner) {
case CORNER_BOTTOM_RIGHT:
params.gravity = Gravity.END | Gravity.BOTTOM;
- params.x = 15;
+ params.x = PANEL_EDGE_MARGIN;
params.y = 90;
break;
case CORNER_BOTTOM_LEFT:
params.gravity = Gravity.START | Gravity.BOTTOM;
- params.x = 15;
+ params.x = PANEL_EDGE_MARGIN;
params.y = 90;
break;
case CORNER_TOP_LEFT:
params.gravity = Gravity.START | Gravity.TOP;
- params.x = 15;
+ params.x = PANEL_EDGE_MARGIN;
params.y = 30;
break;
case CORNER_TOP_RIGHT:
params.gravity = Gravity.END | Gravity.TOP;
- params.x = 15;
+ params.x = PANEL_EDGE_MARGIN;
params.y = 30;
break;
default:
@@ -329,13 +441,22 @@
return mCurrentCornerIndex;
}
+ @VisibleForTesting
+ WindowManager.LayoutParams getLayoutParamsForTesting() {
+ return mParams;
+ }
+
+ @VisibleForTesting
+ boolean getIsDraggingForTesting() {
+ return mIsDragging;
+ }
+
/**
* Retrieves the layout params for AutoclickIndicatorView, used when it's added to the Window
* Manager.
*/
- @VisibleForTesting
@NonNull
- WindowManager.LayoutParams getLayoutParams() {
+ private WindowManager.LayoutParams getDefaultLayoutParams() {
final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
@@ -348,7 +469,7 @@
mContext.getString(R.string.accessibility_autoclick_type_settings_panel_title);
layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
- setPanelPositionForCorner(layoutParams, mCurrentCornerIndex);
+ setPanelPositionForCorner(layoutParams, CORNER_BOTTOM_RIGHT);
return layoutParams;
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index d11ae0a..e0dd8b6 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -182,7 +182,7 @@
private final int mMinimumVelocity;
private final int mMaximumVelocity;
- private MouseEventHandler mMouseEventHandler;
+ private final MouseEventHandler mMouseEventHandler;
public FullScreenMagnificationGestureHandler(
@UiContext Context context,
@@ -194,8 +194,7 @@
boolean detectShortcutTrigger,
@NonNull WindowMagnificationPromptController promptController,
int displayId,
- FullScreenMagnificationVibrationHelper fullScreenMagnificationVibrationHelper,
- MouseEventHandler mouseEventHandler) {
+ FullScreenMagnificationVibrationHelper fullScreenMagnificationVibrationHelper) {
this(
context,
fullScreenMagnificationController,
@@ -210,8 +209,7 @@
/* magnificationLogger= */ null,
ViewConfiguration.get(context),
new OneFingerPanningSettingsProvider(
- context, Flags.enableMagnificationOneFingerPanningGesture()),
- mouseEventHandler);
+ context, Flags.enableMagnificationOneFingerPanningGesture()));
}
/** Constructor for tests. */
@@ -229,8 +227,7 @@
FullScreenMagnificationVibrationHelper fullScreenMagnificationVibrationHelper,
MagnificationLogger magnificationLogger,
ViewConfiguration viewConfiguration,
- OneFingerPanningSettingsProvider oneFingerPanningSettingsProvider,
- MouseEventHandler mouseEventHandler) {
+ OneFingerPanningSettingsProvider oneFingerPanningSettingsProvider) {
super(displayId, detectSingleFingerTripleTap, detectTwoFingerTripleTap,
detectShortcutTrigger, trace, callback);
if (DEBUG_ALL) {
@@ -316,7 +313,7 @@
mOverscrollEdgeSlop = context.getResources().getDimensionPixelSize(
R.dimen.accessibility_fullscreen_magnification_gesture_edge_slop);
mIsWatch = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
- mMouseEventHandler = mouseEventHandler;
+ mMouseEventHandler = new MouseEventHandler(mFullScreenMagnificationController);
if (mDetectShortcutTrigger) {
mScreenStateReceiver = new ScreenStateReceiver(context, this);
@@ -340,15 +337,14 @@
@Override
void handleMouseOrStylusEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- if (Flags.enableMagnificationFollowsMouseBugfix()) {
- if (mFullScreenMagnificationController.isActivated(mDisplayId)) {
- // TODO(b/354696546): Allow mouse/stylus to activate whichever display they are
- // over, rather than only interacting with the current display.
-
- // Send through the mouse/stylus event handler.
- mMouseEventHandler.onEvent(event, mDisplayId);
- }
+ if (!mFullScreenMagnificationController.isActivated(mDisplayId)) {
+ return;
}
+ // TODO(b/354696546): Allow mouse/stylus to activate whichever display they are
+ // over, rather than only interacting with the current display.
+
+ // Send through the mouse/stylus event handler.
+ mMouseEventHandler.onEvent(event, mDisplayId);
}
private void handleTouchEventWith(
@@ -1170,8 +1166,7 @@
protected void cacheDelayedMotionEvent(MotionEvent event, MotionEvent rawEvent,
int policyFlags) {
- if (Flags.enableMagnificationFollowsMouseBugfix()
- && !event.isFromSource(SOURCE_TOUCHSCREEN)) {
+ if (!event.isFromSource(SOURCE_TOUCHSCREEN)) {
// Only touch events need to be cached and sent later.
return;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
index fa86ba3..6b39c98 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
@@ -146,8 +146,7 @@
} break;
case SOURCE_MOUSE:
case SOURCE_STYLUS: {
- if (magnificationShortcutExists()
- && Flags.enableMagnificationFollowsMouseBugfix()) {
+ if (magnificationShortcutExists()) {
handleMouseOrStylusEvent(event, rawEvent, policyFlags);
}
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 74a87ed..4441db7 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -5232,7 +5232,7 @@
}
}
return singleCategoryKeyedEntries;
- } catch (IOException e) {
+ } catch (Exception e) {
Slog.e(TAG, "Failed to load generated previews for " + provider, e);
return new SparseArray<>();
}
@@ -5261,7 +5261,7 @@
try {
provider.info.generatedPreviewCategories = readGeneratedPreviewCategoriesFromProto(
input);
- } catch (IOException e) {
+ } catch (Exception e) {
Slog.e(TAG, "Failed to read generated previews from file for " + provider, e);
previewsFile.delete();
provider.info.generatedPreviewCategories = 0;
@@ -5314,7 +5314,7 @@
scheduleNotifyGroupHostsForProvidersChangedLocked(provider.getUserId());
}
}
- } catch (IOException e) {
+ } catch (Exception e) {
if (file != null && stream != null) {
file.failWrite(stream);
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index d2a5734..b6fe0ad 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -49,7 +49,6 @@
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
-import android.annotation.EnforcePermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -2443,6 +2442,7 @@
} catch (Installer.InstallerException e) {
Slog.e(TAG, "Failed unmount mirror data", e);
}
+ extendWatchdogTimeout("#unmount might be slow");
mVold.unmount(vol.getId());
mStorageSessionController.onVolumeUnmount(vol.getImmutableVolumeInfo());
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 07a4d52..8b701f0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -198,6 +198,7 @@
import android.annotation.PermissionMethod;
import android.annotation.PermissionName;
import android.annotation.RequiresPermission;
+import android.annotation.SpecialUsers.CanBeALL;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityClient;
@@ -3921,8 +3922,8 @@
* The pkg name and app id have to be specified.
*/
@Override
- public void killApplication(String pkg, int appId, int userId, String reason,
- int exitInfoReason) {
+ public void killApplication(String pkg, int appId, @CanBeALL @UserIdInt int userId,
+ String reason, int exitInfoReason) {
if (pkg == null) {
return;
}
@@ -4307,7 +4308,7 @@
final boolean forceStopPackageLocked(String packageName, int appId,
boolean callerWillRestart, boolean purgeCache, boolean doit,
boolean evenPersistent, boolean uninstalling, boolean packageStateStopped,
- int userId, String reasonString, int reason) {
+ @CanBeALL @UserIdInt int userId, String reasonString, int reason) {
return forceStopPackageInternalLocked(packageName, appId, callerWillRestart, purgeCache,
doit, evenPersistent, uninstalling, packageStateStopped, userId, reasonString,
reason, ProcessList.INVALID_ADJ);
@@ -4317,7 +4318,7 @@
private boolean forceStopPackageInternalLocked(String packageName, int appId,
boolean callerWillRestart, boolean purgeCache, boolean doit,
boolean evenPersistent, boolean uninstalling, boolean packageStateStopped,
- int userId, String reasonString, int reason, int minOomAdj) {
+ @CanBeALL @UserIdInt int userId, String reasonString, int reason, int minOomAdj) {
int i;
if (userId == UserHandle.USER_ALL && packageName == null) {
@@ -18093,7 +18094,7 @@
}
@Override
- public void killApplicationSync(String pkgName, int appId, int userId,
+ public void killApplicationSync(String pkgName, int appId, @CanBeALL @UserIdInt int userId,
String reason, int exitInfoReason) {
if (pkgName == null) {
return;
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/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index b8babe6..a61368c 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -67,6 +67,8 @@
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SpecialUsers.CanBeALL;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManager.ProcessCapability;
import android.app.ActivityThread;
@@ -2961,8 +2963,8 @@
}
@GuardedBy({"mService", "mProcLock"})
- boolean killPackageProcessesLSP(String packageName, int appId, int userId, int minOomAdj,
- int reasonCode, int subReason, String reason) {
+ boolean killPackageProcessesLSP(String packageName, int appId, @CanBeALL @UserIdInt int userId,
+ int minOomAdj, int reasonCode, int subReason, String reason) {
return killPackageProcessesLSP(packageName, appId, userId, minOomAdj,
false /* callerWillRestart */, true /* allowRestart */, true /* doit */,
false /* evenPersistent */, false /* setRemoved */, false /* uninstalling */,
@@ -2970,7 +2972,8 @@
}
@GuardedBy("mService")
- void killAppZygotesLocked(String packageName, int appId, int userId, boolean force) {
+ void killAppZygotesLocked(String packageName, int appId, @CanBeALL @UserIdInt int userId,
+ boolean force) {
// See if there are any app zygotes running for this packageName / UID combination,
// and kill it if so.
final ArrayList<AppZygote> zygotesToKill = new ArrayList<>();
@@ -3050,9 +3053,9 @@
@GuardedBy({"mService", "mProcLock"})
boolean killPackageProcessesLSP(String packageName, int appId,
- int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
- boolean doit, boolean evenPersistent, boolean setRemoved, boolean uninstalling,
- int reasonCode, int subReason, String reason) {
+ @CanBeALL @UserIdInt int userId, int minOomAdj, boolean callerWillRestart,
+ boolean allowRestart, boolean doit, boolean evenPersistent, boolean setRemoved,
+ boolean uninstalling, int reasonCode, int subReason, String reason) {
final PackageManagerInternal pm = mService.getPackageManagerInternal();
final ArrayList<Pair<ProcessRecord, Boolean>> procs = new ArrayList<>();
@@ -5220,7 +5223,7 @@
}
@GuardedBy("mService")
- void sendPackageBroadcastLocked(int cmd, String[] packages, int userId) {
+ void sendPackageBroadcastLocked(int cmd, String[] packages, @CanBeALL @UserIdInt int userId) {
boolean foundProcess = false;
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord r = mLruProcesses.get(i);
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/UserController.java b/services/core/java/com/android/server/am/UserController.java
index e0fbaf4..18f3500 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -4059,7 +4059,7 @@
synchronized (mUserSwitchingDialogLock) {
dismissUserSwitchingDialog(null);
mUserSwitchingDialog = new UserSwitchingDialog(mService.mContext, fromUser, toUser,
- switchingFromSystemUserMessage, switchingToSystemUserMessage);
+ mHandler, switchingFromSystemUserMessage, switchingToSystemUserMessage);
mUserSwitchingDialog.show(onShown);
}
}
diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java
index d1fcb9d..223e0b7 100644
--- a/services/core/java/com/android/server/am/UserSwitchingDialog.java
+++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java
@@ -34,7 +34,6 @@
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
-import android.os.Looper;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
@@ -72,7 +71,7 @@
// Time to wait for the onAnimationEnd() callbacks before moving on
private static final int ANIMATION_TIMEOUT_MS = 1000;
- private final Handler mHandler = new Handler(Looper.myLooper());
+ private final Handler mHandler;
protected final UserInfo mOldUser;
protected final UserInfo mNewUser;
@@ -81,13 +80,14 @@
protected final Context mContext;
private final int mTraceCookie;
- UserSwitchingDialog(Context context, UserInfo oldUser, UserInfo newUser,
+ UserSwitchingDialog(Context context, UserInfo oldUser, UserInfo newUser, Handler handler,
String switchingFromSystemUserMessage, String switchingToSystemUserMessage) {
super(context, R.style.Theme_Material_NoActionBar_Fullscreen);
mContext = context;
mOldUser = oldUser;
mNewUser = newUser;
+ mHandler = handler;
mSwitchingFromSystemUserMessage = switchingFromSystemUserMessage;
mSwitchingToSystemUserMessage = switchingToSystemUserMessage;
mDisableAnimations = SystemProperties.getBoolean(
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/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d800503..b85967f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -954,7 +954,8 @@
/**
* Stores information about a device using absolute volume behavior.
*/
- private static final class AbsoluteVolumeDeviceInfo {
+ private static final class AbsoluteVolumeDeviceInfo implements IBinder.DeathRecipient {
+ private final AudioService mParent;
private final AudioDeviceAttributes mDevice;
private final List<VolumeInfo> mVolumeInfos;
private final IAudioDeviceVolumeDispatcher mCallback;
@@ -962,16 +963,26 @@
private @AudioManager.AbsoluteDeviceVolumeBehavior int mDeviceVolumeBehavior;
private AbsoluteVolumeDeviceInfo(
+ AudioService parent,
AudioDeviceAttributes device,
List<VolumeInfo> volumeInfos,
IAudioDeviceVolumeDispatcher callback,
boolean handlesVolumeAdjustment,
@AudioManager.AbsoluteDeviceVolumeBehavior int behavior) {
+ this.mParent = parent;
this.mDevice = device;
this.mVolumeInfos = volumeInfos;
this.mCallback = callback;
this.mHandlesVolumeAdjustment = handlesVolumeAdjustment;
this.mDeviceVolumeBehavior = behavior;
+
+ try {
+ this.mCallback.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException | NullPointerException e) {
+ // NPE can be raised when mocking the callback object
+ Slog.w(TAG, "Exception: " + e
+ + "\nCannot listen to callback binder death for device " + mDevice);
+ }
}
/**
@@ -991,6 +1002,25 @@
}
return null;
}
+
+ @Override
+ public void binderDied() {
+ if (mParent.removeAudioSystemDeviceOutFromAbsVolumeDevices(mDevice.getInternalType())
+ != null) {
+ mParent.dispatchDeviceVolumeBehavior(mDevice,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE);
+ }
+ }
+
+ public void unlinkToDeath() {
+ try {
+ mCallback.asBinder().unlinkToDeath(this, 0);
+ } catch (NullPointerException e) {
+ // NPE can be raised when mocking the callback object
+ Slog.w(TAG, "Exception: " + e
+ + "\nCannot unlink to death, null binder object for device " + mDevice);
+ }
+ }
}
// Devices for the which use the "absolute volume" concept (framework sends audio signal
@@ -8142,16 +8172,16 @@
int deviceOut = device.getInternalType();
if (register) {
- AbsoluteVolumeDeviceInfo info = new AbsoluteVolumeDeviceInfo(
+ AbsoluteVolumeDeviceInfo info = new AbsoluteVolumeDeviceInfo(this,
device, volumes, cb, handlesVolumeAdjustment, deviceVolumeBehavior);
final AbsoluteVolumeDeviceInfo oldInfo = getAbsoluteVolumeDeviceInfo(deviceOut);
+ addAudioSystemDeviceOutToAbsVolumeDevices(deviceOut, info);
boolean volumeBehaviorChanged = (oldInfo == null)
|| (oldInfo.mDeviceVolumeBehavior != deviceVolumeBehavior);
if (volumeBehaviorChanged) {
removeAudioSystemDeviceOutFromFullVolumeDevices(deviceOut);
removeAudioSystemDeviceOutFromFixedVolumeDevices(deviceOut);
- addAudioSystemDeviceOutToAbsVolumeDevices(deviceOut, info);
dispatchDeviceVolumeBehavior(device, deviceVolumeBehavior);
}
@@ -8177,8 +8207,10 @@
}
}
} else {
- boolean wasAbsVol = removeAudioSystemDeviceOutFromAbsVolumeDevices(deviceOut) != null;
- if (wasAbsVol) {
+ AbsoluteVolumeDeviceInfo deviceInfo = removeAudioSystemDeviceOutFromAbsVolumeDevices(
+ deviceOut);
+ if (deviceInfo != null) {
+ deviceInfo.unlinkToDeath();
dispatchDeviceVolumeBehavior(device, AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE);
}
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index a5058dd..cf5fa96 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -540,9 +540,23 @@
DEFAULT_MANDATORY_BIOMETRICS_STATUS)
&& mMandatoryBiometricsRequirementsSatisfied.getOrDefault(userId,
DEFAULT_MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED_STATUS)
- && getEnabledForApps(userId, TYPE_ANY_BIOMETRIC)
- && (mFingerprintEnrolledForUser.getOrDefault(userId, false /* default */)
- || mFaceEnrolledForUser.getOrDefault(userId, false /* default */));
+ && getBiometricStatusForIdentityCheck(userId);
+ }
+
+ private boolean getBiometricStatusForIdentityCheck(int userId) {
+ if (com.android.settings.flags.Flags.biometricsOnboardingEducation()) {
+ if (mFingerprintEnrolledForUser.getOrDefault(userId, false /* default */)
+ && getEnabledForApps(userId, TYPE_FINGERPRINT)) {
+ return true;
+ } else {
+ return mFaceEnrolledForUser.getOrDefault(userId, false /* default */)
+ && getEnabledForApps(userId, TYPE_FACE);
+ }
+ } else {
+ return (mFingerprintEnrolledForUser.getOrDefault(userId, false /* default */)
+ || mFaceEnrolledForUser.getOrDefault(userId, false /* default */))
+ && getEnabledForApps(userId, TYPE_ANY_BIOMETRIC);
+ }
}
void notifyEnabledOnKeyguardCallbacks(int userId, int modality) {
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index c393e92..79af6ed 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -24,6 +24,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SpecialUsers.CanBeALL;
+import android.annotation.SpecialUsers.CanBeCURRENT;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManager.RestrictionLevel;
@@ -361,7 +363,8 @@
*/
@Override
public void registerContentObserver(Uri uri, boolean notifyForDescendants,
- IContentObserver observer, int userHandle, int targetSdkVersion) {
+ IContentObserver observer, @CanBeALL @CanBeCURRENT @UserIdInt int userHandle,
+ int targetSdkVersion) {
if (observer == null || uri == null) {
throw new IllegalArgumentException("You must pass a valid uri and observer");
}
@@ -1398,8 +1401,8 @@
}
}
- private int handleIncomingUser(Uri uri, int pid, int uid, int modeFlags, boolean allowNonFull,
- int userId) {
+ private @CanBeALL @UserIdInt int handleIncomingUser(Uri uri, int pid, int uid, int modeFlags,
+ boolean allowNonFull, @CanBeALL @CanBeCURRENT @UserIdInt int userId) {
if (userId == UserHandle.USER_CURRENT) {
userId = ActivityManager.getCurrentUser();
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 872f334..f4daf87 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -890,10 +890,12 @@
// still need to let WindowManager know so it can update its own internal state for
// things like display cutouts.
display.getNonOverrideDisplayInfoLocked(mTempDisplayInfo);
- if (!mTempNonOverrideDisplayInfo.equals(mTempDisplayInfo)) {
- logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_BASIC_CHANGED
- | LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED;
+ if (!mTempNonOverrideDisplayInfo.equals(mTempDisplayInfo,
+ /* compareOnlyBasicChanges */ true)) {
+ logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_BASIC_CHANGED;
}
+ logicalDisplayEventMask
+ |= updateAndGetMaskForDisplayPropertyChanges(mTempNonOverrideDisplayInfo);
}
mLogicalDisplaysToUpdate.put(displayId, logicalDisplayEventMask);
mUpdatedLogicalDisplays.put(displayId, UPDATE_STATE_UPDATED);
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index c3057de..7cc178d 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -280,6 +280,11 @@
Flags::committedStateSeparateEvent
);
+ private final FlagState mDelayImplicitRrRegistrationUntilRrAccessed = new FlagState(
+ Flags.FLAG_DELAY_IMPLICIT_RR_REGISTRATION_UNTIL_RR_ACCESSED,
+ Flags::delayImplicitRrRegistrationUntilRrAccessed
+ );
+
/**
* @return {@code true} if 'port' is allowed in display layout configuration file.
*/
@@ -586,7 +591,6 @@
return mFramerateOverrideTriggersRrCallbacks.isEnabled();
}
-
/**
* @return {@code true} if the flag for sending refresh rate events only for the apps in
* foreground is enabled
@@ -604,6 +608,13 @@
}
/**
+ * @return {@code true} if the flag for only explicit subscription for RR changes is enabled
+ */
+ public boolean isDelayImplicitRrRegistrationUntilRrAccessedEnabled() {
+ return mDelayImplicitRrRegistrationUntilRrAccessed.isEnabled();
+ }
+
+ /**
* dumps all flagstates
* @param pw printWriter
*/
@@ -660,6 +671,7 @@
pw.println(" " + mFramerateOverrideTriggersRrCallbacks);
pw.println(" " + mRefreshRateEventForForegroundApps);
pw.println(" " + mCommittedStateSeparateEvent);
+ pw.println(" " + mDelayImplicitRrRegistrationUntilRrAccessed);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index f545130..a0064a9 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -474,9 +474,9 @@
description: "Feature flag to trigger the RR callbacks when framerate overridding happens."
bug: "390113266"
is_fixed_read_only: true
- metadata {
- purpose: PURPOSE_BUGFIX
- }
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
@@ -508,3 +508,14 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "delay_implicit_rr_registration_until_rr_accessed"
+ namespace: "display_manager"
+ description: "Feature flag for clients to subscribe to RR changes by either explicitly subscribing for refresh rate changes or request for refresh rate data"
+ bug: "391828526"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/core/java/com/android/server/inputmethod/ClientController.java b/services/core/java/com/android/server/inputmethod/ClientController.java
index 0381a31..d03c086 100644
--- a/services/core/java/com/android/server/inputmethod/ClientController.java
+++ b/services/core/java/com/android/server/inputmethod/ClientController.java
@@ -43,21 +43,23 @@
@GuardedBy("ImfLock.class")
private final List<ClientControllerCallback> mCallbacks = new ArrayList<>();
+ @NonNull
private final PackageManagerInternal mPackageManagerInternal;
interface ClientControllerCallback {
- void onClientRemoved(ClientState client);
+ void onClientRemoved(@NonNull ClientState client);
}
- ClientController(PackageManagerInternal packageManagerInternal) {
+ ClientController(@NonNull PackageManagerInternal packageManagerInternal) {
mPackageManagerInternal = packageManagerInternal;
}
@GuardedBy("ImfLock.class")
- ClientState addClient(IInputMethodClientInvoker clientInvoker,
- IRemoteInputConnection inputConnection, int selfReportedDisplayId, int callerUid,
- int callerPid) {
+ @NonNull
+ ClientState addClient(@NonNull IInputMethodClientInvoker clientInvoker,
+ @NonNull IRemoteInputConnection fallbackInputConnection, int selfReportedDisplayId,
+ int callerUid, int callerPid) {
final IBinder.DeathRecipient deathRecipient = () -> {
// Exceptionally holding ImfLock here since this is a internal lambda expression.
synchronized (ImfLock.class) {
@@ -90,15 +92,15 @@
// have the client crash. Thus we do not verify the display ID at all here. Instead we
// later check the display ID every time the client needs to interact with the specified
// display.
- final ClientState cs = new ClientState(clientInvoker, inputConnection,
- callerUid, callerPid, selfReportedDisplayId, deathRecipient);
+ final var cs = new ClientState(clientInvoker, fallbackInputConnection, callerUid, callerPid,
+ selfReportedDisplayId, deathRecipient);
mClients.put(clientInvoker.asBinder(), cs);
return cs;
}
@VisibleForTesting
@GuardedBy("ImfLock.class")
- boolean removeClient(IInputMethodClient client) {
+ boolean removeClient(@NonNull IInputMethodClient client) {
return removeClientAsBinder(client.asBinder());
}
@@ -116,7 +118,7 @@
}
@GuardedBy("ImfLock.class")
- void addClientControllerCallback(ClientControllerCallback callback) {
+ void addClientControllerCallback(@NonNull ClientControllerCallback callback) {
mCallbacks.add(callback);
}
@@ -127,15 +129,15 @@
}
@GuardedBy("ImfLock.class")
- void forAllClients(Consumer<ClientState> consumer) {
+ void forAllClients(@NonNull Consumer<ClientState> consumer) {
for (int i = 0; i < mClients.size(); i++) {
consumer.accept(mClients.valueAt(i));
}
}
@GuardedBy("ImfLock.class")
- boolean verifyClientAndPackageMatch(
- @NonNull IInputMethodClient client, @NonNull String packageName) {
+ boolean verifyClientAndPackageMatch(@NonNull IInputMethodClient client,
+ @NonNull String packageName) {
final ClientState cs = mClients.get(client.asBinder());
if (cs == null) {
throw new IllegalArgumentException("unknown client " + client.asBinder());
diff --git a/services/core/java/com/android/server/inputmethod/ClientState.java b/services/core/java/com/android/server/inputmethod/ClientState.java
index e98a5a7..ea80995 100644
--- a/services/core/java/com/android/server/inputmethod/ClientState.java
+++ b/services/core/java/com/android/server/inputmethod/ClientState.java
@@ -16,6 +16,8 @@
package com.android.server.inputmethod;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.IBinder;
import android.util.SparseArray;
import android.view.inputmethod.InputBinding;
@@ -24,12 +26,17 @@
import com.android.internal.inputmethod.IRemoteInputConnection;
final class ClientState {
+
+ @NonNull
final IInputMethodClientInvoker mClient;
+ @NonNull
final IRemoteInputConnection mFallbackInputConnection;
final int mUid;
final int mPid;
final int mSelfReportedDisplayId;
+ @NonNull
final InputBinding mBinding;
+ @NonNull
final IBinder.DeathRecipient mClientDeathRecipient;
@GuardedBy("ImfLock.class")
@@ -39,30 +46,31 @@
boolean mSessionRequestedForAccessibility;
@GuardedBy("ImfLock.class")
+ @Nullable
InputMethodManagerService.SessionState mCurSession;
@GuardedBy("ImfLock.class")
- SparseArray<InputMethodManagerService.AccessibilitySessionState> mAccessibilitySessions =
+ @NonNull
+ final SparseArray<InputMethodManagerService.AccessibilitySessionState> mAccessibilitySessions =
new SparseArray<>();
- @Override
- public String toString() {
- return "ClientState{" + Integer.toHexString(
- System.identityHashCode(this)) + " mUid=" + mUid
- + " mPid=" + mPid + " mSelfReportedDisplayId=" + mSelfReportedDisplayId + "}";
- }
-
- ClientState(IInputMethodClientInvoker client,
- IRemoteInputConnection fallbackInputConnection,
- int uid, int pid, int selfReportedDisplayId,
- IBinder.DeathRecipient clientDeathRecipient) {
+ ClientState(@NonNull IInputMethodClientInvoker client,
+ @NonNull IRemoteInputConnection fallbackInputConnection, int uid, int pid,
+ int selfReportedDisplayId, @NonNull IBinder.DeathRecipient clientDeathRecipient) {
mClient = client;
mFallbackInputConnection = fallbackInputConnection;
mUid = uid;
mPid = pid;
mSelfReportedDisplayId = selfReportedDisplayId;
- mBinding = new InputBinding(null /*conn*/, mFallbackInputConnection.asBinder(), mUid,
+ mBinding = new InputBinding(null /* conn */, fallbackInputConnection.asBinder(), mUid,
mPid);
mClientDeathRecipient = clientDeathRecipient;
}
+
+ @Override
+ public String toString() {
+ return "ClientState{" + Integer.toHexString(System.identityHashCode(this))
+ + " mUid=" + mUid + " mPid=" + mPid
+ + " mSelfReportedDisplayId=" + mSelfReportedDisplayId + "}";
+ }
}
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java
index 650ea60..9d88983 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java
@@ -58,23 +58,17 @@
private final Handler mHandler;
@AnyThread
- @Nullable
- static IInputMethodClientInvoker create(@Nullable IInputMethodClient inputMethodClient,
+ @NonNull
+ static IInputMethodClientInvoker create(@NonNull IInputMethodClient inputMethodClient,
@NonNull Handler handler) {
- if (inputMethodClient == null) {
- return null;
- }
final boolean isProxy = Binder.isProxy(inputMethodClient);
return new IInputMethodClientInvoker(inputMethodClient, isProxy, isProxy ? null : handler);
}
@AnyThread
- @Nullable
- static IInputMethodClientInvoker create$ravenwood(
- @Nullable IInputMethodClient inputMethodClient, @NonNull Handler handler) {
- if (inputMethodClient == null) {
- return null;
- }
+ @NonNull
+ static IInputMethodClientInvoker create$ravenwood(@NonNull IInputMethodClient inputMethodClient,
+ @NonNull Handler handler) {
return new IInputMethodClientInvoker(inputMethodClient, true, null);
}
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java b/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
index a8b61af..02987a9 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
@@ -89,8 +89,8 @@
@BinderThread
interface Callback {
- void addClient(IInputMethodClient client, IRemoteInputConnection inputConnection,
- int selfReportedDisplayId);
+ void addClient(@NonNull IInputMethodClient client,
+ @NonNull IRemoteInputConnection inputConnection, int selfReportedDisplayId);
InputMethodInfo getCurrentInputMethodInfoAsUser(@UserIdInt int userId);
@@ -242,9 +242,9 @@
}
@Override
- public void addClient(IInputMethodClient client, IRemoteInputConnection inputmethod,
- int untrustedDisplayId) {
- mCallback.addClient(client, inputmethod, untrustedDisplayId);
+ public void addClient(@NonNull IInputMethodClient client,
+ @NonNull IRemoteInputConnection fallbackInputConnection, int untrustedDisplayId) {
+ mCallback.addClient(client, fallbackInputConnection, untrustedDisplayId);
}
@Override
@@ -414,7 +414,7 @@
}
@Override
- public void reportPerceptibleAsync(IBinder windowToken, boolean perceptible) {
+ public void reportPerceptibleAsync(@NonNull IBinder windowToken, boolean perceptible) {
mCallback.reportPerceptibleAsync(windowToken, perceptible);
}
diff --git a/services/core/java/com/android/server/inputmethod/ImeBindingState.java b/services/core/java/com/android/server/inputmethod/ImeBindingState.java
index 5deed39..adfd991 100644
--- a/services/core/java/com/android/server/inputmethod/ImeBindingState.java
+++ b/services/core/java/com/android/server/inputmethod/ImeBindingState.java
@@ -43,9 +43,9 @@
final int mUserId;
/**
- * The last window token that we confirmed to be focused. This is always updated upon
- * reports from the input method client. If the window state is already changed before the
- * report is handled, this field just keeps the last value.
+ * The last window token that we confirmed to be focused. This is always updated upon reports
+ * from the input method client. If the window state is already changed before the report is
+ * handled, this field just keeps the last value.
*/
@Nullable
final IBinder mFocusedWindow;
@@ -59,11 +59,9 @@
final int mFocusedWindowSoftInputMode;
/**
- * The client by which {@link #mFocusedWindow} was reported. This gets updated whenever
- * an
- * IME-focusable window gained focus (without necessarily starting an input connection),
- * while {@link InputMethodManagerService#mClient} only gets updated when we actually start an
- * input connection.
+ * The client by which {@link #mFocusedWindow} was reported. This gets updated whenever an
+ * IME-focusable window gained focus (without necessarily starting an input connection), while
+ * {@link UserData#mCurClient} only gets updated when we actually start an input connection.
*
* @see #mFocusedWindow
*/
@@ -72,15 +70,16 @@
/**
* The editor info by which {@link #mFocusedWindow} was reported. This differs from
- * {@link InputMethodManagerService#mCurEditorInfo} the same way {@link #mFocusedWindowClient}
- * differs from {@link InputMethodManagerService#mCurClient}.
+ * {@link UserData#mCurEditorInfo} the same way {@link #mFocusedWindowClient} differs from
+ * {@link UserData#mCurClient}.
*
* @see #mFocusedWindow
*/
@Nullable
final EditorInfo mFocusedWindowEditorInfo;
- void dumpDebug(ProtoOutputStream proto, WindowManagerInternal windowManagerInternal) {
+ void dumpDebug(@NonNull ProtoOutputStream proto,
+ @NonNull WindowManagerInternal windowManagerInternal) {
proto.write(CUR_FOCUSED_WINDOW_NAME,
windowManagerInternal.getWindowName(mFocusedWindow));
proto.write(CUR_FOCUSED_WINDOW_SOFT_INPUT_MODE,
@@ -94,18 +93,14 @@
p.println(prefix + "mFocusedWindowClient=" + mFocusedWindowClient);
}
+ @NonNull
static ImeBindingState newEmptyState() {
- return new ImeBindingState(
- /*userId=*/ UserHandle.USER_NULL,
- /*focusedWindow=*/ null,
- /*focusedWindowSoftInputMode=*/ SOFT_INPUT_STATE_UNSPECIFIED,
- /*focusedWindowClient=*/ null,
- /*focusedWindowEditorInfo=*/ null
- );
+ return new ImeBindingState(UserHandle.USER_NULL /* userId */, null /* focusedWindow */,
+ SOFT_INPUT_STATE_UNSPECIFIED /* focusedWindowSoftInputMode */,
+ null /* focusedWindowClient */, null /* focusedWindowEditorInfo */);
}
- ImeBindingState(@UserIdInt int userId,
- @Nullable IBinder focusedWindow,
+ ImeBindingState(@UserIdInt int userId, @Nullable IBinder focusedWindow,
@SoftInputModeFlags int focusedWindowSoftInputMode,
@Nullable ClientState focusedWindowClient,
@Nullable EditorInfo focusedWindowEditorInfo) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index dfdd9e5..395d85e 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -482,10 +482,15 @@
};
static class SessionState {
+
+ @NonNull
final ClientState mClient;
+ @NonNull
final IInputMethodInvoker mMethod;
+ @Nullable
IInputMethodSession mSession;
+ @Nullable
InputChannel mChannel;
@UserIdInt
@@ -496,14 +501,13 @@
return "SessionState{uid=" + mClient.mUid + " pid=" + mClient.mPid
+ " method=" + Integer.toHexString(
IInputMethodInvoker.getBinderIdentityHashCode(mMethod))
- + " session=" + Integer.toHexString(
- System.identityHashCode(mSession))
+ + " session=" + Integer.toHexString(System.identityHashCode(mSession))
+ " channel=" + mChannel
+ " userId=" + mUserId
+ "}";
}
- SessionState(ClientState client, IInputMethodInvoker method,
+ SessionState(@NonNull ClientState client, @NonNull IInputMethodInvoker method,
IInputMethodSession session, InputChannel channel, @UserIdInt int userId) {
mClient = client;
mMethod = method;
@@ -517,22 +521,24 @@
* Record session state for an accessibility service.
*/
static class AccessibilitySessionState {
+
+ @NonNull
final ClientState mClient;
// Id of the accessibility service.
final int mId;
- public IAccessibilityInputMethodSession mSession;
+ @Nullable
+ IAccessibilityInputMethodSession mSession;
@Override
public String toString() {
return "AccessibilitySessionState{uid=" + mClient.mUid + " pid=" + mClient.mPid
+ " id=" + Integer.toHexString(mId)
- + " session=" + Integer.toHexString(
- System.identityHashCode(mSession))
+ + " session=" + Integer.toHexString(System.identityHashCode(mSession))
+ "}";
}
- AccessibilitySessionState(ClientState client, int id,
+ AccessibilitySessionState(@NonNull ClientState client, int id,
IAccessibilityInputMethodSession session) {
mClient = client;
mId = id;
@@ -544,6 +550,7 @@
* Manages the IME clients.
*/
@SharedByAllUsersField
+ @NonNull
private final ClientController mClientController;
/**
@@ -1770,19 +1777,21 @@
* <p>As a general principle, IPCs from the application process that take
* {@link IInputMethodClient} will be rejected without this step.</p>
*
- * @param client {@link android.os.Binder} proxy that is associated with the
- * singleton instance of
- * {@link android.view.inputmethod.InputMethodManager} that runs
- * on the client process
- * @param inputConnection communication channel for the fallback {@link InputConnection}
- * @param selfReportedDisplayId self-reported display ID to which the client is associated.
- * Whether the client is still allowed to access to this display
- * or not needs to be evaluated every time the client interacts
- * with the display
+ * @param client {@link android.os.Binder} proxy that is associated with the
+ * singleton instance of
+ * {@link android.view.inputmethod.InputMethodManager} that runs
+ * on the client process
+ * @param fallbackInputConnection communication channel for the fallback {@link InputConnection}
+ * @param selfReportedDisplayId self-reported display ID to which the client is associated.
+ * Whether the client is still allowed to access to this display
+ * or not needs to be evaluated every time the client interacts
+ * with the display
*/
@Override
- public void addClient(IInputMethodClient client, IRemoteInputConnection inputConnection,
- int selfReportedDisplayId) {
+ public void addClient(@NonNull IInputMethodClient client,
+ @NonNull IRemoteInputConnection fallbackInputConnection, int selfReportedDisplayId) {
+ Objects.requireNonNull(client, "client must not be null");
+ Objects.requireNonNull(fallbackInputConnection, "fallbackInputConnection must not be null");
// Here there are two scenarios where this method is called:
// A. IMM is being instantiated in a different process and this is an IPC from that process
// B. IMM is being instantiated in the same process but Binder.clearCallingIdentity() is
@@ -1791,16 +1800,15 @@
// actually running.
final int callerUid = Binder.getCallingUid();
final int callerPid = Binder.getCallingPid();
- final IInputMethodClientInvoker clientInvoker =
- IInputMethodClientInvoker.create(client, mHandler);
+ final var clientInvoker = IInputMethodClientInvoker.create(client, mHandler);
synchronized (ImfLock.class) {
- mClientController.addClient(clientInvoker, inputConnection, selfReportedDisplayId,
- callerUid, callerPid);
+ mClientController.addClient(clientInvoker, fallbackInputConnection,
+ selfReportedDisplayId, callerUid, callerPid);
}
}
@GuardedBy("ImfLock.class")
- private void onClientRemoved(ClientState client) {
+ private void onClientRemoved(@NonNull ClientState client) {
clearClientSessionLocked(client);
clearClientSessionForAccessibilityLocked(client);
// TODO(b/324907325): Remove the suppress warnings once b/324907325 is fixed.
@@ -1814,7 +1822,8 @@
*/
// TODO(b/325515685): Move this method to InputMethodBindingController
@GuardedBy("ImfLock.class")
- private void onClientRemovedInternalLocked(ClientState client, @NonNull UserData userData) {
+ private void onClientRemovedInternalLocked(@NonNull ClientState client,
+ @NonNull UserData userData) {
final int userId = userData.mUserId;
if (userData.mCurClient == client) {
hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, 0 /* flags */,
@@ -1962,7 +1971,7 @@
session.mMethod.startInput(startInputToken, userData.mCurInputConnection,
userData.mCurEditorInfo, restarting, navButtonFlags, userData.mCurImeDispatcher);
if (Flags.refactorInsetsController()) {
- if (isShowRequestedForCurrentWindow(userId) && userData.mImeBindingState != null
+ if (isShowRequestedForCurrentWindow(userId)
&& userData.mImeBindingState.mFocusedWindow != null) {
// Re-use current statsToken, if it exists.
final var statsToken = userData.mCurStatsToken != null ? userData.mCurStatsToken
@@ -2028,15 +2037,14 @@
}
}
+ @NonNull
private SparseArray<IAccessibilityInputMethodSession> createAccessibilityInputMethodSessions(
- SparseArray<AccessibilitySessionState> accessibilitySessions) {
- final SparseArray<IAccessibilityInputMethodSession> accessibilityInputMethodSessions =
- new SparseArray<>();
- if (accessibilitySessions != null) {
- for (int i = 0; i < accessibilitySessions.size(); i++) {
- accessibilityInputMethodSessions.append(accessibilitySessions.keyAt(i),
- accessibilitySessions.valueAt(i).mSession);
- }
+ @NonNull SparseArray<AccessibilitySessionState> accessibilitySessions) {
+ final var accessibilityInputMethodSessions =
+ new SparseArray<IAccessibilityInputMethodSession>();
+ for (int i = 0; i < accessibilitySessions.size(); i++) {
+ accessibilityInputMethodSessions.append(accessibilitySessions.keyAt(i),
+ accessibilitySessions.valueAt(i).mSession);
}
return accessibilityInputMethodSessions;
}
@@ -2547,14 +2555,14 @@
}
@GuardedBy("ImfLock.class")
- void clearClientSessionLocked(ClientState cs) {
+ void clearClientSessionLocked(@NonNull ClientState cs) {
finishSessionLocked(cs.mCurSession);
cs.mCurSession = null;
cs.mSessionRequested = false;
}
@GuardedBy("ImfLock.class")
- void clearClientSessionForAccessibilityLocked(ClientState cs) {
+ void clearClientSessionForAccessibilityLocked(@NonNull ClientState cs) {
for (int i = 0; i < cs.mAccessibilitySessions.size(); i++) {
finishSessionForAccessibilityLocked(cs.mAccessibilitySessions.valueAt(i));
}
@@ -2563,7 +2571,7 @@
}
@GuardedBy("ImfLock.class")
- void clearClientSessionForAccessibilityLocked(ClientState cs, int id) {
+ void clearClientSessionForAccessibilityLocked(@NonNull ClientState cs, int id) {
AccessibilitySessionState session = cs.mAccessibilitySessions.get(id);
if (session != null) {
finishSessionForAccessibilityLocked(session);
@@ -2572,7 +2580,7 @@
}
@GuardedBy("ImfLock.class")
- private void finishSessionLocked(SessionState sessionState) {
+ private void finishSessionLocked(@Nullable SessionState sessionState) {
if (sessionState != null) {
if (sessionState.mSession != null) {
try {
@@ -2885,8 +2893,7 @@
ProtoLog.v(IMMS_DEBUG, "IME window vis: %s active: %s visible: %s displayId: %s", vis,
(vis & InputMethodService.IME_ACTIVE), (vis & InputMethodService.IME_VISIBLE),
curTokenDisplayId);
- final IBinder focusedWindowToken = userData.mImeBindingState != null
- ? userData.mImeBindingState.mFocusedWindow : null;
+ final IBinder focusedWindowToken = userData.mImeBindingState.mFocusedWindow;
final Boolean windowPerceptible = focusedWindowToken != null
? mFocusedWindowPerceptible.get(focusedWindowToken) : null;
@@ -3474,7 +3481,7 @@
@BinderThread
@Override
- public void reportPerceptibleAsync(IBinder windowToken, boolean perceptible) {
+ public void reportPerceptibleAsync(@NonNull IBinder windowToken, boolean perceptible) {
Binder.withCleanCallingIdentity(() -> {
Objects.requireNonNull(windowToken, "windowToken must not be null");
synchronized (ImfLock.class) {
@@ -4868,7 +4875,7 @@
@GuardedBy("ImfLock.class")
void setEnabledSessionForAccessibilityLocked(
- SparseArray<AccessibilitySessionState> accessibilitySessions,
+ @NonNull SparseArray<AccessibilitySessionState> accessibilitySessions,
@NonNull UserData userData) {
// mEnabledAccessibilitySessions could the same object as accessibilitySessions.
SparseArray<IAccessibilityInputMethodSession> disabledSessions = new SparseArray<>();
@@ -5156,7 +5163,7 @@
interactive ? bindingController.getImeWindowVis() : 0,
bindingController.getBackDisposition(), userId);
// Inform the current client of the change in active status
- if (userData.mCurClient == null || userData.mCurClient.mClient == null) {
+ if (userData.mCurClient == null) {
return;
}
if (mImePlatformCompatUtils.shouldUseSetInteractiveProtocol(
@@ -5920,9 +5927,8 @@
// from all clients.
if (bindingController.getCurMethod() != null) {
// TODO(b/324907325): Remove the suppress warnings once b/324907325 is fixed.
- @SuppressWarnings("GuardedBy") Consumer<ClientState> clearClientSession =
- c -> clearClientSessionForAccessibilityLocked(c,
- accessibilityConnectionId);
+ @SuppressWarnings("GuardedBy") Consumer<ClientState> clearClientSession = c ->
+ clearClientSessionForAccessibilityLocked(c, accessibilityConnectionId);
mClientController.forAllClients(clearClientSession);
AccessibilitySessionState session = userData.mEnabledAccessibilitySessions.get(
@@ -5998,7 +6004,7 @@
@BinderThread
@GuardedBy("ImfLock.class")
private void reportFullscreenModeLocked(boolean fullscreen, @NonNull UserData userData) {
- if (userData.mCurClient != null && userData.mCurClient.mClient != null) {
+ if (userData.mCurClient != null) {
userData.mInFullscreenMode = fullscreen;
userData.mCurClient.mClient.reportFullscreenMode(fullscreen);
}
@@ -6732,9 +6738,7 @@
boolean setImeVisibilityOnFocusedWindowClient(boolean visible, UserData userData,
@NonNull ImeTracker.Token statsToken) {
if (Flags.refactorInsetsController()) {
- if (userData.mImeBindingState != null
- && userData.mImeBindingState.mFocusedWindowClient != null
- && userData.mImeBindingState.mFocusedWindowClient.mClient != null) {
+ if (userData.mImeBindingState.mFocusedWindowClient != null) {
userData.mImeBindingState.mFocusedWindowClient.mClient.setImeVisibility(visible,
statsToken);
return true;
diff --git a/services/core/java/com/android/server/inputmethod/UserData.java b/services/core/java/com/android/server/inputmethod/UserData.java
index 96da17e..7377908a 100644
--- a/services/core/java/com/android/server/inputmethod/UserData.java
+++ b/services/core/java/com/android/server/inputmethod/UserData.java
@@ -140,9 +140,9 @@
InputMethodManagerService.SessionState mEnabledSession;
@GuardedBy("ImfLock.class")
- @Nullable
- SparseArray<InputMethodManagerService.AccessibilitySessionState>
- mEnabledAccessibilitySessions = new SparseArray<>();
+ @NonNull
+ SparseArray<InputMethodManagerService.AccessibilitySessionState> mEnabledAccessibilitySessions =
+ new SparseArray<>();
/**
* A per-user cache of {@link InputMethodSettings#getEnabledInputMethodsStr()}.
diff --git a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
index b863c96..7252925 100644
--- a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
+++ b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
@@ -128,9 +128,9 @@
}
@Override
- public void addClient(IInputMethodClient client, IRemoteInputConnection inputConnection,
- int selfReportedDisplayId) {
- offload(() -> mInner.addClient(client, inputConnection, selfReportedDisplayId));
+ public void addClient(@NonNull IInputMethodClient client,
+ @NonNull IRemoteInputConnection fallbackInputConnection, int selfReportedDisplayId) {
+ offload(() -> mInner.addClient(client, fallbackInputConnection, selfReportedDisplayId));
}
@Override
@@ -331,7 +331,7 @@
}
@Override
- public void reportPerceptibleAsync(IBinder windowToken, boolean perceptible) {
+ public void reportPerceptibleAsync(@NonNull IBinder windowToken, boolean perceptible) {
// Already async TODO(b/293640003): ordering issues?
mInner.reportPerceptibleAsync(windowToken, perceptible);
}
@@ -468,7 +468,7 @@
IInputMethodClient client, InputBindResult res, int startInputSeq) {
synchronized (ImfLock.class) {
final ClientState cs = mInner.getClientStateLocked(client);
- if (cs != null && cs.mClient != null) {
+ if (cs != null) {
cs.mClient.onStartInputResult(res, startInputSeq);
} else {
// client is unbound.
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
index 1a29150..940bcb4 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
@@ -16,6 +16,7 @@
package com.android.server.location.contexthub;
+import android.annotation.NonNull;
import android.app.AppOpsManager;
import android.content.Context;
import android.hardware.contexthub.EndpointInfo;
@@ -64,7 +65,7 @@
* Internal interface used to invoke client callbacks.
*/
interface CallbackConsumer {
- void accept(IContextHubEndpointCallback callback) throws RemoteException;
+ void accept(@NonNull IContextHubEndpointCallback callback) throws RemoteException;
}
/** The context of the service. */
@@ -86,7 +87,7 @@
private final EndpointInfo mHalEndpointInfo;
/** The remote callback interface for this endpoint. */
- private final IContextHubEndpointCallback mContextHubEndpointCallback;
+ @NonNull private final IContextHubEndpointCallback mContextHubEndpointCallback;
/** True if this endpoint is registered with the service/HAL. */
@GuardedBy("mRegistrationLock")
@@ -158,7 +159,7 @@
IEndpointCommunication hubInterface,
ContextHubEndpointManager endpointManager,
EndpointInfo halEndpointInfo,
- IContextHubEndpointCallback callback,
+ @NonNull IContextHubEndpointCallback callback,
String packageName,
String attributionTag,
ContextHubTransactionManager transactionManager) {
@@ -419,9 +420,7 @@
}
/* package */ void attachDeathRecipient() throws RemoteException {
- if (mContextHubEndpointCallback != null) {
- mContextHubEndpointCallback.asBinder().linkToDeath(this, 0 /* flags */);
- }
+ mContextHubEndpointCallback.asBinder().linkToDeath(this, 0 /* flags */);
}
/* package */ void onEndpointSessionOpenRequest(
@@ -664,15 +663,13 @@
* @return false if the callback threw a RemoteException
*/
private boolean invokeCallback(CallbackConsumer consumer) {
- if (mContextHubEndpointCallback != null) {
- acquireWakeLock();
- try {
- consumer.accept(mContextHubEndpointCallback);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException while calling endpoint callback", e);
- releaseWakeLock();
- return false;
- }
+ acquireWakeLock();
+ try {
+ consumer.accept(mContextHubEndpointCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling endpoint callback", e);
+ releaseWakeLock();
+ return false;
}
return true;
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
index 30bb8f3..8ab581e 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
@@ -17,6 +17,7 @@
package com.android.server.location.contexthub;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.content.Context;
import android.hardware.contexthub.ContextHubInfo;
import android.hardware.contexthub.EndpointInfo;
@@ -240,7 +241,7 @@
*/
/* package */ IContextHubEndpoint registerEndpoint(
HubEndpointInfo pendingEndpointInfo,
- IContextHubEndpointCallback callback,
+ @NonNull IContextHubEndpointCallback callback,
String packageName,
String attributionTag)
throws RemoteException {
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index bf7351c..2c0c55b 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -792,6 +792,10 @@
Log.e(TAG, "Endpoint manager failed to initialize");
throw new UnsupportedOperationException("Endpoint registration is not supported");
}
+ if (callback == null) {
+ Log.e(TAG, "Endpoint callback is invalid");
+ throw new IllegalArgumentException("registerEndpoint must have a non-null callback");
+ }
return mEndpointManager.registerEndpoint(
pendingHubEndpointInfo, callback, packageName, attributionTag);
}
diff --git a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
index 177eefb..3f75b11 100644
--- a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
@@ -29,7 +29,6 @@
import android.util.Log;
import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.flags.Flags;
import com.android.server.FgThread;
import java.util.Objects;
@@ -107,26 +106,19 @@
boolean isInExtensionTime = mEmergencyCallEndRealtimeMs != Long.MIN_VALUE
&& (SystemClock.elapsedRealtime() - mEmergencyCallEndRealtimeMs) < extensionTimeMs;
- if (!Flags.enforceTelephonyFeatureMapping()) {
- return mIsInEmergencyCall
- || isInExtensionTime
- || mTelephonyManager.getEmergencyCallbackMode()
- || mTelephonyManager.isInEmergencySmsMode();
- } else {
- boolean emergencyCallbackMode = false;
- boolean emergencySmsMode = false;
- PackageManager pm = mContext.getPackageManager();
- if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING)) {
- emergencyCallbackMode = mTelephonyManager.getEmergencyCallbackMode();
- }
- if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)) {
- emergencySmsMode = mTelephonyManager.isInEmergencySmsMode();
- }
- return mIsInEmergencyCall
- || isInExtensionTime
- || emergencyCallbackMode
- || emergencySmsMode;
+ boolean emergencyCallbackMode = false;
+ boolean emergencySmsMode = false;
+ PackageManager pm = mContext.getPackageManager();
+ if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING)) {
+ emergencyCallbackMode = mTelephonyManager.getEmergencyCallbackMode();
}
+ if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)) {
+ emergencySmsMode = mTelephonyManager.isInEmergencySmsMode();
+ }
+ return mIsInEmergencyCall
+ || isInExtensionTime
+ || emergencyCallbackMode
+ || emergencySmsMode;
}
private class EmergencyCallTelephonyCallback extends TelephonyCallback implements
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/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index cb01727..1ac9711 100644
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -544,6 +544,7 @@
// and overwritten.
throw new XmlPullParserException("old version " + oldVersion + "; ignoring");
case 3:
+ case 4:
// Upgrading from version 3 to 5 is not a breaking change so do not ignore the
// overlay file.
return;
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index 29f8243..ae41519 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -24,6 +24,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SpecialUsers.CanBeALL;
import android.annotation.UserIdInt;
import android.content.pm.PackageManager;
import android.os.CreateAppDataArgs;
@@ -548,7 +549,7 @@
return prepareAppDataFuture;
}
- void clearAppDataLIF(AndroidPackage pkg, int userId, int flags) {
+ void clearAppDataLIF(AndroidPackage pkg, @CanBeALL @UserIdInt int userId, int flags) {
if (pkg == null) {
return;
}
@@ -559,7 +560,8 @@
}
}
- void clearAppDataLeafLIF(String packageName, String volumeUuid, int userId, int flags) {
+ void clearAppDataLeafLIF(String packageName, String volumeUuid, @CanBeALL @UserIdInt int userId,
+ int flags) {
final Computer snapshot = mPm.snapshotComputer();
final PackageStateInternal packageStateInternal =
snapshot.getPackageStateInternal(packageName);
diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
index c3af578..463989a 100644
--- a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
+++ b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
@@ -332,7 +332,8 @@
userId)
!= PERMISSION_GRANTED) {
if(Build.IS_DEBUGGABLE) {
- Slog.d(TAG, "handlePackageAdd " + packageName + ": installer doesn't "
+ Slog.d(TAG, "handlePackageAdd " + packageName + ": installer ("
+ + installerPackageName + ") doesn't "
+ "have INSTALL_PACKAGES permission, skipping");
}
return;
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 90adb66..38aa57f 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -24,6 +24,7 @@
import static android.content.pm.PackageManager.DELETE_SUCCEEDED;
import static android.content.pm.PackageManager.MATCH_KNOWN_PACKAGES;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.UserHandle.USER_ALL;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
@@ -35,6 +36,8 @@
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SpecialUsers.CanBeALL;
+import android.annotation.UserIdInt;
import android.app.ApplicationExitInfo;
import android.app.ApplicationPackageManager;
import android.content.Intent;
@@ -120,7 +123,7 @@
final boolean res;
final int removeUser = (deleteFlags & PackageManager.DELETE_ALL_USERS) != 0
- ? UserHandle.USER_ALL : userId;
+ ? USER_ALL : userId;
final PackageSetting uninstalledPs;
final PackageSetting disabledSystemPs;
@@ -181,7 +184,7 @@
if (libraryInfo != null) {
boolean flagSdkLibIndependence = Flags.sdkLibIndependence();
for (int currUserId : allUsers) {
- if (removeUser != UserHandle.USER_ALL && removeUser != currUserId) {
+ if (removeUser != USER_ALL && removeUser != currUserId) {
continue;
}
var libClientPackagesPair = computer.getPackagesUsingSharedLibrary(
@@ -225,7 +228,7 @@
&& ((deleteFlags & PackageManager.DELETE_SYSTEM_APP) == 0)) {
// We're downgrading a system app, which will apply to all users, so
// freeze them all during the downgrade
- freezeUser = UserHandle.USER_ALL;
+ freezeUser = USER_ALL;
priorUserStates = new SparseArray<>();
for (int i = 0; i < allUsers.length; i++) {
PackageUserState userState = uninstalledPs.readUserState(allUsers[i]);
@@ -419,7 +422,7 @@
if (PackageManagerServiceUtils.isSystemApp(ps)) {
final boolean deleteSystem = (flags & PackageManager.DELETE_SYSTEM_APP) != 0;
final boolean deleteAllUsers =
- user == null || user.getIdentifier() == UserHandle.USER_ALL;
+ user == null || user.getIdentifier() == USER_ALL;
if ((!deleteSystem || deleteAllUsers) && disabledPs == null) {
Slog.w(TAG, "Attempt to delete unknown system package "
+ ps.getPkg().getPackageName());
@@ -462,9 +465,9 @@
Manifest.permission.SUSPEND_APPS, packageName, userId) == PERMISSION_GRANTED);
}
- final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier();
+ final int userId = user == null ? USER_ALL : user.getIdentifier();
// Remember which users are affected, before the installed states are modified
- outInfo.mRemovedUsers = userId == UserHandle.USER_ALL
+ outInfo.mRemovedUsers = userId == USER_ALL
? ps.queryUsersInstalledOrHasData(allUserHandles)
: new int[]{userId};
outInfo.populateBroadcastUsers(ps);
@@ -477,7 +480,7 @@
outInfo.mRemovedPackageVersionCode = ps.getVersionCode();
if ((!systemApp || (flags & PackageManager.DELETE_SYSTEM_APP) != 0)
- && userId != UserHandle.USER_ALL) {
+ && userId != USER_ALL) {
// The caller is asking that the package only be deleted for a single
// user. To do this, we just mark its uninstalled state and delete
// its data. If this is a system app, we only allow this to happen if
@@ -550,7 +553,7 @@
for (final int affectedUserId : outInfo.mRemovedUsers) {
if (hadSuspendAppsPermission.get(affectedUserId)) {
mPm.unsuspendForSuspendingPackage(snapshot, packageName,
- affectedUserId /*suspendingUserId*/, true /*inAllUsers*/);
+ affectedUserId /*suspendingUserId*/, USER_ALL);
mPm.removeAllDistractingPackageRestrictions(snapshot, affectedUserId);
}
}
@@ -562,7 +565,7 @@
}
@GuardedBy("mPm.mInstallLock")
- private void deleteInstalledPackageLIF(PackageSetting ps, int userId,
+ private void deleteInstalledPackageLIF(PackageSetting ps, @CanBeALL @UserIdInt int userId,
boolean deleteCodeAndResources, int flags, @NonNull int[] allUserHandles,
@NonNull PackageRemovedInfo outInfo, boolean writeSettings) {
synchronized (mPm.mLock) {
@@ -588,7 +591,7 @@
@GuardedBy("mPm.mLock")
private void markPackageUninstalledForUserLPw(PackageSetting ps, UserHandle user, int flags) {
- final int[] userIds = (user == null || user.getIdentifier() == UserHandle.USER_ALL)
+ final int[] userIds = (user == null || user.getIdentifier() == USER_ALL)
? mUserManagerInternal.getUserIds()
: new int[] {user.getIdentifier()};
for (int nextUserId : userIds) {
@@ -685,7 +688,7 @@
flags |= PackageManager.DELETE_KEEP_DATA;
}
try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) {
- deleteInstalledPackageLIF(deletedPs, UserHandle.USER_ALL, true, flags, allUserHandles,
+ deleteInstalledPackageLIF(deletedPs, USER_ALL, true, flags, allUserHandles,
outInfo, writeSettings);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageFreezer.java b/services/core/java/com/android/server/pm/PackageFreezer.java
index 11f2059..d66eb81 100644
--- a/services/core/java/com/android/server/pm/PackageFreezer.java
+++ b/services/core/java/com/android/server/pm/PackageFreezer.java
@@ -18,6 +18,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SpecialUsers.CanBeALL;
+import android.annotation.UserIdInt;
import android.content.pm.Flags;
import android.content.pm.PackageManager;
@@ -60,12 +62,12 @@
}
}
- PackageFreezer(String packageName, int userId, String killReason,
+ PackageFreezer(String packageName, @CanBeALL @UserIdInt int userId, String killReason,
PackageManagerService pm, int exitInfoReason, @Nullable InstallRequest request) {
this(packageName, userId, killReason, pm, exitInfoReason, request, false);
}
- PackageFreezer(String packageName, int userId, String killReason,
+ PackageFreezer(String packageName, @CanBeALL @UserIdInt int userId, String killReason,
PackageManagerService pm, int exitInfoReason, @Nullable InstallRequest request,
boolean waitAppKilled) {
mPm = pm;
diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
index ed568b8..f5230c5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
+++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
@@ -689,7 +689,7 @@
final int suspendingUserId =
crossUserSuspensionEnabledRo() ? UserHandle.USER_SYSTEM : affectedUser;
mService.unsuspendForSuspendingPackage(
- snapshot(), PLATFORM_PACKAGE_NAME, suspendingUserId, /* inAllUsers= */ false);
+ snapshot(), PLATFORM_PACKAGE_NAME, suspendingUserId, affectedUser);
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2464a29..021da6f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -34,6 +34,7 @@
import static android.crashrecovery.flags.Flags.refactorCrashrecovery;
import static android.os.Process.INVALID_UID;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import static android.os.UserHandle.USER_ALL;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
@@ -50,6 +51,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SpecialUsers.CanBeALL;
import android.annotation.StringRes;
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
@@ -1586,13 +1588,13 @@
}
void scheduleWritePackageRestrictions(UserHandle user) {
- final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier();
+ final int userId = user == null ? USER_ALL : user.getIdentifier();
scheduleWritePackageRestrictions(userId);
}
- void scheduleWritePackageRestrictions(int userId) {
+ void scheduleWritePackageRestrictions(@CanBeALL @UserIdInt int userId) {
invalidatePackageInfoCache();
- if (userId == UserHandle.USER_ALL) {
+ if (userId == USER_ALL) {
synchronized (mDirtyUsers) {
for (int aUserId : mUserManager.getUserIds()) {
mDirtyUsers.add(aUserId);
@@ -1805,7 +1807,7 @@
private void installAllowlistedSystemPackages() {
if (mUserManager.installWhitelistedSystemPackages(isFirstBoot(), isDeviceUpgrading(),
mExistingPackages)) {
- scheduleWritePackageRestrictions(UserHandle.USER_ALL);
+ scheduleWritePackageRestrictions(USER_ALL);
scheduleWriteSettings();
}
}
@@ -2392,7 +2394,7 @@
final PackageSetting ps = packageSettings.valueAt(i);
if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.getVolumeUuid())) {
// No apps are running this early, so no need to freeze
- mAppDataHelper.clearAppDataLIF(ps.getPkg(), UserHandle.USER_ALL,
+ mAppDataHelper.clearAppDataLIF(ps.getPkg(), USER_ALL,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
| Installer.FLAG_CLEAR_CODE_CACHE_ONLY
| Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES);
@@ -3074,8 +3076,8 @@
}
@NonNull
- int[] resolveUserIds(int userId) {
- return (userId == UserHandle.USER_ALL) ? mUserManager.getUserIds() : new int[] { userId };
+ int[] resolveUserIds(@CanBeALL @UserIdInt int userId) {
+ return (userId == USER_ALL) ? mUserManager.getUserIds() : new int[]{userId};
}
private void setUpInstantAppInstallerActivityLP(ActivityInfo installerActivity) {
@@ -3108,11 +3110,11 @@
}
void killApplication(String pkgName, @AppIdInt int appId, String reason, int exitInfoReason) {
- killApplication(pkgName, appId, UserHandle.USER_ALL, reason, exitInfoReason);
+ killApplication(pkgName, appId, USER_ALL, reason, exitInfoReason);
}
void killApplication(String pkgName, @AppIdInt int appId,
- @UserIdInt int userId, String reason, int exitInfoReason) {
+ @CanBeALL @UserIdInt int userId, String reason, int exitInfoReason) {
// Request the ActivityManager to kill the process(only for existing packages)
// so that we do not end up in a confused state while the user is still using the older
// version of the application while the new one gets installed.
@@ -3131,7 +3133,7 @@
}
void killApplicationSync(String pkgName, @AppIdInt int appId,
- @UserIdInt int userId, String reason, int exitInfoReason) {
+ @CanBeALL @UserIdInt int userId, String reason, int exitInfoReason) {
ActivityManagerInternal mAmi = LocalServices.getService(ActivityManagerInternal.class);
if (Thread.holdsLock(mLock) || mAmi == null) {
// holds PM's lock, go back killApplication to avoid it run into watchdog reset.
@@ -3228,23 +3230,24 @@
}
/**
- * @param inAllUsers Whether to unsuspend packages suspended by the given package in other
- * users. This flag is only used when cross-user suspension is enabled.
+ * @param suspendingUserId The user that has suspended apps using the suspending package.
+ * @param targetUserId The user whose apps should be unsuspended. Pass {@code USER_ALL} to
+ * unsuspend for all users.
*/
void unsuspendForSuspendingPackage(@NonNull Computer computer, String suspendingPackage,
- @UserIdInt int suspendingUserId, boolean inAllUsers) {
+ @UserIdInt int suspendingUserId, @CanBeALL @UserIdInt int targetUserId) {
// TODO: This can be replaced by a special parameter to iterate all packages, rather than
// this weird pre-collect of all packages.
final String[] allPackages = computer.getPackageStates().keySet().toArray(new String[0]);
final Predicate<UserPackage> suspenderPredicate =
UserPackage.of(suspendingUserId, suspendingPackage)::equals;
- if (!crossUserSuspensionEnabledRo() || !inAllUsers) {
+ if (!crossUserSuspensionEnabledRo() || targetUserId != USER_ALL) {
mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(computer,
- allPackages, suspenderPredicate, suspendingUserId);
+ allPackages, suspenderPredicate, targetUserId);
} else {
- for (int targetUserId: mUserManager.getUserIds()) {
+ for (int user : mUserManager.getUserIds()) {
mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(
- computer, allPackages, suspenderPredicate, targetUserId);
+ computer, allPackages, suspenderPredicate, user);
}
}
}
@@ -3381,11 +3384,11 @@
&& !snapshot.isCallerSameApp(packageName, callingUid)) {
return false;
}
- return isPackageDeviceAdmin(packageName, UserHandle.USER_ALL);
+ return isPackageDeviceAdmin(packageName, USER_ALL);
}
// TODO(b/261957226): centralise this logic in DPM
- boolean isPackageDeviceAdmin(String packageName, int userId) {
+ boolean isPackageDeviceAdmin(String packageName, @CanBeALL @UserIdInt int userId) {
final IDevicePolicyManager dpm = getDevicePolicyManager();
final DevicePolicyManagerInternal dpmi =
mInjector.getLocalService(DevicePolicyManagerInternal.class);
@@ -3405,7 +3408,7 @@
// Does it contain a device admin for any user?
int[] allUsers = mUserManager.getUserIds();
int[] targetUsers;
- if (userId == UserHandle.USER_ALL) {
+ if (userId == USER_ALL) {
targetUsers = allUsers;
} else {
targetUsers = new int[]{userId};
@@ -3555,7 +3558,7 @@
/** This method takes a specific user id as well as UserHandle.USER_ALL. */
@GuardedBy("mLock")
void clearPackagePreferredActivitiesLPw(String packageName,
- @NonNull SparseBooleanArray outUserChanged, int userId) {
+ @NonNull SparseBooleanArray outUserChanged, @CanBeALL @UserIdInt int userId) {
mSettings.clearPackagePreferredActivities(packageName, outUserChanged, userId);
}
@@ -4152,7 +4155,7 @@
// This app should not generally be allowed to get disabled by the UI, but
// if it ever does, we don't want to end up with some of the user's apps
// permanently suspended.
- unsuspendForSuspendingPackage(computer, packageName, userId, true /* inAllUsers */);
+ unsuspendForSuspendingPackage(computer, packageName, userId, USER_ALL);
removeAllDistractingPackageRestrictions(computer, userId);
}
success = true;
@@ -4243,9 +4246,9 @@
};
mContext.getContentResolver().registerContentObserver(android.provider.Settings.Global
.getUriFor(Global.ENABLE_EPHEMERAL_FEATURE),
- false, co, UserHandle.USER_ALL);
+ false, co, USER_ALL);
mContext.getContentResolver().registerContentObserver(android.provider.Settings.Secure
- .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_ALL);
+ .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, USER_ALL);
co.onChange(true);
mAppsFilter.onSystemReady(LocalServices.getService(PackageManagerInternal.class));
@@ -4388,14 +4391,14 @@
}
}
- public PackageFreezer freezePackage(String packageName, int userId, String killReason,
- int exitInfoReason, InstallRequest request) {
+ public PackageFreezer freezePackage(String packageName, @CanBeALL @UserIdInt int userId,
+ String killReason, int exitInfoReason, InstallRequest request) {
return freezePackage(packageName, userId, killReason, exitInfoReason, request,
/* waitAppKilled= */ false);
}
- private PackageFreezer freezePackage(String packageName, int userId, String killReason,
- int exitInfoReason, InstallRequest request, boolean waitAppKilled) {
+ private PackageFreezer freezePackage(String packageName, @CanBeALL @UserIdInt int userId,
+ String killReason, int exitInfoReason, InstallRequest request, boolean waitAppKilled) {
return new PackageFreezer(packageName, userId, killReason, this, exitInfoReason, request,
waitAppKilled);
}
@@ -4773,7 +4776,7 @@
final Computer snapshot = snapshotComputer();
final AndroidPackage pkg = snapshot.getPackage(packageName);
try (PackageFreezer ignored =
- freezePackage(packageName, UserHandle.USER_ALL,
+ freezePackage(packageName, USER_ALL,
"clearApplicationProfileData",
ApplicationExitInfo.REASON_OTHER, null /* request */)) {
try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) {
@@ -4819,7 +4822,7 @@
public void run() {
mHandler.removeCallbacks(this);
final boolean succeeded;
- try (PackageFreezer freezer = freezePackage(packageName, UserHandle.USER_ALL,
+ try (PackageFreezer freezer = freezePackage(packageName, USER_ALL,
"clearApplicationUserData",
ApplicationExitInfo.REASON_USER_REQUESTED, null /* request */,
/* waitAppKilled= */ true)) {
@@ -4846,7 +4849,7 @@
== PERMISSION_GRANTED) {
final Computer snapshot = snapshotComputer();
unsuspendForSuspendingPackage(
- snapshot, packageName, userId, true /* inAllUsers */);
+ snapshot, packageName, userId, USER_ALL);
removeAllDistractingPackageRestrictions(snapshot, userId);
synchronized (mLock) {
flushPackageRestrictionsAsUserInternalLocked(userId);
@@ -6371,13 +6374,13 @@
if (mComponentResolver.updateMimeGroup(snapshotComputer(), packageName, mimeGroup)) {
Binder.withCleanCallingIdentity(() -> {
mPreferredActivityHelper.clearPackagePreferredActivities(packageName,
- UserHandle.USER_ALL);
+ USER_ALL);
// Send the ACTION_PACKAGE_CHANGED when the mimeGroup has changes
final Computer snapShot = snapshotComputer();
final ArrayList<String> components = new ArrayList<>(
Collections.singletonList(packageName));
final int appId = packageState.getAppId();
- final int[] userIds = resolveUserIds(UserHandle.USER_ALL);
+ final int[] userIds = resolveUserIds(USER_ALL);
final String reason = "The mimeGroup is changed";
for (int i = 0; i < userIds.length; i++) {
final PackageUserStateInternal pkgUserState =
diff --git a/services/core/java/com/android/server/pm/PreferredActivityHelper.java b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
index 41d2aeb..fa56596 100644
--- a/services/core/java/com/android/server/pm/PreferredActivityHelper.java
+++ b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
@@ -25,6 +25,7 @@
import static com.android.server.pm.PackageManagerService.TAG;
import android.annotation.NonNull;
+import android.annotation.SpecialUsers.CanBeALL;
import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Intent;
@@ -115,7 +116,8 @@
}
/** This method takes a specific user id as well as UserHandle.USER_ALL. */
- public void clearPackagePreferredActivities(String packageName, int userId) {
+ public void clearPackagePreferredActivities(String packageName,
+ @CanBeALL @UserIdInt int userId) {
final SparseBooleanArray changedUsers = new SparseBooleanArray();
synchronized (mPm.mLock) {
mPm.clearPackagePreferredActivitiesLPw(packageName, changedUsers, userId);
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index f01a74e..22b4ec7 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -30,6 +30,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SpecialUsers.CanBeALL;
+import android.annotation.UserIdInt;
import android.content.pm.PackageManager;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
@@ -256,7 +258,8 @@
* Make sure this flag is set for partially installed apps. If not it's meaningless to
* delete a partially installed application.
*/
- public void clearPackageStateForUserLIF(PackageSetting ps, int userId, int flags) {
+ public void clearPackageStateForUserLIF(PackageSetting ps, @CanBeALL @UserIdInt int userId,
+ int flags) {
final String packageName = ps.getPackageName();
// Step 1: always destroy app profiles except when explicitly preserved
if ((flags & Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES) == 0) {
@@ -370,13 +373,13 @@
* This method deletes the package from internal data structures such as mPackages / mSettings.
*
* @param targetUserId indicates the target user of the deletion. It equals to
- * {@link UserHandle.USER_ALL} if the deletion was initiated for all users,
+ * {@link UserHandle#USER_ALL} if the deletion was initiated for all users,
* otherwise it equals to the specific user id that the deletion was meant
* for.
*/
@GuardedBy("mPm.mInstallLock")
- public void removePackageDataLIF(final PackageSetting deletedPs, int targetUserId,
- @NonNull int[] allUserHandles,
+ public void removePackageDataLIF(final PackageSetting deletedPs,
+ @CanBeALL @UserIdInt int targetUserId, @NonNull int[] allUserHandles,
@NonNull PackageRemovedInfo outInfo, int flags, boolean writeSettings) {
String packageName = deletedPs.getPackageName();
if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + deletedPs);
@@ -482,7 +485,8 @@
}
}
- private static boolean shouldDeletePackageSetting(PackageSetting deletedPs, int userId,
+ private static boolean shouldDeletePackageSetting(PackageSetting deletedPs,
+ @CanBeALL @UserIdInt int userId,
int[] allUserHandles, int flags) {
if ((flags & PackageManager.DELETE_KEEP_DATA) != 0) {
return false;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 485a280..92257f1 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -35,6 +35,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SpecialUsers.CanBeALL;
import android.annotation.UserIdInt;
import android.app.compat.ChangeIdStateCache;
import android.content.ComponentName;
@@ -6639,7 +6640,7 @@
/** This method takes a specific user id as well as UserHandle.USER_ALL. */
void clearPackagePreferredActivities(String packageName,
- @NonNull SparseBooleanArray outUserChanged, int userId) {
+ @NonNull SparseBooleanArray outUserChanged, @CanBeALL @UserIdInt int userId) {
boolean changed = false;
ArrayList<PreferredActivity> removed = null;
for (int i = 0; i < mPreferredActivities.size(); i++) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 5c5a9c1..ac19ea1 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -37,6 +37,7 @@
import android.annotation.AppIdInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SpecialUsers.CanBeALL;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AppOpsManager;
@@ -765,7 +766,7 @@
@Override
public void onPackageUninstalled(@NonNull String packageName, int appId,
@NonNull PackageState packageState, @Nullable AndroidPackage pkg,
- @NonNull List<AndroidPackage> sharedUserPkgs, @UserIdInt int userId) {
+ @NonNull List<AndroidPackage> sharedUserPkgs, @CanBeALL @UserIdInt int userId) {
if (userId != UserHandle.USER_ALL) {
final int[] userIds = getAllUserIds();
if (!ArrayUtils.contains(userIds, userId)) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index e51ec04..33d57d5 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -65,6 +65,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SpecialUsers.CanBeALL;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AppOpsManager;
@@ -5284,7 +5285,7 @@
@Override
public void onPackageUninstalled(@NonNull String packageName, int appId,
@NonNull PackageState packageState, @Nullable AndroidPackage pkg,
- @NonNull List<AndroidPackage> sharedUserPkgs, @UserIdInt int userId) {
+ @NonNull List<AndroidPackage> sharedUserPkgs, @CanBeALL @UserIdInt int userId) {
Objects.requireNonNull(packageState, "packageState");
Objects.requireNonNull(packageName, "packageName");
Objects.requireNonNull(sharedUserPkgs, "sharedUserPkgs");
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
index 3d295f7..f2491d9 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
@@ -19,6 +19,7 @@
import android.annotation.AppIdInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SpecialUsers.CanBeALL;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -662,5 +663,5 @@
*/
void onPackageUninstalled(@NonNull String packageName, int appId,
@NonNull PackageState packageState, @Nullable AndroidPackage pkg,
- @NonNull List<AndroidPackage> sharedUserPkgs, @UserIdInt int userId);
+ @NonNull List<AndroidPackage> sharedUserPkgs, @CanBeALL @UserIdInt int userId);
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index a5c1284..ad765c8 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SpecialUsers.CanBeALL;
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.content.pm.PackageInstaller.SessionParams;
@@ -325,7 +326,7 @@
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
void onPackageUninstalled(@NonNull String packageName, int appId,
@Nullable PackageState packageState, @Nullable AndroidPackage pkg,
- @NonNull List<AndroidPackage> sharedUserPkgs, @UserIdInt int userId);
+ @NonNull List<AndroidPackage> sharedUserPkgs, @CanBeALL @UserIdInt int userId);
/**
* The permission-related parameters passed in for package installation.
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 1d62087..2cf6b7e 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -2911,19 +2911,7 @@
return false;
}
- mCounter.getCounts(counts, procState);
-
- // Return counts only if at least one of the elements is non-zero.
- for (int i = counts.length - 1; i >= 0; --i) {
- if (counts[i] != 0) {
- return true;
- }
- }
- return false;
- }
-
- public void logState(Printer pw, String prefix) {
- pw.println(prefix + "mCounter=" + mCounter);
+ return mCounter.getCounts(counts, procState);
}
/**
diff --git a/services/core/java/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessor.java
index 5f93bdf..a550f2d 100644
--- a/services/core/java/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessor.java
@@ -68,11 +68,15 @@
// processor. All that remains to be done is copy the estimates over.
MultiStateStats.States.forEachTrackedStateCombination(deviceStateConfig,
states -> {
- screenStats.getDeviceStats(mTmpScreenStats, states);
+ if (!screenStats.getDeviceStats(mTmpScreenStats, states)) {
+ return;
+ }
double power =
mScreenPowerStatsLayout.getScreenDozePowerEstimate(mTmpScreenStats);
- mStatsLayout.setDevicePowerEstimate(mTmpDeviceStats, power);
- stats.setDeviceStats(states, mTmpDeviceStats);
+ if (power != 0) {
+ mStatsLayout.setDevicePowerEstimate(mTmpDeviceStats, power);
+ stats.setDeviceStats(states, mTmpDeviceStats);
+ }
});
}
}
diff --git a/services/core/java/com/android/server/power/stats/processor/CpuPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/CpuPowerStatsProcessor.java
index 6ce2cf7..98280be 100644
--- a/services/core/java/com/android/server/power/stats/processor/CpuPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/CpuPowerStatsProcessor.java
@@ -119,6 +119,8 @@
mTmpUidStatsArray = new long[descriptor.uidStatsArrayLength];
mWakelockDescriptor = null;
+
+ initEnergyConsumerToPowerBracketMaps();
}
/**
@@ -157,9 +159,6 @@
if (mPlan == null) {
mPlan = new PowerEstimationPlan(stats.getConfig());
- if (mStatsLayout.getEnergyConsumerCount() != 0) {
- initEnergyConsumerToPowerBracketMaps();
- }
}
Intermediates intermediates = new Intermediates();
@@ -255,6 +254,10 @@
*/
private void initEnergyConsumerToPowerBracketMaps() {
int energyConsumerCount = mStatsLayout.getEnergyConsumerCount();
+ if (energyConsumerCount == 0) {
+ return;
+ }
+
int powerBracketCount = mStatsLayout.getCpuPowerBracketCount();
mEnergyConsumerToCombinedEnergyConsumerMap = new int[energyConsumerCount];
@@ -404,7 +407,10 @@
deviceStatsIntermediates.timeByBracket = new long[powerBracketCount];
deviceStatsIntermediates.powerByBracket = new double[powerBracketCount];
- stats.getDeviceStats(mTmpDeviceStatsArray, deviceStateEstimation.stateValues);
+ if (!stats.getDeviceStats(mTmpDeviceStatsArray, deviceStateEstimation.stateValues)) {
+ continue;
+ }
+
for (int step = 0; step < cpuScalingStepCount; step++) {
if (intermediates.timeByScalingStep[step] == 0) {
continue;
@@ -429,16 +435,19 @@
}
if (wakelockStats != null) {
- wakelockStats.getDeviceStats(mTmpWakelockDeviceStats,
- deviceStateEstimation.stateValues);
- double wakelockPowerEstimate = mWakelockPowerStatsLayout.getDevicePowerEstimate(
- mTmpWakelockDeviceStats);
- power = Math.max(0, power - wakelockPowerEstimate);
+ if (wakelockStats.getDeviceStats(mTmpWakelockDeviceStats,
+ deviceStateEstimation.stateValues)) {
+ double wakelockPowerEstimate = mWakelockPowerStatsLayout.getDevicePowerEstimate(
+ mTmpWakelockDeviceStats);
+ power = Math.max(0, power - wakelockPowerEstimate);
+ }
}
- deviceStatsIntermediates.power = power;
- mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, power);
- stats.setDeviceStats(deviceStateEstimation.stateValues, mTmpDeviceStatsArray);
+ if (power != 0) {
+ deviceStatsIntermediates.power = power;
+ mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, power);
+ stats.setDeviceStats(deviceStateEstimation.stateValues, mTmpDeviceStatsArray);
+ }
}
}
@@ -538,11 +547,12 @@
}
if (wakelockStats != null) {
- wakelockStats.getUidStats(mTmpWakelockUidStats, uid,
- proportionalEstimate.stateValues);
- double wakelockPowerEstimate = mWakelockPowerStatsLayout.getUidPowerEstimate(
- mTmpWakelockUidStats);
- power = Math.max(0, power - wakelockPowerEstimate);
+ if (wakelockStats.getUidStats(mTmpWakelockUidStats, uid,
+ proportionalEstimate.stateValues)) {
+ double wakelockPowerEstimate = mWakelockPowerStatsLayout.getUidPowerEstimate(
+ mTmpWakelockUidStats);
+ power = Math.max(0, power - wakelockPowerEstimate);
+ }
}
if (power != 0) {
diff --git a/services/core/java/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessor.java
index a544daa..58835053 100644
--- a/services/core/java/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessor.java
@@ -259,7 +259,9 @@
stats.forEachStateStatsKey(key -> {
RxTxPowerEstimators estimators = mRxTxPowerEstimators.get(key);
- stats.getStateStats(mTmpStateStatsArray, key, deviceStates);
+ if (!stats.getStateStats(mTmpStateStatsArray, key, deviceStates)) {
+ return;
+ }
long rxTime = mStatsLayout.getStateRxTime(mTmpStateStatsArray);
intermediates.rxPower += estimators.mRxPowerEstimator.calculatePower(rxTime);
for (int txLevel = 0; txLevel < ModemActivityInfo.getNumTxPowerLevels(); txLevel++) {
diff --git a/services/core/java/com/android/server/power/stats/processor/MultiStateStats.java b/services/core/java/com/android/server/power/stats/processor/MultiStateStats.java
index 6932575..0038943 100644
--- a/services/core/java/com/android/server/power/stats/processor/MultiStateStats.java
+++ b/services/core/java/com/android/server/power/stats/processor/MultiStateStats.java
@@ -16,6 +16,7 @@
package com.android.server.power.stats.processor;
+import android.annotation.CheckResult;
import android.annotation.Nullable;
import android.util.Slog;
@@ -342,10 +343,12 @@
}
/**
- * Returns accumulated stats for the specified composite state.
+ * Returns accumulated stats for the specified composite state or false if the results are
+ * all zeros.
*/
- void getStats(long[] outValues, int[] states) {
- mCounter.getCounts(outValues, mFactory.getSerialState(states));
+ @CheckResult
+ boolean getStats(long[] outValues, int[] states) {
+ return mCounter.getCounts(outValues, mFactory.getSerialState(states));
}
/**
@@ -389,15 +392,7 @@
private void writeXmlForStates(TypedXmlSerializer serializer, int[] states, long[] values)
throws IOException {
- mCounter.getCounts(values, mFactory.getSerialState(states));
- boolean nonZero = false;
- for (long value : values) {
- if (value != 0) {
- nonZero = true;
- break;
- }
- }
- if (!nonZero) {
+ if (!mCounter.getCounts(values, mFactory.getSerialState(states))) {
return;
}
@@ -470,15 +465,7 @@
StringBuilder sb = new StringBuilder();
long[] values = new long[mCounter.getArrayLength()];
States.forEachTrackedStateCombination(mFactory.mStates, states -> {
- mCounter.getCounts(values, mFactory.getSerialState(states));
- boolean nonZero = false;
- for (long value : values) {
- if (value != 0) {
- nonZero = true;
- break;
- }
- }
- if (!nonZero) {
+ if (!mCounter.getCounts(values, mFactory.getSerialState(states))) {
return;
}
diff --git a/services/core/java/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessor.java
index 3957ae0..ad628e4 100644
--- a/services/core/java/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessor.java
@@ -69,12 +69,15 @@
// processor. All that remains to be done is copy the estimates over.
MultiStateStats.States.forEachTrackedStateCombination(deviceStateConfig,
states -> {
- mobileRadioStats.getDeviceStats(mTmpMobileRadioDeviceStats, states);
- double callPowerEstimate =
- mMobileRadioStatsLayout.getDeviceCallPowerEstimate(
- mTmpMobileRadioDeviceStats);
- mStatsLayout.setDevicePowerEstimate(mTmpDeviceStats, callPowerEstimate);
- stats.setDeviceStats(states, mTmpDeviceStats);
+ if (!mobileRadioStats.getDeviceStats(mTmpMobileRadioDeviceStats, states)) {
+ return;
+ }
+ double callPowerEstimate = mMobileRadioStatsLayout.getDeviceCallPowerEstimate(
+ mTmpMobileRadioDeviceStats);
+ if (callPowerEstimate != 0) {
+ mStatsLayout.setDevicePowerEstimate(mTmpDeviceStats, callPowerEstimate);
+ stats.setDeviceStats(states, mTmpDeviceStats);
+ }
});
}
}
diff --git a/services/core/java/com/android/server/power/stats/processor/PowerComponentAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/processor/PowerComponentAggregatedPowerStats.java
index f9b9da9..d285a59 100644
--- a/services/core/java/com/android/server/power/stats/processor/PowerComponentAggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/processor/PowerComponentAggregatedPowerStats.java
@@ -16,6 +16,7 @@
package com.android.server.power.stats.processor;
+import android.annotation.CheckResult;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.BatteryStats;
@@ -312,6 +313,11 @@
return uids;
}
+ /**
+ * Populates outValues with the stats for the specified states. If the stats are all 0,
+ * returns false, leaving outValues unchanged.
+ */
+ @CheckResult
boolean getDeviceStats(long[] outValues, int[] deviceStates) {
if (deviceStates.length != mDeviceStateConfig.length) {
throw new IllegalArgumentException(
@@ -319,12 +325,16 @@
+ " expected: " + mDeviceStateConfig.length);
}
if (mDeviceStats != null) {
- mDeviceStats.getStats(outValues, deviceStates);
- return true;
+ return mDeviceStats.getStats(outValues, deviceStates);
}
return false;
}
+ /**
+ * Populates outValues with the stats for the specified key and device states. If the stats
+ * are all 0, returns false, leaving outValues unchanged.
+ */
+ @CheckResult
boolean getStateStats(long[] outValues, int key, int[] deviceStates) {
if (deviceStates.length != mDeviceStateConfig.length) {
throw new IllegalArgumentException(
@@ -333,8 +343,7 @@
}
MultiStateStats stateStats = mStateStats.get(key);
if (stateStats != null) {
- stateStats.getStats(outValues, deviceStates);
- return true;
+ return stateStats.getStats(outValues, deviceStates);
}
return false;
}
@@ -345,6 +354,11 @@
}
}
+ /**
+ * Populates outValues with the stats for the specified UID and UID states. If the stats are
+ * all 0, returns false, leaving outValues unchanged.
+ */
+ @CheckResult
boolean getUidStats(long[] outValues, int uid, int[] uidStates) {
if (uidStates.length != mUidStateConfig.length) {
throw new IllegalArgumentException(
@@ -353,8 +367,7 @@
}
UidStats uidStats = mUidStats.get(uid);
if (uidStats != null && uidStats.stats != null) {
- uidStats.stats.getStats(outValues, uidStates);
- return true;
+ return uidStats.stats.getStats(outValues, uidStates);
}
return false;
}
@@ -578,15 +591,7 @@
long[] values = new long[stats.getDimensionCount()];
MultiStateStats.States[] stateInfo = stats.getStates();
MultiStateStats.States.forEachTrackedStateCombination(stateInfo, states -> {
- stats.getStats(values, states);
- boolean nonZero = false;
- for (long value : values) {
- if (value != 0) {
- nonZero = true;
- break;
- }
- }
- if (!nonZero) {
+ if (!stats.getStats(values, states)) {
return;
}
diff --git a/services/core/java/com/android/server/power/stats/processor/SensorPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/SensorPowerStatsProcessor.java
index ba728d3..284e6a9 100644
--- a/services/core/java/com/android/server/power/stats/processor/SensorPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/SensorPowerStatsProcessor.java
@@ -298,12 +298,16 @@
continue;
}
- if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) {
+ double power = ((Intermediates) estimation.intermediates).power;
+ if (power == 0) {
continue;
}
- mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray,
- ((Intermediates) estimation.intermediates).power);
+ if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) {
+ Arrays.fill(mTmpDeviceStatsArray, 0);
+ }
+
+ mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, power);
stats.setDeviceStats(estimation.stateValues, mTmpDeviceStatsArray);
}
}
diff --git a/services/core/java/com/android/server/storage/ImmutableVolumeInfo.java b/services/core/java/com/android/server/storage/ImmutableVolumeInfo.java
index 9d60a57..0f8212e 100644
--- a/services/core/java/com/android/server/storage/ImmutableVolumeInfo.java
+++ b/services/core/java/com/android/server/storage/ImmutableVolumeInfo.java
@@ -136,4 +136,9 @@
public boolean isVisibleForWrite(int userId) {
return mVolumeInfo.isVisibleForWrite(userId);
}
+
+ @Override
+ public String toString() {
+ return mVolumeInfo.toString();
+ }
}
diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java
index 342b864..281aeb6 100644
--- a/services/core/java/com/android/server/storage/StorageSessionController.java
+++ b/services/core/java/com/android/server/storage/StorageSessionController.java
@@ -156,14 +156,15 @@
StorageUserConnection connection = null;
synchronized (mLock) {
connection = mConnections.get(connectionUserId);
- if (connection != null) {
- Slog.i(TAG, "Notifying volume state changed for session with id: " + sessionId);
- connection.notifyVolumeStateChanged(sessionId,
- vol.buildStorageVolume(mContext, vol.getMountUserId(), false));
- } else {
- Slog.w(TAG, "No available storage user connection for userId : "
- + connectionUserId);
- }
+ }
+
+ if (connection != null) {
+ Slog.i(TAG, "Notifying volume state changed for session with id: " + sessionId);
+ connection.notifyVolumeStateChanged(sessionId,
+ vol.buildStorageVolume(mContext, vol.getMountUserId(), false));
+ } else {
+ Slog.w(TAG, "No available storage user connection for userId : "
+ + connectionUserId);
}
}
@@ -225,16 +226,18 @@
String sessionId = vol.getId();
int userId = getConnectionUserIdForVolume(vol);
+ StorageUserConnection connection = null;
synchronized (mLock) {
- StorageUserConnection connection = mConnections.get(userId);
- if (connection != null) {
- Slog.i(TAG, "Removed session for vol with id: " + sessionId);
- connection.removeSession(sessionId);
- return connection;
- } else {
- Slog.w(TAG, "Session already removed for vol with id: " + sessionId);
- return null;
- }
+ connection = mConnections.get(userId);
+ }
+
+ if (connection != null) {
+ Slog.i(TAG, "Removed session for vol with id: " + sessionId);
+ connection.removeSession(sessionId);
+ return connection;
+ } else {
+ Slog.w(TAG, "Session already removed for vol with id: " + sessionId);
+ return null;
}
}
diff --git a/services/core/java/com/android/server/storage/WatchedVolumeInfo.java b/services/core/java/com/android/server/storage/WatchedVolumeInfo.java
index 4124cfb..94e52cd 100644
--- a/services/core/java/com/android/server/storage/WatchedVolumeInfo.java
+++ b/services/core/java/com/android/server/storage/WatchedVolumeInfo.java
@@ -203,4 +203,9 @@
public boolean isVisibleForWrite(int userId) {
return mVolumeInfo.isVisibleForWrite(userId);
}
+
+ @Override
+ public String toString() {
+ return mVolumeInfo.toString();
+ }
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java b/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
index 07d9ad1..da6478b 100644
--- a/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
@@ -124,6 +124,8 @@
Slog.d(VibrationThread.TAG,
"Turning off vibrator " + getVibratorId());
}
+ // Make sure we ignore any pending callback from old vibration commands.
+ conductor.nextVibratorCallbackStepId(getVibratorId());
controller.off();
getVibration().stats.reportVibratorOff();
mPendingVibratorOffDeadline = 0;
diff --git a/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
index e495af5..b3eead1 100644
--- a/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
@@ -73,7 +73,8 @@
PrimitiveSegment[] primitivesArray =
primitives.toArray(new PrimitiveSegment[primitives.size()]);
- long vibratorOnResult = controller.on(primitivesArray, getVibration().id);
+ int stepId = conductor.nextVibratorCallbackStepId(getVibratorId());
+ long vibratorOnResult = controller.on(primitivesArray, getVibration().id, stepId);
handleVibratorOnResult(vibratorOnResult);
getVibration().stats.reportComposePrimitives(vibratorOnResult, primitivesArray);
diff --git a/services/core/java/com/android/server/vibrator/ComposePwleV2VibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePwleV2VibratorStep.java
index bb8e6ee..7b41457 100644
--- a/services/core/java/com/android/server/vibrator/ComposePwleV2VibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/ComposePwleV2VibratorStep.java
@@ -72,7 +72,8 @@
+ controller.getVibratorInfo().getId());
}
PwlePoint[] pwlesArray = pwles.toArray(new PwlePoint[pwles.size()]);
- long vibratorOnResult = controller.on(pwlesArray, getVibration().id);
+ int stepId = conductor.nextVibratorCallbackStepId(getVibratorId());
+ long vibratorOnResult = controller.on(pwlesArray, getVibration().id, stepId);
handleVibratorOnResult(vibratorOnResult);
getVibration().stats.reportComposePwle(vibratorOnResult, pwlesArray);
diff --git a/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
index e8952fa..d003251 100644
--- a/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
@@ -71,7 +71,8 @@
+ controller.getVibratorInfo().getId());
}
RampSegment[] pwlesArray = pwles.toArray(new RampSegment[pwles.size()]);
- long vibratorOnResult = controller.on(pwlesArray, getVibration().id);
+ int stepId = conductor.nextVibratorCallbackStepId(getVibratorId());
+ long vibratorOnResult = controller.on(pwlesArray, getVibration().id, stepId);
handleVibratorOnResult(vibratorOnResult);
getVibration().stats.reportComposePwle(vibratorOnResult, pwlesArray);
diff --git a/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java b/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java
index a92ac67..b2cc1f6 100644
--- a/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java
+++ b/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java
@@ -148,7 +148,7 @@
}
@Override
- public void notifyVibratorCallback(int vibratorId, long vibrationId) {
+ public void notifyVibratorCallback(int vibratorId, long vibrationId, long stepId) {
// ignored, external control does not expect callbacks from the vibrator
}
diff --git a/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java b/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java
index 4b23216..88bb181 100644
--- a/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java
@@ -64,7 +64,8 @@
}
VibrationEffect fallback = getVibration().getFallback(prebaked.getEffectId());
- long vibratorOnResult = controller.on(prebaked, getVibration().id);
+ int stepId = conductor.nextVibratorCallbackStepId(getVibratorId());
+ long vibratorOnResult = controller.on(prebaked, getVibration().id, stepId);
handleVibratorOnResult(vibratorOnResult);
getVibration().stats.reportPerformEffect(vibratorOnResult, prebaked);
diff --git a/services/core/java/com/android/server/vibrator/PerformVendorEffectVibratorStep.java b/services/core/java/com/android/server/vibrator/PerformVendorEffectVibratorStep.java
index 407f3d9..d4bcc36 100644
--- a/services/core/java/com/android/server/vibrator/PerformVendorEffectVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/PerformVendorEffectVibratorStep.java
@@ -49,7 +49,8 @@
public List<Step> play() {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "PerformVendorEffectVibratorStep");
try {
- long vibratorOnResult = controller.on(effect, getVibration().id);
+ int stepId = conductor.nextVibratorCallbackStepId(getVibratorId());
+ long vibratorOnResult = controller.on(effect, getVibration().id, stepId);
vibratorOnResult = Math.min(vibratorOnResult, VENDOR_EFFECT_MAX_DURATION_MS);
handleVibratorOnResult(vibratorOnResult);
getVibration().stats.reportPerformVendorEffect(vibratorOnResult);
diff --git a/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java b/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
index 8478e77..26b9595 100644
--- a/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
@@ -20,6 +20,7 @@
import android.os.SystemClock;
import android.os.Trace;
import android.os.VibrationEffect;
+import android.os.vibrator.Flags;
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationEffectSegment;
import android.util.Slog;
@@ -49,6 +50,10 @@
@Override
public boolean acceptVibratorCompleteCallback(int vibratorId) {
+ if (Flags.fixVibrationThreadCallbackHandling()) {
+ // TODO: remove this method once flag removed.
+ return super.acceptVibratorCompleteCallback(vibratorId);
+ }
// Ensure the super method is called and will reset the off timeout and boolean flag.
// This is true if the vibrator was ON and this callback has the same vibratorId.
if (!super.acceptVibratorCompleteCallback(vibratorId)) {
@@ -161,7 +166,8 @@
"Turning on vibrator " + controller.getVibratorInfo().getId() + " for "
+ duration + "ms");
}
- long vibratorOnResult = controller.on(duration, getVibration().id);
+ int stepId = conductor.nextVibratorCallbackStepId(getVibratorId());
+ long vibratorOnResult = controller.on(duration, getVibration().id, stepId);
handleVibratorOnResult(vibratorOnResult);
getVibration().stats.reportVibratorOn(vibratorOnResult);
return vibratorOnResult;
diff --git a/services/core/java/com/android/server/vibrator/SingleVibrationSession.java b/services/core/java/com/android/server/vibrator/SingleVibrationSession.java
index 628221b..309eb8c 100644
--- a/services/core/java/com/android/server/vibrator/SingleVibrationSession.java
+++ b/services/core/java/com/android/server/vibrator/SingleVibrationSession.java
@@ -137,13 +137,13 @@
}
@Override
- public void notifyVibratorCallback(int vibratorId, long vibrationId) {
+ public void notifyVibratorCallback(int vibratorId, long vibrationId, long stepId) {
if (vibrationId != mVibration.id) {
return;
}
synchronized (mLock) {
if (mConductor != null) {
- mConductor.notifyVibratorComplete(vibratorId);
+ mConductor.notifyVibratorComplete(vibratorId, stepId);
}
}
}
diff --git a/services/core/java/com/android/server/vibrator/VendorVibrationSession.java b/services/core/java/com/android/server/vibrator/VendorVibrationSession.java
index 64b52b1..bda3d44 100644
--- a/services/core/java/com/android/server/vibrator/VendorVibrationSession.java
+++ b/services/core/java/com/android/server/vibrator/VendorVibrationSession.java
@@ -218,8 +218,8 @@
}
@Override
- public void notifyVibratorCallback(int vibratorId, long vibrationId) {
- Slog.d(TAG, "Vibration callback received for vibration " + vibrationId
+ public void notifyVibratorCallback(int vibratorId, long vibrationId, long stepId) {
+ Slog.d(TAG, "Vibration callback received for vibration " + vibrationId + " step " + stepId
+ " on vibrator " + vibratorId + ", ignoring...");
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationSession.java b/services/core/java/com/android/server/vibrator/VibrationSession.java
index ae95a70..23715e39 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSession.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSession.java
@@ -106,7 +106,7 @@
* complete (e.g. on(), perform(), compose()). This does not mean the vibration is complete,
* since its playback might have one or more interactions with the vibrator hardware.
*/
- void notifyVibratorCallback(int vibratorId, long vibrationId);
+ void notifyVibratorCallback(int vibratorId, long vibrationId, long stepId);
/**
* Notify all synced vibrators have completed the last synchronized command during the playback
diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
index 1e20deb..36e1322 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
@@ -30,6 +30,7 @@
import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.server.vibrator.VibrationSession.Status;
@@ -93,6 +94,8 @@
private final Object mLock = new Object();
@GuardedBy("mLock")
private final IntArray mSignalVibratorsComplete;
+ @GuardedBy("mLock")
+ private final SparseIntArray mSignalVibratorStepIds;
@Nullable
@GuardedBy("mLock")
private Vibration.EndInfo mSignalCancel = null;
@@ -121,6 +124,8 @@
this.vibratorManagerHooks = vibratorManagerHooks;
this.mSignalVibratorsComplete =
new IntArray(mDeviceAdapter.getAvailableVibratorIds().length);
+ this.mSignalVibratorStepIds =
+ new SparseIntArray(mDeviceAdapter.getAvailableVibratorIds().length);
}
@Nullable
@@ -418,7 +423,7 @@
* <p>This is a lightweight method intended to be called directly via native callbacks.
* The state update is recorded for processing on the main execution thread (VibrationThread).
*/
- public void notifyVibratorComplete(int vibratorId) {
+ public void notifyVibratorComplete(int vibratorId, long stepId) {
// HAL callbacks may be triggered directly within HAL calls, so these notifications
// could be on the VibrationThread as it calls the HAL, or some other executor later.
// Therefore no thread assertion is made here.
@@ -428,6 +433,14 @@
}
synchronized (mLock) {
+ if (Flags.fixVibrationThreadCallbackHandling()
+ && mSignalVibratorStepIds.get(vibratorId) != stepId) {
+ if (DEBUG) {
+ Slog.d(TAG, "Vibrator " + vibratorId + " callback for step=" + stepId
+ + " ignored, current step=" + mSignalVibratorStepIds.get(vibratorId));
+ }
+ return;
+ }
mSignalVibratorsComplete.add(vibratorId);
mLock.notify();
}
@@ -645,6 +658,26 @@
}
}
+ /**
+ * Updates and returns the next step id value to be used in vibrator commands.
+ *
+ * <p>This new step id will be kept by this conductor to filter out old callbacks that might be
+ * triggered too late by the HAL, preventing them from affecting the ongoing vibration playback.
+ */
+ public int nextVibratorCallbackStepId(int vibratorId) {
+ if (!Flags.fixVibrationThreadCallbackHandling()) {
+ return 0;
+ }
+ if (Build.IS_DEBUGGABLE) {
+ expectIsVibrationThread(true);
+ }
+ synchronized (mLock) {
+ int stepId = mSignalVibratorStepIds.get(vibratorId) + 1;
+ mSignalVibratorStepIds.put(vibratorId, stepId);
+ return stepId;
+ }
+ }
+
private static CombinedVibration.Sequential toSequential(CombinedVibration effect) {
if (effect instanceof CombinedVibration.Sequential) {
return (CombinedVibration.Sequential) effect;
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index acb31ce..ab13b0e 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -72,7 +72,7 @@
public interface OnVibrationCompleteListener {
/** Callback triggered when an active vibration command is complete. */
- void onComplete(int vibratorId, long vibrationId);
+ void onComplete(int vibratorId, long vibrationId, long stepId);
}
/** Representation of the vibrator state based on the interactions through this controller. */
@@ -285,11 +285,11 @@
* @return The positive duration of the vibration started, if successful, zero if the vibrator
* do not support the input or a negative number if the operation failed.
*/
- public long on(long milliseconds, long vibrationId) {
+ public long on(long milliseconds, long vibrationId, long stepId) {
Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#on");
try {
synchronized (mLock) {
- long duration = mNativeWrapper.on(milliseconds, vibrationId);
+ long duration = mNativeWrapper.on(milliseconds, vibrationId, stepId);
if (duration > 0) {
mCurrentAmplitude = -1;
updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
@@ -310,7 +310,7 @@
* @return The positive duration of the vibration started, if successful, zero if the vibrator
* do not support the input or a negative number if the operation failed.
*/
- public long on(VibrationEffect.VendorEffect vendorEffect, long vibrationId) {
+ public long on(VibrationEffect.VendorEffect vendorEffect, long vibrationId, long stepId) {
Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#on (vendor)");
synchronized (mLock) {
Parcel vendorData = Parcel.obtain();
@@ -319,7 +319,7 @@
vendorData.setDataPosition(0);
long duration = mNativeWrapper.performVendorEffect(vendorData,
vendorEffect.getEffectStrength(), vendorEffect.getScale(),
- vendorEffect.getAdaptiveScale(), vibrationId);
+ vendorEffect.getAdaptiveScale(), vibrationId, stepId);
if (duration > 0) {
mCurrentAmplitude = -1;
updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
@@ -341,12 +341,12 @@
* @return The positive duration of the vibration started, if successful, zero if the vibrator
* do not support the input or a negative number if the operation failed.
*/
- public long on(PrebakedSegment prebaked, long vibrationId) {
+ public long on(PrebakedSegment prebaked, long vibrationId, long stepId) {
Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#on (Prebaked)");
try {
synchronized (mLock) {
long duration = mNativeWrapper.perform(prebaked.getEffectId(),
- prebaked.getEffectStrength(), vibrationId);
+ prebaked.getEffectStrength(), vibrationId, stepId);
if (duration > 0) {
mCurrentAmplitude = -1;
updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
@@ -367,14 +367,14 @@
* @return The positive duration of the vibration started, if successful, zero if the vibrator
* do not support the input or a negative number if the operation failed.
*/
- public long on(PrimitiveSegment[] primitives, long vibrationId) {
+ public long on(PrimitiveSegment[] primitives, long vibrationId, long stepId) {
Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#on (Primitive)");
try {
if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
return 0;
}
synchronized (mLock) {
- long duration = mNativeWrapper.compose(primitives, vibrationId);
+ long duration = mNativeWrapper.compose(primitives, vibrationId, stepId);
if (duration > 0) {
mCurrentAmplitude = -1;
updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
@@ -394,7 +394,7 @@
*
* @return The duration of the effect playing, or 0 if unsupported.
*/
- public long on(RampSegment[] primitives, long vibrationId) {
+ public long on(RampSegment[] primitives, long vibrationId, long stepId) {
Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#on (PWLE)");
try {
if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
@@ -402,7 +402,8 @@
}
synchronized (mLock) {
int braking = mVibratorInfo.getDefaultBraking();
- long duration = mNativeWrapper.composePwle(primitives, braking, vibrationId);
+ long duration = mNativeWrapper.composePwle(
+ primitives, braking, vibrationId, stepId);
if (duration > 0) {
mCurrentAmplitude = -1;
updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
@@ -422,14 +423,14 @@
*
* @return The duration of the effect playing, or 0 if unsupported.
*/
- public long on(PwlePoint[] pwlePoints, long vibrationId) {
+ public long on(PwlePoint[] pwlePoints, long vibrationId, long stepId) {
Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#on (PWLE v2)");
try {
if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2)) {
return 0;
}
synchronized (mLock) {
- long duration = mNativeWrapper.composePwleV2(pwlePoints, vibrationId);
+ long duration = mNativeWrapper.composePwleV2(pwlePoints, vibrationId, stepId);
if (duration > 0) {
mCurrentAmplitude = -1;
updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
@@ -544,26 +545,27 @@
private static native boolean isAvailable(long nativePtr);
- private static native long on(long nativePtr, long milliseconds, long vibrationId);
+ private static native long on(long nativePtr, long milliseconds, long vibrationId,
+ long stepId);
private static native void off(long nativePtr);
private static native void setAmplitude(long nativePtr, float amplitude);
private static native long performEffect(long nativePtr, long effect, long strength,
- long vibrationId);
+ long vibrationId, long stepId);
private static native long performVendorEffect(long nativePtr, Parcel vendorData,
- long strength, float scale, float adaptiveScale, long vibrationId);
+ long strength, float scale, float adaptiveScale, long vibrationId, long stepId);
private static native long performComposedEffect(long nativePtr, PrimitiveSegment[] effect,
- long vibrationId);
+ long vibrationId, long stepId);
private static native long performPwleEffect(long nativePtr, RampSegment[] effect,
- int braking, long vibrationId);
+ int braking, long vibrationId, long stepId);
private static native long performPwleV2Effect(long nativePtr, PwlePoint[] effect,
- long vibrationId);
+ long vibrationId, long stepId);
private static native void setExternalControl(long nativePtr, boolean enabled);
@@ -595,8 +597,8 @@
}
/** Turns vibrator on for given time. */
- public long on(long milliseconds, long vibrationId) {
- return on(mNativePtr, milliseconds, vibrationId);
+ public long on(long milliseconds, long vibrationId, long stepId) {
+ return on(mNativePtr, milliseconds, vibrationId, stepId);
}
/** Turns vibrator off. */
@@ -610,30 +612,31 @@
}
/** Turns vibrator on to perform one of the supported effects. */
- public long perform(long effect, long strength, long vibrationId) {
- return performEffect(mNativePtr, effect, strength, vibrationId);
+ public long perform(long effect, long strength, long vibrationId, long stepId) {
+ return performEffect(mNativePtr, effect, strength, vibrationId, stepId);
}
/** Turns vibrator on to perform a vendor-specific effect. */
public long performVendorEffect(Parcel vendorData, long strength, float scale,
- float adaptiveScale, long vibrationId) {
+ float adaptiveScale, long vibrationId, long stepId) {
return performVendorEffect(mNativePtr, vendorData, strength, scale, adaptiveScale,
- vibrationId);
+ vibrationId, stepId);
}
/** Turns vibrator on to perform effect composed of give primitives effect. */
- public long compose(PrimitiveSegment[] primitives, long vibrationId) {
- return performComposedEffect(mNativePtr, primitives, vibrationId);
+ public long compose(PrimitiveSegment[] primitives, long vibrationId, long stepId) {
+ return performComposedEffect(mNativePtr, primitives, vibrationId, stepId);
}
/** Turns vibrator on to perform PWLE effect composed of given primitives. */
- public long composePwle(RampSegment[] primitives, int braking, long vibrationId) {
- return performPwleEffect(mNativePtr, primitives, braking, vibrationId);
+ public long composePwle(RampSegment[] primitives, int braking, long vibrationId,
+ long stepId) {
+ return performPwleEffect(mNativePtr, primitives, braking, vibrationId, stepId);
}
/** Turns vibrator on to perform PWLE effect composed of given points. */
- public long composePwleV2(PwlePoint[] pwlePoints, long vibrationId) {
- return performPwleV2Effect(mNativePtr, pwlePoints, vibrationId);
+ public long composePwleV2(PwlePoint[] pwlePoints, long vibrationId, long stepId) {
+ return performPwleV2Effect(mNativePtr, pwlePoints, vibrationId, stepId);
}
/** Enabled the device vibrator to be controlled by another service. */
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 75b1b20..3f5fc33 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -1293,14 +1293,14 @@
}
}
- private void onVibrationComplete(int vibratorId, long vibrationId) {
+ private void onVibrationComplete(int vibratorId, long vibrationId, long stepId) {
synchronized (mLock) {
if (mCurrentSession != null) {
if (DEBUG) {
- Slog.d(TAG, "Vibration " + vibrationId + " on vibrator " + vibratorId
- + " complete, notifying thread");
+ Slog.d(TAG, "Vibration " + vibrationId + " step " + stepId
+ + " on vibrator " + vibratorId + " complete, notifying thread");
}
- mCurrentSession.notifyVibratorCallback(vibratorId, vibrationId);
+ mCurrentSession.notifyVibratorCallback(vibratorId, vibrationId, stepId);
}
}
}
@@ -2100,10 +2100,10 @@
}
@Override
- public void onComplete(int vibratorId, long vibrationId) {
+ public void onComplete(int vibratorId, long vibrationId, long stepId) {
VibratorManagerService service = mServiceRef.get();
if (service != null) {
- service.onVibrationComplete(vibratorId, vibrationId);
+ service.onVibrationComplete(vibratorId, vibrationId, stepId);
}
}
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
index b3e68a35..8e8455a 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
@@ -16,6 +16,8 @@
package com.android.server.wallpaper;
+import static android.app.WallpaperManager.ORIENTATION_LANDSCAPE;
+import static android.app.WallpaperManager.ORIENTATION_SQUARE_LANDSCAPE;
import static android.app.WallpaperManager.ORIENTATION_UNKNOWN;
import static android.app.WallpaperManager.getOrientation;
import static android.app.WallpaperManager.getRotatedOrientation;
@@ -28,6 +30,7 @@
import static com.android.server.wallpaper.WallpaperUtils.getWallpaperDir;
import static com.android.window.flags.Flags.multiCrop;
+import android.app.WallpaperManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageDecoder;
@@ -821,4 +824,54 @@
}
}
}
+
+ /**
+ * Returns true if a wallpaper is compatible with a given display with ID, {@code displayId}.
+ *
+ * <p>A wallpaper is compatible with a display if any of the following are true
+ * <ol>the display is a default display</o>
+ * <ol>the wallpaper is a stock wallpaper</ol>
+ * <ol>the wallpaper size is at least 3/4 of the display resolution and, in landscape displays,
+ * the wallpaper has an aspect ratio of at least 11:13.</ol>
+ */
+ @VisibleForTesting
+ boolean isWallpaperCompatibleForDisplay(int displayId, WallpaperData wallpaperData) {
+ if (displayId == DEFAULT_DISPLAY) {
+ return true;
+ }
+
+ File wallpaperFile = wallpaperData.getWallpaperFile();
+ if (!wallpaperFile.exists()) {
+ // Assumption: Stock wallpaper is suitable for all display sizes.
+ return true;
+ }
+
+ DisplayInfo displayInfo = mWallpaperDisplayHelper.getDisplayInfo(displayId);
+ Point displaySize = new Point(displayInfo.logicalWidth, displayInfo.logicalHeight);
+ int displayOrientation = WallpaperManager.getOrientation(displaySize);
+
+ Point wallpaperImageSize = new Point(
+ (int) Math.ceil(wallpaperData.cropHint.width() / wallpaperData.mSampleSize),
+ (int) Math.ceil(wallpaperData.cropHint.height() / wallpaperData.mSampleSize));
+ if (wallpaperImageSize.equals(0, 0)) {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(wallpaperFile.getAbsolutePath(), options);
+ wallpaperImageSize.set(options.outWidth, options.outHeight);
+ }
+
+ double maxDisplayToImageRatio = Math.max((double) displaySize.x / wallpaperImageSize.x,
+ (double) displaySize.y / wallpaperImageSize.y);
+ if (maxDisplayToImageRatio > 1.5) {
+ return false;
+ }
+
+ // For displays in landscape, we only support images with an aspect ratio >= 11:13
+ if (displayOrientation == ORIENTATION_LANDSCAPE) {
+ return ((double) wallpaperImageSize.x / wallpaperImageSize.y) >= 11.0 / 13;
+ }
+
+ // For other orientations, we don't enforce any aspect ratio.
+ return true;
+ }
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 69b2b9b..d620e98 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -118,7 +118,6 @@
import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;
-import android.util.ArraySet;
import android.util.EventLog;
import android.util.IntArray;
import android.util.Slog;
@@ -161,7 +160,6 @@
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
-import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -743,10 +741,8 @@
final WallpaperDisplayHelper mWallpaperDisplayHelper;
final WallpaperCropper mWallpaperCropper;
- // TODO(b/384519749): Remove this set after we introduce the aspect ratio check.
- private final Set<Integer> mWallpaperCompatibleDisplaysForTest = new ArraySet<>();
-
- private boolean isWallpaperCompatibleForDisplay(int displayId, WallpaperConnection connection) {
+ @VisibleForTesting
+ boolean isWallpaperCompatibleForDisplay(int displayId, WallpaperConnection connection) {
if (connection == null) {
return false;
}
@@ -757,26 +753,14 @@
// Image wallpaper
if (isDeviceEligibleForDesktopExperienceWallpaper(mContext)) {
- // TODO(b/384519749): check display's resolution and image wallpaper cropped image
- // aspect ratio.
- return displayId == DEFAULT_DISPLAY
- || mWallpaperCompatibleDisplaysForTest.contains(displayId);
+ return mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
+ connection.mWallpaper);
}
// When enableConnectedDisplaysWallpaper is off, we assume the image wallpaper supports all
// usable displays.
return true;
}
- @VisibleForTesting
- void addWallpaperCompatibleDisplayForTest(int displayId) {
- mWallpaperCompatibleDisplaysForTest.add(displayId);
- }
-
- @VisibleForTesting
- void removeWallpaperCompatibleDisplayForTest(int displayId) {
- mWallpaperCompatibleDisplaysForTest.remove(displayId);
- }
-
private void updateFallbackConnection(int clientUid) {
if (mLastWallpaper == null || mFallbackWallpaper == null) return;
final WallpaperConnection systemConnection = mLastWallpaper.connection;
@@ -844,7 +828,7 @@
// `which` flag.
DisplayConnector fallbackConnector =
mFallbackWallpaper.connection.getDisplayConnectorOrCreate(displayId);
- if (fallbackConnector != null && fallbackConnector.mEngine != null) {
+ if (fallbackConnector != null) {
fallbackConnector.mWhich = which;
fallbackConnector.connectLocked(mFallbackWallpaper.connection,
mFallbackWallpaper);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 9692b69..7b6d408f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -109,7 +109,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED;
import static android.view.WindowManager.PROPERTY_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING_STATE_SHARING;
-import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_UNSET;
import static android.view.WindowManager.TRANSIT_RELAUNCH;
import static android.view.WindowManager.hasWindowExtensionsEnabled;
@@ -321,9 +320,7 @@
import android.util.Slog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
-import android.view.AppTransitionAnimationSpec;
import android.view.DisplayInfo;
-import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.InputApplicationHandle;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
@@ -493,6 +490,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.
@@ -2348,7 +2346,8 @@
// The snapshot of home is only used once because it won't be updated while screen
// is on (see {@link TaskSnapshotController#screenTurningOff}).
mWmService.mTaskSnapshotController.removeSnapshotCache(task.mTaskId);
- if ((mDisplayContent.mAppTransition.getTransitFlags()
+ final Transition transition = mTransitionController.getCollectingTransition();
+ if (transition != null && (transition.getFlags()
& WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0) {
// Only use snapshot of home as starting window when unlocking directly.
return false;
@@ -3637,7 +3636,6 @@
if (DEBUG_VISIBILITY || DEBUG_TRANSITION) {
Slog.v(TAG_TRANSITION, "Prepare close transition: finishing " + this);
}
- mDisplayContent.prepareAppTransition(TRANSIT_CLOSE);
// When finishing the activity preemptively take the snapshot before the app window
// is marked as hidden and any configuration changes take place
@@ -3739,7 +3737,6 @@
private void prepareActivityHideTransitionAnimation() {
final DisplayContent dc = mDisplayContent;
- dc.prepareAppTransition(TRANSIT_CLOSE);
setVisibility(false);
dc.executeAppTransition();
}
@@ -4391,13 +4388,6 @@
removeStartingWindow();
}
- // If app transition animation was running for this activity, then we need to ensure that
- // the app transition notifies that animations have completed in
- // DisplayContent.handleAnimatingStoppedAndTransition(), so add to that list now
- if (isAnimating(TRANSITION | PARENTS, ANIMATION_TYPE_APP_TRANSITION)) {
- getDisplayContent().mNoAnimationNotifyOnTransitionFinished.add(token);
- }
-
if (delayed && !isEmpty()) {
// set the token aside because it has an active animation to be finished
ProtoLog.v(WM_DEBUG_ADD_REMOVE,
@@ -5069,8 +5059,6 @@
void applyOptionsAnimation() {
if (DEBUG_TRANSITION) Slog.i(TAG, "Applying options for " + this);
if (mPendingRemoteAnimation != null) {
- mDisplayContent.mAppTransition.overridePendingAppTransitionRemote(
- mPendingRemoteAnimation);
mTransitionController.setStatusBarTransitionDelay(
mPendingRemoteAnimation.getStatusBarTransitionDelay());
} else {
@@ -5100,14 +5088,6 @@
IRemoteCallback finishCallback = null;
switch (animationType) {
case ANIM_CUSTOM:
- displayContent.mAppTransition.overridePendingAppTransition(
- pendingOptions.getPackageName(),
- pendingOptions.getCustomEnterResId(),
- pendingOptions.getCustomExitResId(),
- pendingOptions.getCustomBackgroundColor(),
- pendingOptions.getAnimationStartedListener(),
- pendingOptions.getAnimationFinishedListener(),
- pendingOptions.getOverrideTaskTransition());
options = AnimationOptions.makeCustomAnimOptions(pendingOptions.getPackageName(),
pendingOptions.getCustomEnterResId(), pendingOptions.getCustomExitResId(),
pendingOptions.getCustomBackgroundColor(),
@@ -5116,9 +5096,6 @@
finishCallback = pendingOptions.getAnimationFinishedListener();
break;
case ANIM_CLIP_REVEAL:
- displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
- pendingOptions.getStartX(), pendingOptions.getStartY(),
- pendingOptions.getWidth(), pendingOptions.getHeight());
options = AnimationOptions.makeClipRevealAnimOptions(
pendingOptions.getStartX(), pendingOptions.getStartY(),
pendingOptions.getWidth(), pendingOptions.getHeight());
@@ -5130,9 +5107,6 @@
}
break;
case ANIM_SCALE_UP:
- displayContent.mAppTransition.overridePendingAppTransitionScaleUp(
- pendingOptions.getStartX(), pendingOptions.getStartY(),
- pendingOptions.getWidth(), pendingOptions.getHeight());
options = AnimationOptions.makeScaleUpAnimOptions(
pendingOptions.getStartX(), pendingOptions.getStartY(),
pendingOptions.getWidth(), pendingOptions.getHeight(),
@@ -5148,10 +5122,6 @@
case ANIM_THUMBNAIL_SCALE_DOWN:
final boolean scaleUp = (animationType == ANIM_THUMBNAIL_SCALE_UP);
final HardwareBuffer buffer = pendingOptions.getThumbnail();
- displayContent.mAppTransition.overridePendingAppTransitionThumb(buffer,
- pendingOptions.getStartX(), pendingOptions.getStartY(),
- pendingOptions.getAnimationStartedListener(),
- scaleUp);
options = AnimationOptions.makeThumbnailAnimOptions(buffer,
pendingOptions.getStartX(), pendingOptions.getStartY(), scaleUp);
startCallback = pendingOptions.getAnimationStartedListener();
@@ -5164,36 +5134,9 @@
break;
case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
- final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs();
- final IAppTransitionAnimationSpecsFuture specsFuture =
- pendingOptions.getSpecsFuture();
- if (specsFuture != null) {
- displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture(
- specsFuture, pendingOptions.getAnimationStartedListener(),
- animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP);
- } else if (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_DOWN
- && specs != null) {
- displayContent.mAppTransition.overridePendingAppTransitionMultiThumb(
- specs, pendingOptions.getAnimationStartedListener(),
- pendingOptions.getAnimationFinishedListener(), false);
- } else {
- displayContent.mAppTransition.overridePendingAppTransitionAspectScaledThumb(
- pendingOptions.getThumbnail(),
- pendingOptions.getStartX(), pendingOptions.getStartY(),
- pendingOptions.getWidth(), pendingOptions.getHeight(),
- pendingOptions.getAnimationStartedListener(),
- (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP));
- if (intent.getSourceBounds() == null) {
- intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
- pendingOptions.getStartY(),
- pendingOptions.getStartX() + pendingOptions.getWidth(),
- pendingOptions.getStartY() + pendingOptions.getHeight()));
- }
- }
+ // TODO(b/397847511): remove the related types from ActivityOptions.
break;
case ANIM_OPEN_CROSS_PROFILE_APPS:
- displayContent.mAppTransition
- .overridePendingAppTransitionStartCrossProfileApps();
options = AnimationOptions.makeCrossProfileAnimOptions();
break;
case ANIM_NONE:
@@ -5450,8 +5393,6 @@
}
private void setVisibility(boolean visible, boolean deferHidingClient) {
- final AppTransition appTransition = getDisplayContent().mAppTransition;
-
// Don't set visibility to false if we were already not visible. This prevents WM from
// adding the app to the closing app list which doesn't make sense for something that is
// already not visible. However, set visibility to true even if we are already visible.
@@ -5471,8 +5412,8 @@
}
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "setAppVisibility(%s, visible=%b): %s visible=%b mVisibleRequested=%b Callers=%s",
- token, visible, appTransition, isVisible(), mVisibleRequested,
+ "setAppVisibility(%s, visible=%b): visible=%b mVisibleRequested=%b Callers=%s",
+ token, visible, isVisible(), mVisibleRequested,
Debug.getCallers(6));
// Before setting mVisibleRequested so we can track changes.
@@ -5569,15 +5510,6 @@
updateReportedVisibilityLocked();
}
- @Override
- boolean applyAnimation(LayoutParams lp, @TransitionOldType int transit, boolean enter,
- boolean isVoiceInteraction, @Nullable ArrayList<WindowContainer> sources) {
- if ((mTransitionChangeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
- return false;
- }
- return super.applyAnimation(lp, transit, enter, isVoiceInteraction, sources);
- }
-
/**
* Update visibility to this {@link ActivityRecord}.
*
@@ -6447,6 +6379,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 +6413,7 @@
if (isStopping) {
ProtoLog.v(WM_DEBUG_STATES, "Moving to STOPPED: %s (stop complete)", this);
+ mStoppedTime = SystemClock.uptimeMillis();
setState(STOPPED, "activityStopped");
}
@@ -6639,9 +6573,7 @@
// starting window is drawn, the transition can start earlier. Exclude finishing and bubble
// because it may be a trampoline.
if (app == null && !finishing && !mLaunchedFromBubble
- && mVisibleRequested && !mDisplayContent.mAppTransition.isReady()
- && !mDisplayContent.mAppTransition.isRunning()
- && mDisplayContent.isNextTransitionForward()) {
+ && mVisibleRequested && mDisplayContent.isNextTransitionForward()) {
// The pending transition state will be cleared after the transition is started, so
// save the state for launching the client later (used by LaunchActivityItem).
mStartingData.mIsTransitionForward = true;
@@ -7523,7 +7455,6 @@
}
}
- getDisplayContent().mAppTransition.notifyAppTransitionFinishedLocked(token);
scheduleAnimation();
// Schedule to handle the stopping and finishing activities which the animation is done
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/AppCompatAspectRatioPolicy.java b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
index 15c0789..3535a96 100644
--- a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
@@ -36,8 +36,7 @@
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
-
-import com.android.window.flags.Flags;
+import android.window.DesktopModeFlags;
/**
* Encapsulate app compat policy logic related to aspect ratio.
@@ -298,7 +297,8 @@
// Camera compat mode is an exception to this, where the activity is letterboxed
// to an aspect ratio commonly found on phones, e.g. 16:9, to avoid issues like
// stretching of the camera preview.
- || (Flags.ignoreAspectRatioRestrictionsForResizeableFreeformActivities()
+ || (DesktopModeFlags
+ .IGNORE_ASPECT_RATIO_RESTRICTIONS_FOR_RESIZEABLE_FREEFORM_ACTIVITIES.isTrue()
&& task.getWindowingMode() == WINDOWING_MODE_FREEFORM
&& !mActivityRecord.shouldCreateAppCompatDisplayInsets()
&& !AppCompatCameraPolicy.shouldCameraCompatControlAspectRatio(
diff --git a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
index 47d30c9..5eed5470 100644
--- a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
@@ -23,6 +23,7 @@
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA;
import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA;
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION;
+import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION;
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH;
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
@@ -32,6 +33,7 @@
import static com.android.server.wm.AppCompatUtils.isChangeEnabled;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import com.android.server.wm.utils.OptPropFactory;
import com.android.window.flags.Flags;
@@ -60,6 +62,8 @@
private final OptPropFactory.OptProp mCameraCompatEnableRefreshViaPauseOptProp;
@NonNull
private final OptPropFactory.OptProp mCameraCompatAllowForceRotationOptProp;
+ @Nullable
+ private final OptPropFactory.OptProp mCameraCompatAllowOrientationTreatmentOptProp;
AppCompatCameraOverrides(@NonNull ActivityRecord activityRecord,
@NonNull AppCompatConfiguration appCompatConfiguration,
@@ -80,6 +84,10 @@
mCameraCompatAllowForceRotationOptProp = optPropBuilder.create(
PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION,
isCameraCompatTreatmentEnabled);
+ mCameraCompatAllowOrientationTreatmentOptProp =
+ Flags.enableCameraCompatForDesktopWindowingOptOut() ? optPropBuilder.create(
+ PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION,
+ isCameraCompatTreatmentEnabled) : null;
}
/**
@@ -168,10 +176,31 @@
* </ul>
*/
boolean shouldApplyFreeformTreatmentForCameraCompat() {
- return Flags.enableCameraCompatForDesktopWindowing() && (isChangeEnabled(mActivityRecord,
- OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT)
- || mActivityRecord.mWmService.mAppCompatConfiguration
- .isCameraCompatFreeformWindowingTreatmentEnabled());
+ return Flags.enableCameraCompatForDesktopWindowing()
+ && (shouldEnableCameraCompatFreeformTreatmentForApp()
+ || shouldEnableCameraCompatFreeformTreatmentForAllApps());
+ }
+
+ private boolean shouldEnableCameraCompatFreeformTreatmentForApp() {
+ if (mCameraCompatAllowOrientationTreatmentOptProp != null) {
+ return mCameraCompatAllowOrientationTreatmentOptProp
+ .shouldEnableWithOptOutOverrideAndProperty(isChangeEnabled(mActivityRecord,
+ OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT));
+ } else {
+ return isChangeEnabled(mActivityRecord,
+ OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT);
+ }
+ }
+
+ /**
+ * Returns whether camera compat treatment should be enabled for all apps targeted for treatment
+ * (usually fixed-orientation apps).
+ *
+ * <p>This can be enabled via adb only.
+ */
+ private boolean shouldEnableCameraCompatFreeformTreatmentForAllApps() {
+ return mActivityRecord.mWmService.mAppCompatConfiguration
+ .isCameraCompatFreeformWindowingTreatmentEnabled();
}
boolean isOverrideOrientationOnlyForCameraEnabled() {
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index b932ef3..6718ae4 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -748,14 +748,9 @@
&& policy.okToAnimate(true /* ignoreScreenOn */)) {
return false;
}
- // Consider unoccluding only when all unknown visibilities have been
- // resolved, as otherwise we just may be starting another occluding activity.
- final boolean isUnoccluding =
- mDisplayContent.mAppTransition.isUnoccluding()
- && mDisplayContent.mUnknownAppVisibilityController.allResolved();
- // If keyguard is showing, or we're unoccluding, force the keyguard's orientation,
+ // Use keyguard's orientation if it is showing and not occluded
// even if SystemUI hasn't updated the attrs yet.
- if (policy.isKeyguardShowingAndNotOccluded() || isUnoccluding) {
+ if (policy.isKeyguardShowingAndNotOccluded()) {
return true;
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e30c24d..214a3a1 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -88,7 +88,6 @@
import static android.window.DisplayAreaOrganizer.FEATURE_IME;
import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
-import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_BOOT;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_CONTENT_RECORDING;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_FOCUS;
@@ -369,12 +368,6 @@
private MetricsLogger mMetricsLogger;
- /**
- * List of clients without a transtiton animation that we notify once we are done
- * transitioning since they won't be notified through the app window animator.
- */
- final List<IBinder> mNoAnimationNotifyOnTransitionFinished = new ArrayList<>();
-
// Mapping from a token IBinder to a WindowToken object on this display.
private final HashMap<IBinder, WindowToken> mTokenMap = new HashMap();
@@ -829,28 +822,36 @@
*/
private final ToBooleanFunction<WindowState> mFindFocusedWindow = w -> {
final ActivityRecord focusedApp = mFocusedApp;
+ final boolean canReceiveKeys = w.canReceiveKeys();
ProtoLog.v(WM_DEBUG_FOCUS, "Looking for focus: %s, flags=%d, canReceive=%b, reason=%s",
- w, w.mAttrs.flags, w.canReceiveKeys(),
+ w, w.mAttrs.flags, canReceiveKeys,
w.canReceiveKeysReason(false /* fromUserTouch */));
- if (!w.canReceiveKeys()) {
+ if (!canReceiveKeys) {
return false;
}
- // When switching the app task, we keep the IME window visibility for better
- // transitioning experiences.
- // However, in case IME created a child window or the IME selection dialog without
- // dismissing during the task switching to keep the window focus because IME window has
- // higher window hierarchy, we don't give it focus if the next IME layering target
- // doesn't request IME visible.
- if (w.mIsImWindow && w.isChildWindow() && (mImeLayeringTarget == null
- || !mImeLayeringTarget.isRequestedVisible(ime()))) {
- return false;
- }
- if (w.mAttrs.type == TYPE_INPUT_METHOD_DIALOG && mImeLayeringTarget != null
- && !(mImeLayeringTarget.isRequestedVisible(ime())
- && mImeLayeringTarget.isVisibleRequested())) {
- return false;
+ // IME windows remain visibleRequested while switching apps to maintain a smooth animation.
+ // This persists until the new app is focused, so they can be visibleRequested despite not
+ // being visible to the user (i.e. occluded). These rank higher in the window hierarchy than
+ // app windows, so they will always be considered first. To avoid having the focus stuck,
+ // an IME window (child or not) cannot be focused if the IME parent is not visible. However,
+ // child windows also require the IME to be visible in the current app.
+ if (w.mIsImWindow) {
+ final boolean imeParentVisible = mInputMethodSurfaceParentWindow != null
+ && mInputMethodSurfaceParentWindow.isVisibleRequested();
+ if (!imeParentVisible) {
+ ProtoLog.v(WM_DEBUG_FOCUS, "findFocusedWindow: IME window not focusable as"
+ + " IME parent is not visible");
+ return false;
+ }
+
+ if (w.isChildWindow()
+ && !getInsetsStateController().getImeSourceProvider().isImeShowing()) {
+ ProtoLog.v(WM_DEBUG_FOCUS, "findFocusedWindow: IME child window not focusable as"
+ + " IME is not visible");
+ return false;
+ }
}
final ActivityRecord activity = w.mActivityRecord;
@@ -1171,8 +1172,6 @@
mFixedRotationTransitionListener = new FixedRotationTransitionListener(mDisplayId);
mAppTransition = new AppTransition(mWmService.mContext, mWmService, this);
- mAppTransition.registerListenerLocked(mWmService.mActivityManagerAppTransitionNotifier);
- mAppTransition.registerListenerLocked(mFixedRotationTransitionListener);
mTransitionController.registerLegacyListener(mFixedRotationTransitionListener);
mUnknownAppVisibilityController = new UnknownAppVisibilityController(mWmService, this);
mRemoteDisplayChangeController = new RemoteDisplayChangeController(this);
@@ -2858,13 +2857,6 @@
return isVisible() && !mRemoved && !mRemoving;
}
- @Override
- void onAppTransitionDone() {
- super.onAppTransitionDone();
- mWmService.mWindowsChanged = true;
- onTransitionFinished();
- }
-
void onTransitionFinished() {
// If the transition finished callback cannot match the token for some reason, make sure the
// rotated state is cleared if it is already invisible.
@@ -3385,9 +3377,7 @@
mDeferredRemoval = false;
try {
mUnknownAppVisibilityController.clear();
- mAppTransition.removeAppTransitionTimeoutCallbacks();
mTransitionController.unregisterLegacyListener(mFixedRotationTransitionListener);
- handleAnimatingStoppedAndTransition();
mDeviceStateController.unregisterDeviceStateCallback(mDeviceStateConsumer);
super.removeImmediately();
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
@@ -3570,11 +3560,7 @@
mDisplayRotation.dumpDebug(proto, DISPLAY_ROTATION);
mDisplayFrames.dumpDebug(proto, DISPLAY_FRAMES);
proto.write(MIN_SIZE_OF_RESIZEABLE_TASK_DP, mMinSizeOfResizeableTaskDp);
- if (mTransitionController.isShellTransitionsEnabled()) {
- mTransitionController.dumpDebugLegacy(proto, APP_TRANSITION);
- } else {
- mAppTransition.dumpDebug(proto, APP_TRANSITION);
- }
+ mTransitionController.dumpDebugLegacy(proto, APP_TRANSITION);
if (mFocusedApp != null) {
mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
}
@@ -5634,61 +5620,20 @@
* @see AppTransition#prepareAppTransition
*/
void requestTransitionAndLegacyPrepare(@WindowManager.TransitionType int transit,
- @WindowManager.TransitionFlags int flags) {
- prepareAppTransition(transit, flags);
- mTransitionController.requestTransitionIfNeeded(transit, flags, null /* trigger */, this);
+ @WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger) {
+ mTransitionController.requestTransitionIfNeeded(transit, flags, trigger, this);
}
void executeAppTransition() {
mTransitionController.setReady(this);
- if (mAppTransition.isTransitionSet()) {
- ProtoLog.w(WM_DEBUG_APP_TRANSITIONS,
- "Execute app transition: %s, displayId: %d Callers=%s",
- mAppTransition, mDisplayId, Debug.getCallers(5));
- mAppTransition.setReady();
- mWmService.mWindowPlacerLocked.requestTraversal();
- }
- }
-
- /**
- * Update pendingLayoutChanges after app transition has finished.
- */
- void handleAnimatingStoppedAndTransition() {
- int changes = 0;
-
- mAppTransition.setIdle();
-
- for (int i = mNoAnimationNotifyOnTransitionFinished.size() - 1; i >= 0; i--) {
- final IBinder token = mNoAnimationNotifyOnTransitionFinished.get(i);
- mAppTransition.notifyAppTransitionFinishedLocked(token);
- }
- mNoAnimationNotifyOnTransitionFinished.clear();
-
- mWallpaperController.hideDeferredWallpapersIfNeededLegacy();
-
- onAppTransitionDone();
-
- changes |= FINISH_LAYOUT_REDO_LAYOUT;
- ProtoLog.v(WM_DEBUG_WALLPAPER, "Wallpaper layer changed: assigning layers + relayout");
- computeImeTarget(true /* updateImeTarget */);
- mWallpaperMayChange = true;
- // Since the window list has been rebuilt, focus might have to be recomputed since the
- // actual order of windows might have changed again.
- mWmService.mFocusMayChange = true;
-
- pendingLayoutChanges |= changes;
}
/** Check if pending app transition is for activity / task launch. */
boolean isNextTransitionForward() {
// TODO(b/191375840): decouple "forwardness" from transition system.
- if (mTransitionController.isShellTransitionsEnabled()) {
- @WindowManager.TransitionType int type =
- mTransitionController.getCollectingTransitionType();
- return type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT;
- }
- return mAppTransition.containsTransitRequest(TRANSIT_OPEN)
- || mAppTransition.containsTransitRequest(TRANSIT_TO_FRONT);
+ final @WindowManager.TransitionType int type =
+ mTransitionController.getCollectingTransitionType();
+ return type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT;
}
/**
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index fbe8501..7aa2101 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -631,7 +631,6 @@
mHandler.post(mAppTransitionFinished);
}
};
- displayContent.mAppTransition.registerListenerLocked(mAppTransitionListener);
displayContent.mTransitionController.registerLegacyListener(mAppTransitionListener);
// TODO: Make it can take screenshot on external display
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 6091b83..6d73739 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -250,7 +250,7 @@
// to the locked state before holding the sleep token again
if (!ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS) {
dc.requestTransitionAndLegacyPrepare(
- TRANSIT_TO_FRONT, TRANSIT_FLAG_KEYGUARD_APPEARING);
+ TRANSIT_TO_FRONT, TRANSIT_FLAG_KEYGUARD_APPEARING, /* trigger= */ null);
}
dc.mWallpaperController.adjustWallpaperWindows();
dc.executeAppTransition();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 40f16c1..f309372 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2103,10 +2103,6 @@
removeRootTasksInWindowingModes(WINDOWING_MODE_PINNED);
}
- // Set a transition to ensure that we don't immediately try and update the visibility
- // of the activity entering PIP
- r.getDisplayContent().prepareAppTransition(TRANSIT_NONE);
-
transitionController.collect(task);
// Defer the windowing mode change until after the transition to prevent the activity
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 6b3499a..6cd1336 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -54,7 +54,6 @@
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
-import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -504,6 +503,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.
*/
@@ -1647,8 +1657,7 @@
// Prevent the transition from being executed too early if the top activity is
// resumed but the mVisibleRequested of any other activity is true, the transition
// should wait until next activity resumed.
- if (r.isState(RESUMED) || (r.isVisible()
- && !mDisplayContent.mAppTransition.containsTransitRequest(TRANSIT_CLOSE))) {
+ if (r.isState(RESUMED) || r.isVisible()) {
r.finishIfPossible(reason, false /* oomAdj */);
} else {
r.destroyIfPossible(reason);
@@ -2324,11 +2333,6 @@
mLastSurfaceSize.set(width, height);
}
- @VisibleForTesting
- boolean isInChangeTransition() {
- return AppTransition.isChangeTransitOld(mTransit);
- }
-
@Override
void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
@@ -3854,6 +3858,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);
@@ -5293,11 +5298,9 @@
// Place a new activity at top of root task, so it is next to interact with the user.
if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- mDisplayContent.prepareAppTransition(TRANSIT_NONE);
mTaskSupervisor.mNoAnimActivities.add(r);
mTransitionController.setNoAnimation(r);
} else {
- mDisplayContent.prepareAppTransition(TRANSIT_OPEN);
mTaskSupervisor.mNoAnimActivities.remove(r);
}
if (newTask && !r.mLaunchTaskBehind) {
@@ -5477,7 +5480,8 @@
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
Task finishedTask = r.getTask();
- mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED);
+ mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED,
+ finishedTask);
r.finishIfPossible(reason, false /* oomAdj */);
// Also terminate any activities below it that aren't yet stopped, to avoid a situation
@@ -5695,7 +5699,6 @@
ActivityOptions.abort(options);
}
}
- mDisplayContent.prepareAppTransition(transit);
}
final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,
@@ -5747,12 +5750,9 @@
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
if (noAnimation) {
- mDisplayContent.prepareAppTransition(TRANSIT_NONE);
mTaskSupervisor.mNoAnimActivities.add(top);
- if (mTransitionController.isShellTransitionsEnabled()) {
- mTransitionController.collect(top);
- mTransitionController.setNoAnimation(top);
- }
+ mTransitionController.collect(top);
+ mTransitionController.setNoAnimation(top);
ActivityOptions.abort(options);
} else {
updateTransitLocked(TRANSIT_TO_FRONT, options);
@@ -5862,10 +5862,6 @@
moveTaskToBackInner(tr, transition);
});
} else {
- // Skip the transition for pinned task.
- if (!inPinnedWindowingMode()) {
- mDisplayContent.prepareAppTransition(TRANSIT_TO_BACK);
- }
moveTaskToBackInner(tr, null /* transition */);
}
return true;
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index bbda849..80095b3 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -44,10 +44,6 @@
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
-import static android.view.WindowManager.TRANSIT_CLOSE;
-import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
-import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_OPEN;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_STATES;
import static com.android.server.wm.ActivityRecord.State.PAUSED;
@@ -56,7 +52,6 @@
import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
@@ -1682,36 +1677,15 @@
final DisplayContent dc = taskDisplayArea.mDisplayContent;
if (prev != null) {
if (prev.finishing) {
- if (DEBUG_TRANSITION) {
- Slog.v(TAG_TRANSITION, "Prepare close transition: prev=" + prev);
- }
if (mTaskSupervisor.mNoAnimActivities.contains(prev)) {
anim = false;
- dc.prepareAppTransition(TRANSIT_NONE);
- } else {
- dc.prepareAppTransition(TRANSIT_CLOSE);
}
prev.setVisibility(false);
- } else {
- if (DEBUG_TRANSITION) {
- Slog.v(TAG_TRANSITION, "Prepare open transition: prev=" + prev);
- }
- if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
- anim = false;
- dc.prepareAppTransition(TRANSIT_NONE);
- } else {
- dc.prepareAppTransition(TRANSIT_OPEN,
- next.mLaunchTaskBehind ? TRANSIT_FLAG_OPEN_BEHIND : 0);
- }
- }
- } else {
- if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
- if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
+ } else if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
anim = false;
- dc.prepareAppTransition(TRANSIT_NONE);
- } else {
- dc.prepareAppTransition(TRANSIT_OPEN);
}
+ } else if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
+ anim = false;
}
if (anim) {
@@ -1915,10 +1889,12 @@
// Even if the transient activity is occluded, defer pausing (addToStopping will still
// be called) it until the transient transition is done. So the current resuming
// activity won't need to wait for additional pause complete.
+ ProtoLog.d(WM_DEBUG_STATES, "startPausing: Skipping pause for transient "
+ + "resumed activity=%s", mResumedActivity);
return false;
}
- ProtoLog.d(WM_DEBUG_STATES, "startPausing: taskFrag =%s " + "mResumedActivity=%s", this,
+ ProtoLog.d(WM_DEBUG_STATES, "startPausing: taskFrag=%s mResumedActivity=%s", this,
mResumedActivity);
if (mPausingActivity != null) {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 5217a75..fd7d96a 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1640,6 +1640,12 @@
mSyncEngine.setSyncMethod(syncId, BLASTSyncEngine.METHOD_BLAST);
}
mSyncEngine.addToSyncSet(syncId, target);
+ } else {
+ // If there is an existing sync group for the commit-at-end activity,
+ // enforce BLAST sync method for its windows, before resuming config dispatch.
+ target.forAllWindows(windowState -> {
+ windowState.mSyncMethodOverride = BLASTSyncEngine.METHOD_BLAST;
+ }, true /* traverseTopToBottom */);
}
// Reset surface state here (since it was skipped in buildFinishTransaction). Since
// we are resuming config to the "current" state, we have to calculate the matching
@@ -2026,23 +2032,18 @@
if (mOverrideOptions == null) {
return;
}
-
- if (!Flags.moveAnimationOptionsToChange()) {
- info.setAnimationOptions(mOverrideOptions);
- } else {
- final List<TransitionInfo.Change> changes = info.getChanges();
- for (int i = changes.size() - 1; i >= 0; --i) {
- final WindowContainer<?> container = mTargets.get(i).mContainer;
- if (container.asActivityRecord() != null
- || shouldApplyAnimOptionsToTask(container.asTask())) {
- changes.get(i).setAnimationOptions(mOverrideOptions);
- // TODO(b/295805497): Extract mBackgroundColor from AnimationOptions.
- changes.get(i).setBackgroundColor(mOverrideOptions.getBackgroundColor());
- } else if (shouldApplyAnimOptionsToEmbeddedTf(container.asTaskFragment())) {
- // We only override AnimationOptions because backgroundColor should be from
- // TaskFragmentAnimationParams.
- changes.get(i).setAnimationOptions(mOverrideOptions);
- }
+ final List<TransitionInfo.Change> changes = info.getChanges();
+ for (int i = changes.size() - 1; i >= 0; --i) {
+ final WindowContainer<?> container = mTargets.get(i).mContainer;
+ if (container.asActivityRecord() != null
+ || shouldApplyAnimOptionsToTask(container.asTask())) {
+ changes.get(i).setAnimationOptions(mOverrideOptions);
+ // TODO(b/295805497): Extract mBackgroundColor from AnimationOptions.
+ changes.get(i).setBackgroundColor(mOverrideOptions.getBackgroundColor());
+ } else if (shouldApplyAnimOptionsToEmbeddedTf(container.asTaskFragment())) {
+ // We only override AnimationOptions because backgroundColor should be from
+ // TaskFragmentAnimationParams.
+ changes.get(i).setAnimationOptions(mOverrideOptions);
}
}
updateActivityTargetForCrossProfileAnimation(info);
@@ -2933,9 +2934,6 @@
final AnimationOptions animOptionsForActivityTransition =
calculateAnimationOptionsForActivityTransition(type, sortedTargets);
- if (!Flags.moveAnimationOptionsToChange() && animOptionsForActivityTransition != null) {
- out.setAnimationOptions(animOptionsForActivityTransition);
- }
final ArraySet<WindowContainer> occludedAtEndContainers = new ArraySet<>();
// Convert all the resolved ChangeInfos into TransactionInfo.Change objects in order.
@@ -3059,27 +3057,25 @@
}
AnimationOptions animOptions = null;
- if (Flags.moveAnimationOptionsToChange()) {
- if (activityRecord != null && animOptionsForActivityTransition != null) {
- animOptions = animOptionsForActivityTransition;
- } else if (Flags.activityEmbeddingOverlayPresentationFlag()
- && isEmbeddedTaskFragment) {
- final TaskFragmentAnimationParams params = taskFragment.getAnimationParams();
- if (params.hasOverrideAnimation()) {
- // Only set AnimationOptions if there's any animation override.
- // We use separated field for backgroundColor, and
- // AnimationOptions#backgroundColor will be removed in long term.
- animOptions = AnimationOptions.makeCustomAnimOptions(
- taskFragment.getTask().getBasePackageName(),
- params.getOpenAnimationResId(), params.getChangeAnimationResId(),
- params.getCloseAnimationResId(), 0 /* backgroundColor */,
- false /* overrideTaskTransition */);
- animOptions.setUserId(taskFragment.getTask().mUserId);
- }
+ if (activityRecord != null && animOptionsForActivityTransition != null) {
+ animOptions = animOptionsForActivityTransition;
+ } else if (Flags.activityEmbeddingOverlayPresentationFlag()
+ && isEmbeddedTaskFragment) {
+ final TaskFragmentAnimationParams params = taskFragment.getAnimationParams();
+ if (params.hasOverrideAnimation()) {
+ // Only set AnimationOptions if there's any animation override.
+ // We use separated field for backgroundColor, and
+ // AnimationOptions#backgroundColor will be removed in long term.
+ animOptions = AnimationOptions.makeCustomAnimOptions(
+ taskFragment.getTask().getBasePackageName(),
+ params.getOpenAnimationResId(), params.getChangeAnimationResId(),
+ params.getCloseAnimationResId(), 0 /* backgroundColor */,
+ false /* overrideTaskTransition */);
+ animOptions.setUserId(taskFragment.getTask().mUserId);
}
- if (animOptions != null) {
- change.setAnimationOptions(animOptions);
- }
+ }
+ if (animOptions != null) {
+ change.setAnimationOptions(animOptions);
}
if (activityRecord != null) {
diff --git a/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java b/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java
index 2428836..e612d8e 100644
--- a/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java
+++ b/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java
@@ -20,6 +20,7 @@
import static android.graphics.Matrix.MSCALE_Y;
import static android.graphics.Matrix.MSKEW_X;
import static android.graphics.Matrix.MSKEW_Y;
+import static android.view.Display.INVALID_DISPLAY;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_TPL;
@@ -35,6 +36,7 @@
import android.util.IntArray;
import android.util.Pair;
import android.util.Size;
+import android.util.SparseArray;
import android.view.InputWindowHandle;
import android.window.ITrustedPresentationListener;
import android.window.TrustedPresentationThresholds;
@@ -251,7 +253,7 @@
Rect tmpLogicalDisplaySize = new Rect();
Matrix tmpInverseMatrix = new Matrix();
float[] tmpMatrix = new float[9];
- Region coveredRegionsAbove = new Region();
+ SparseArray<Region> coveredRegionsAboveByDisplay = new SparseArray<>();
long currTimeMs = System.currentTimeMillis();
ProtoLog.v(WM_DEBUG_TPL, "Checking %d windows", mLastWindowHandles.first.length);
@@ -262,7 +264,7 @@
ProtoLog.v(WM_DEBUG_TPL, "Skipping %s", windowHandle.name);
continue;
}
- var displayFound = false;
+ int displayId = INVALID_DISPLAY;
tmpRectF.set(windowHandle.frame);
for (var displayHandle : mLastWindowHandles.second) {
if (displayHandle.mDisplayId == windowHandle.displayId) {
@@ -273,17 +275,18 @@
tmpLogicalDisplaySize.set(0, 0, displayHandle.mLogicalSize.getWidth(),
displayHandle.mLogicalSize.getHeight());
tmpRect.intersect(tmpLogicalDisplaySize);
- displayFound = true;
+ displayId = displayHandle.mDisplayId;
break;
}
}
- if (!displayFound) {
+ if (displayId == INVALID_DISPLAY) {
ProtoLog.v(WM_DEBUG_TPL, "Skipping %s, no associated display %d", windowHandle.name,
windowHandle.displayId);
continue;
}
+ Region coveredRegionsAbove = coveredRegionsAboveByDisplay.get(displayId, new Region());
var listeners = mRegisteredListeners.get(windowHandle.getWindowToken());
if (listeners != null) {
Region region = new Region();
@@ -304,6 +307,7 @@
}
coveredRegionsAbove.op(tmpRect, Region.Op.UNION);
+ coveredRegionsAboveByDisplay.put(displayId, coveredRegionsAbove);
ProtoLog.v(WM_DEBUG_TPL, "coveredRegionsAbove updated with %s frame:%s region:%s",
windowHandle.name, tmpRect.toShortString(), coveredRegionsAbove);
}
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 7c88abc..9506ffe 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -217,8 +217,7 @@
}
// If in a transition, defer commits for activities that are going invisible
- if (!visible && (mTransitionController.inTransition()
- || getDisplayContent().mAppTransition.isRunning())) {
+ if (!visible && mTransitionController.inTransition()) {
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 45202a2..d0d2067 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -16,9 +16,6 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
@@ -29,24 +26,15 @@
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
-import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.os.UserHandle.USER_NULL;
import static android.view.SurfaceControl.Transaction;
import static android.view.WindowInsets.Type.InsetsType;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
-import static android.window.TaskFragmentAnimationParams.DEFAULT_ANIMATION_BACKGROUND_COLOR;
import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ANIM;
-import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_APP_TRANSITIONS;
-import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_APP_TRANSITIONS_ANIM;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ORIENTATION;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_SYNC_ENGINE;
-import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static com.android.server.wm.AppTransition.MAX_APP_TRANSITION_DURATION;
-import static com.android.server.wm.AppTransition.isActivityTransitOld;
-import static com.android.server.wm.AppTransition.isTaskFragmentTransitOld;
-import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -65,7 +53,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM;
import android.annotation.CallSuper;
import android.annotation.ColorInt;
@@ -81,10 +68,8 @@
import android.os.Debug;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.Trace;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.Pair;
import android.util.Pools;
import android.util.RotationUtils;
import android.util.Slog;
@@ -102,14 +87,11 @@
import android.view.SurfaceControlViewHost;
import android.view.WindowManager;
import android.view.WindowManager.TransitionOldType;
-import android.view.animation.Animation;
import android.window.IWindowContainerToken;
import android.window.WindowContainerToken;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.graphics.ColorUtils;
import com.android.internal.protolog.ProtoLog;
-import com.android.internal.protolog.common.LogLevel;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.wm.SurfaceAnimator.Animatable;
import com.android.server.wm.SurfaceAnimator.AnimationType;
@@ -1315,31 +1297,12 @@
}
/**
- * Returns true if the container or one of its children as some content it can display or wants
- * to display (e.g. app views or saved surface).
- *
- * NOTE: While this method will return true if the there is some content to display, it doesn't
- * mean the container is visible. Use {@link #isVisible()} to determine if the container is
- * visible.
- */
- boolean hasContentToDisplay() {
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final WindowContainer wc = mChildren.get(i);
- if (wc.hasContentToDisplay()) {
- return true;
- }
- }
- return false;
- }
-
- /**
* Returns true if the container or one of its children is considered visible from the
* WindowManager perspective which usually means valid surface and some other internal state
* are true.
*
* NOTE: While this method will return true if the surface is visible, it doesn't mean the
- * client has actually displayed any content. Use {@link #hasContentToDisplay()} to determine if
- * the container has any content to display.
+ * client has actually displayed any content.
*/
boolean isVisible() {
// TODO: Will this be more correct if it checks the visibility of its parents?
@@ -1480,13 +1443,6 @@
}
}
- void onAppTransitionDone() {
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final WindowContainer wc = mChildren.get(i);
- wc.onAppTransitionDone();
- }
- }
-
/**
* Called when this container or one of its descendants changed its requested orientation, and
* wants this container to handle it or pass it to its parent.
@@ -3039,264 +2995,10 @@
getRelativePosition(outPosition);
}
- /**
- * Applies the app transition animation according the given the layout properties in the
- * window hierarchy.
- *
- * @param lp The layout parameters of the window.
- * @param transit The app transition type indicates what kind of transition to be applied.
- * @param enter Whether the app transition is entering transition or not.
- * @param isVoiceInteraction Whether the container is participating in voice interaction or not.
- * @param sources {@link ActivityRecord}s which causes this app transition animation.
- *
- * @return {@code true} when the container applied the app transition, {@code false} if the
- * app transition is disabled or skipped.
- *
- * @see #getAnimationAdapter
- */
- boolean applyAnimation(WindowManager.LayoutParams lp, @TransitionOldType int transit,
- boolean enter, boolean isVoiceInteraction,
- @Nullable ArrayList<WindowContainer> sources) {
- if (mWmService.mDisableTransitionAnimation) {
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
- "applyAnimation: transition animation is disabled or skipped. "
- + "container=%s", this);
- cancelAnimation();
- return false;
- }
-
- // Only apply an animation if the display isn't frozen. If it is frozen, there is no reason
- // to animate and it can cause strange artifacts when we unfreeze the display if some
- // different animation is running.
- try {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WC#applyAnimation");
- if (okToAnimate()) {
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
- "applyAnimation: transit=%s, enter=%b, wc=%s",
- AppTransition.appTransitionOldToString(transit), enter, this);
- applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);
- } else {
- cancelAnimation();
- }
- } finally {
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- }
-
- return isAnimating();
- }
-
- /**
- * Gets the {@link AnimationAdapter} according the given window layout properties in the window
- * hierarchy.
- *
- * @return The return value will always contain two elements, one for normal animations and the
- * other for thumbnail animation, both can be {@code null}.
- *
- * @See com.android.server.wm.RemoteAnimationController.RemoteAnimationRecord
- * @See LocalAnimationAdapter
- */
- Pair<AnimationAdapter, AnimationAdapter> getAnimationAdapter(WindowManager.LayoutParams lp,
- @TransitionOldType int transit, boolean enter, boolean isVoiceInteraction) {
- final Pair<AnimationAdapter, AnimationAdapter> resultAdapters;
- final int appRootTaskClipMode = getDisplayContent().mAppTransition.getAppRootTaskClipMode();
-
- // Separate position and size for use in animators.
- final Rect screenBounds = getAnimationBounds(appRootTaskClipMode);
- mTmpRect.set(screenBounds);
- getAnimationPosition(mTmpPoint);
- mTmpRect.offsetTo(0, 0);
-
- final boolean isChanging = AppTransition.isChangeTransitOld(transit);
-
- if (isChanging) {
- final float durationScale = mWmService.getTransitionAnimationScaleLocked();
- final DisplayInfo displayInfo = getDisplayContent().getDisplayInfo();
- mTmpRect.offsetTo(mTmpPoint.x, mTmpPoint.y);
-
- final AnimationAdapter adapter = new LocalAnimationAdapter(
- new WindowChangeAnimationSpec(null /* startBounds */, mTmpRect,
- displayInfo, durationScale, true /* isAppAnimation */,
- false /* isThumbnail */),
- getSurfaceAnimationRunner());
-
- final AnimationAdapter thumbnailAdapter = null;
- resultAdapters = new Pair<>(adapter, thumbnailAdapter);
- mTransit = transit;
- mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags();
- } else {
- mNeedsAnimationBoundsLayer = (appRootTaskClipMode == ROOT_TASK_CLIP_AFTER_ANIM);
- final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
-
- if (a != null) {
- // Only apply corner radius to animation if we're not in multi window mode.
- // We don't want rounded corners when in pip or split screen.
- final float windowCornerRadius = !inMultiWindowMode()
- ? getDisplayContent().getWindowCornerRadius()
- : 0;
- if (asActivityRecord() != null
- && asActivityRecord().isNeedsLetterboxedAnimation()) {
- asActivityRecord().getLetterboxInnerBounds(mTmpRect);
- }
- AnimationAdapter adapter = new LocalAnimationAdapter(
- new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
- getDisplayContent().mAppTransition.canSkipFirstFrame(),
- appRootTaskClipMode, true /* isAppAnimation */, windowCornerRadius),
- getSurfaceAnimationRunner());
-
- resultAdapters = new Pair<>(adapter, null);
- mNeedsZBoost = a.getZAdjustment() == Animation.ZORDER_TOP
- || AppTransition.isClosingTransitOld(transit);
- mTransit = transit;
- mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags();
- } else {
- resultAdapters = new Pair<>(null, null);
- }
- }
- return resultAdapters;
- }
-
- protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter,
- @TransitionOldType int transit, boolean isVoiceInteraction,
- @Nullable ArrayList<WindowContainer> sources) {
- final Task task = asTask();
- if (task != null && !enter && !task.isActivityTypeHomeOrRecents()) {
- final InsetsControlTarget imeTarget = mDisplayContent.getImeTarget(IME_TARGET_LAYERING);
- final boolean isImeLayeringTarget = imeTarget != null && imeTarget.getWindow() != null
- && imeTarget.getWindow().getTask() == task;
- // Attach and show the IME screenshot when the task is the IME target and performing
- // task closing transition to the next task.
- if (isImeLayeringTarget && AppTransition.isTaskCloseTransitOld(transit)) {
- mDisplayContent.showImeScreenshot();
- }
- }
- final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp,
- transit, enter, isVoiceInteraction);
- AnimationAdapter adapter = adapters.first;
- AnimationAdapter thumbnailAdapter = adapters.second;
- if (adapter != null) {
- if (sources != null) {
- mSurfaceAnimationSources.addAll(sources);
- }
-
- AnimationRunnerBuilder animationRunnerBuilder = new AnimationRunnerBuilder();
-
- // Check if the animation requests to show background color for Activity and embedded
- // TaskFragment.
- final ActivityRecord activityRecord = asActivityRecord();
- final TaskFragment taskFragment = asTaskFragment();
- if (adapter.getShowBackground()
- // Check if it is Activity transition.
- && ((activityRecord != null && isActivityTransitOld(transit))
- // Check if it is embedded TaskFragment transition.
- || (taskFragment != null && taskFragment.isEmbedded()
- && isTaskFragmentTransitOld(transit)))) {
- final @ColorInt int backgroundColorForTransition;
- if (adapter.getBackgroundColor() != 0) {
- // If available use the background color provided through getBackgroundColor
- // which if set originates from a call to overridePendingAppTransition.
- backgroundColorForTransition = adapter.getBackgroundColor();
- } else {
- final TaskFragment organizedTf = activityRecord != null
- ? activityRecord.getOrganizedTaskFragment()
- : taskFragment.getOrganizedTaskFragment();
- if (organizedTf != null && organizedTf.getAnimationParams()
- .getAnimationBackgroundColor() != DEFAULT_ANIMATION_BACKGROUND_COLOR) {
- // This window is embedded and has an animation background color set on the
- // TaskFragment. Pass this color with this window, so the handler can use it
- // as the animation background color if needed,
- backgroundColorForTransition = organizedTf.getAnimationParams()
- .getAnimationBackgroundColor();
- } else {
- // Otherwise default to the window's background color if provided through
- // the theme as the background color for the animation - the top most window
- // with a valid background color and showBackground set takes precedence.
- final Task parentTask = activityRecord != null
- ? activityRecord.getTask()
- : taskFragment.getTask();
- backgroundColorForTransition = parentTask.getTaskDescription()
- .getBackgroundColor();
- }
- }
- // Set to opaque for animation background to prevent it from exposing the blank
- // background or content below.
- animationRunnerBuilder.setTaskBackgroundColor(ColorUtils.setAlphaComponent(
- backgroundColorForTransition, 255));
- }
-
- animationRunnerBuilder.build()
- .startAnimation(getPendingTransaction(), adapter, !isVisible(),
- ANIMATION_TYPE_APP_TRANSITION, thumbnailAdapter);
-
- if (adapter.getShowWallpaper()) {
- getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
- }
- }
- }
-
final SurfaceAnimationRunner getSurfaceAnimationRunner() {
return mWmService.mSurfaceAnimationRunner;
}
- private Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
- boolean isVoiceInteraction) {
- if ((isOrganized()
- // TODO(b/161711458): Clean-up when moved to shell.
- && getWindowingMode() != WINDOWING_MODE_FULLSCREEN
- && getWindowingMode() != WINDOWING_MODE_FREEFORM
- && getWindowingMode() != WINDOWING_MODE_MULTI_WINDOW)) {
- return null;
- }
-
- final DisplayContent displayContent = getDisplayContent();
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- final int width = displayInfo.appWidth;
- final int height = displayInfo.appHeight;
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: container=%s", this);
-
- // Determine the visible rect to calculate the thumbnail clip with
- // getAnimationFrames.
- final Rect frame = new Rect(0, 0, width, height);
- final Rect displayFrame = new Rect(0, 0,
- displayInfo.logicalWidth, displayInfo.logicalHeight);
- final Rect insets = new Rect();
- final Rect stableInsets = new Rect();
- final Rect surfaceInsets = new Rect();
- getAnimationFrames(frame, insets, stableInsets, surfaceInsets);
-
- if (mLaunchTaskBehind) {
- // Differentiate the two animations. This one which is briefly on the screen
- // gets the !enter animation, and the other one which remains on the
- // screen gets the enter animation. Both appear in the mOpeningApps set.
- enter = false;
- }
- ProtoLog.d(WM_DEBUG_APP_TRANSITIONS,
- "Loading animation for app transition. transit=%s enter=%b frame=%s insets=%s "
- + "surfaceInsets=%s",
- AppTransition.appTransitionOldToString(transit), enter, frame, insets,
- surfaceInsets);
- final Configuration displayConfig = displayContent.getConfiguration();
- final Animation a = getDisplayContent().mAppTransition.loadAnimation(lp, transit, enter,
- displayConfig.uiMode, displayConfig.orientation, frame, displayFrame, insets,
- surfaceInsets, stableInsets, isVoiceInteraction, inFreeformWindowingMode(), this);
- if (a != null) {
- if (a != null) {
- // Setup the maximum app transition duration to prevent malicious app may set a long
- // animation duration or infinite repeat counts for the app transition through
- // ActivityOption#makeCustomAnimation or WindowManager#overridePendingTransition.
- a.restrictDuration(MAX_APP_TRANSITION_DURATION);
- }
- if (ProtoLog.isEnabled(WM_DEBUG_ANIM, LogLevel.DEBUG)) {
- ProtoLog.i(WM_DEBUG_ANIM, "Loaded animation %s for %s, duration: %d, stack=%s",
- a, this, ((a != null) ? a.getDuration() : 0), Debug.getCallers(20));
- }
- final int containingWidth = frame.width();
- final int containingHeight = frame.height();
- a.initialize(containingWidth, containingHeight, width, height);
- a.scaleCurrentDuration(mWmService.getTransitionAnimationScaleLocked());
- }
- return a;
- }
-
boolean canCreateRemoteAnimationTarget() {
return false;
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 30dde54..270de01 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) {
@@ -475,8 +482,9 @@
r.detachFromProcess();
if (r.isVisibleRequested()) {
hasVisibleActivity = true;
+ Task finishingTask = r.getTask();
r.mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_CLOSE,
- TRANSIT_FLAG_APP_CRASHED);
+ TRANSIT_FLAG_APP_CRASHED, finishingTask);
}
r.destroyIfPossible("handleAppCrashed");
}
@@ -1228,6 +1236,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 +1258,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 +1307,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 +1349,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 92ad2ce..bfedc90 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1698,17 +1698,6 @@
|| mActivityRecord.isStartingWindowDisplayed());
}
- @Override
- boolean hasContentToDisplay() {
- if (!isDrawn() && (mViewVisibility == View.VISIBLE
- || (isAnimating(TRANSITION | PARENTS)
- && !getDisplayContent().mAppTransition.isTransitionSet()))) {
- return true;
- }
-
- return super.hasContentToDisplay();
- }
-
private boolean isVisibleByPolicyOrInsets() {
return isVisibleByPolicy()
// If we don't have a provider, this window isn't used as a window generating
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index d26539c..9577608 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -204,6 +204,7 @@
"android.frameworks.sensorservice-V1-ndk",
"android.frameworks.stats@1.0",
"android.frameworks.stats-V2-ndk",
+ "android.os.vibrator.flags-aconfig-cc",
"android.system.suspend.control-V1-cpp",
"android.system.suspend.control.internal-cpp",
"android.system.suspend-V1-ndk",
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index abd4cd2..534dbb1 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -21,6 +21,7 @@
#include <android/binder_parcel_jni.h>
#include <android/hardware/vibrator/1.3/IVibrator.h>
#include <android/persistable_bundle_aidl.h>
+#include <android_os_vibrator.h>
#include <nativehelper/JNIHelp.h>
#include <utils/Log.h>
#include <utils/misc.h>
@@ -143,21 +144,23 @@
return mHal->doWithRetry(fn, functionName);
}
- std::function<void()> createCallback(jlong vibrationId) {
+ std::function<void()> createCallback(jlong vibrationId, jlong stepId) {
auto callbackId = ++mCallbackId;
- return [vibrationId, callbackId, this]() {
+ return [vibrationId, stepId, callbackId, this]() {
auto currentCallbackId = mCallbackId.load();
- if (currentCallbackId != callbackId) {
- // This callback is from an older HAL call that is no longer relevant to the service
+ if (!android_os_vibrator_fix_vibration_thread_callback_handling() &&
+ currentCallbackId != callbackId) {
+ // This callback is from an older HAL call that is no longer relevant
return;
}
auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
- jniEnv->CallVoidMethod(mCallbackListener, sMethodIdOnComplete, mVibratorId,
- vibrationId);
+ jniEnv->CallVoidMethod(mCallbackListener, sMethodIdOnComplete, mVibratorId, vibrationId,
+ stepId);
};
}
void disableOldCallbacks() {
+ // TODO remove this once android_os_vibrator_fix_vibration_thread_callback_handling removed
mCallbackId++;
}
@@ -165,6 +168,7 @@
const std::shared_ptr<vibrator::HalController> mHal;
const int32_t mVibratorId;
const jobject mCallbackListener;
+ // TODO remove this once android_os_vibrator_fix_vibration_thread_callback_handling removed
std::atomic<int64_t> mCallbackId;
};
@@ -273,13 +277,13 @@
}
static jlong vibratorOn(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong timeoutMs,
- jlong vibrationId) {
+ jlong vibrationId, jlong stepId) {
VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
if (wrapper == nullptr) {
ALOGE("vibratorOn failed because native wrapper was not initialized");
return -1;
}
- auto callback = wrapper->createCallback(vibrationId);
+ auto callback = wrapper->createCallback(vibrationId, stepId);
auto onFn = [timeoutMs, &callback](vibrator::HalWrapper* hal) {
return hal->on(std::chrono::milliseconds(timeoutMs), callback);
};
@@ -324,7 +328,7 @@
}
static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong effect,
- jlong strength, jlong vibrationId) {
+ jlong strength, jlong vibrationId, jlong stepId) {
VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
if (wrapper == nullptr) {
ALOGE("vibratorPerformEffect failed because native wrapper was not initialized");
@@ -332,7 +336,7 @@
}
Aidl::Effect effectType = static_cast<Aidl::Effect>(effect);
Aidl::EffectStrength effectStrength = static_cast<Aidl::EffectStrength>(strength);
- auto callback = wrapper->createCallback(vibrationId);
+ auto callback = wrapper->createCallback(vibrationId, stepId);
auto performEffectFn = [effectType, effectStrength, &callback](vibrator::HalWrapper* hal) {
return hal->performEffect(effectType, effectStrength, callback);
};
@@ -342,7 +346,7 @@
static jlong vibratorPerformVendorEffect(JNIEnv* env, jclass /* clazz */, jlong ptr,
jobject vendorData, jlong strength, jfloat scale,
- jfloat adaptiveScale, jlong vibrationId) {
+ jfloat adaptiveScale, jlong vibrationId, jlong stepId) {
VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
if (wrapper == nullptr) {
ALOGE("vibratorPerformVendorEffect failed because native wrapper was not initialized");
@@ -350,7 +354,7 @@
}
Aidl::VendorEffect effect =
vendorEffectFromJavaParcel(env, vendorData, strength, scale, adaptiveScale);
- auto callback = wrapper->createCallback(vibrationId);
+ auto callback = wrapper->createCallback(vibrationId, stepId);
auto performVendorEffectFn = [&effect, &callback](vibrator::HalWrapper* hal) {
return hal->performVendorEffect(effect, callback);
};
@@ -359,7 +363,8 @@
}
static jlong vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong ptr,
- jobjectArray composition, jlong vibrationId) {
+ jobjectArray composition, jlong vibrationId,
+ jlong stepId) {
VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
if (wrapper == nullptr) {
ALOGE("vibratorPerformComposedEffect failed because native wrapper was not initialized");
@@ -371,7 +376,7 @@
jobject element = env->GetObjectArrayElement(composition, i);
effects.push_back(effectFromJavaPrimitive(env, element));
}
- auto callback = wrapper->createCallback(vibrationId);
+ auto callback = wrapper->createCallback(vibrationId, stepId);
auto performComposedEffectFn = [&effects, &callback](vibrator::HalWrapper* hal) {
return hal->performComposedEffect(effects, callback);
};
@@ -381,7 +386,8 @@
}
static jlong vibratorPerformPwleEffect(JNIEnv* env, jclass /* clazz */, jlong ptr,
- jobjectArray waveform, jint brakingId, jlong vibrationId) {
+ jobjectArray waveform, jint brakingId, jlong vibrationId,
+ jlong stepId) {
VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
if (wrapper == nullptr) {
ALOGE("vibratorPerformPwleEffect failed because native wrapper was not initialized");
@@ -406,7 +412,7 @@
}
}
- auto callback = wrapper->createCallback(vibrationId);
+ auto callback = wrapper->createCallback(vibrationId, stepId);
auto performPwleEffectFn = [&primitives, &callback](vibrator::HalWrapper* hal) {
return hal->performPwleEffect(primitives, callback);
};
@@ -415,7 +421,7 @@
}
static jlong vibratorPerformPwleV2Effect(JNIEnv* env, jclass /* clazz */, jlong ptr,
- jobjectArray waveform, jlong vibrationId) {
+ jobjectArray waveform, jlong vibrationId, jlong stepId) {
VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
if (wrapper == nullptr) {
ALOGE("vibratorPerformPwleV2Effect failed because native wrapper was not initialized");
@@ -431,7 +437,7 @@
}
composite.pwlePrimitives = primitives;
- auto callback = wrapper->createCallback(vibrationId);
+ auto callback = wrapper->createCallback(vibrationId, stepId);
auto composePwleV2Fn = [&composite, &callback](vibrator::HalWrapper* hal) {
return hal->composePwleV2(composite, callback);
};
@@ -610,16 +616,16 @@
(void*)vibratorNativeInit},
{"getNativeFinalizer", "()J", (void*)vibratorGetNativeFinalizer},
{"isAvailable", "(J)Z", (void*)vibratorIsAvailable},
- {"on", "(JJJ)J", (void*)vibratorOn},
+ {"on", "(JJJJ)J", (void*)vibratorOn},
{"off", "(J)V", (void*)vibratorOff},
{"setAmplitude", "(JF)V", (void*)vibratorSetAmplitude},
- {"performEffect", "(JJJJ)J", (void*)vibratorPerformEffect},
- {"performVendorEffect", "(JLandroid/os/Parcel;JFFJ)J", (void*)vibratorPerformVendorEffect},
- {"performComposedEffect", "(J[Landroid/os/vibrator/PrimitiveSegment;J)J",
+ {"performEffect", "(JJJJJ)J", (void*)vibratorPerformEffect},
+ {"performVendorEffect", "(JLandroid/os/Parcel;JFFJJ)J", (void*)vibratorPerformVendorEffect},
+ {"performComposedEffect", "(J[Landroid/os/vibrator/PrimitiveSegment;JJ)J",
(void*)vibratorPerformComposedEffect},
- {"performPwleEffect", "(J[Landroid/os/vibrator/RampSegment;IJ)J",
+ {"performPwleEffect", "(J[Landroid/os/vibrator/RampSegment;IJJ)J",
(void*)vibratorPerformPwleEffect},
- {"performPwleV2Effect", "(J[Landroid/os/vibrator/PwlePoint;J)J",
+ {"performPwleV2Effect", "(J[Landroid/os/vibrator/PwlePoint;JJ)J",
(void*)vibratorPerformPwleV2Effect},
{"setExternalControl", "(JZ)V", (void*)vibratorSetExternalControl},
{"alwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable},
@@ -632,7 +638,7 @@
auto listenerClassName =
"com/android/server/vibrator/VibratorController$OnVibrationCompleteListener";
jclass listenerClass = FindClassOrDie(env, listenerClassName);
- sMethodIdOnComplete = GetMethodIDOrDie(env, listenerClass, "onComplete", "(IJ)V");
+ sMethodIdOnComplete = GetMethodIDOrDie(env, listenerClass, "onComplete", "(IJJ)V");
jclass primitiveClass = FindClassOrDie(env, "android/os/vibrator/PrimitiveSegment");
sPrimitiveClassInfo.id = GetFieldIDOrDie(env, primitiveClass, "mPrimitiveId", "I");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 5b7e7f1..e3b9fdb 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -1454,11 +1454,12 @@
}
for (int i = 0; i < mLocalPolicies.size(); i++) {
- Set<PolicyKey> localPolicies = new HashSet<>(
- mLocalPolicies.get(mLocalPolicies.keyAt(i)).keySet());
+ // New users are potentially added to mLocalPolicies during the loop body
+ // (see b/374511959).
+ int userId = mLocalPolicies.keyAt(i);
+ Set<PolicyKey> localPolicies = new HashSet<>(mLocalPolicies.get(userId).keySet());
for (PolicyKey policy : localPolicies) {
- PolicyState<?> policyState = mLocalPolicies.get(
- mLocalPolicies.keyAt(i)).get(policy);
+ PolicyState<?> policyState = mLocalPolicies.get(userId).get(policy);
if (policyState.getPoliciesSetByAdmins().containsKey(admin)) {
removeLocalPolicy(
policyState.getPolicyDefinition(), admin, mLocalPolicies.keyAt(i));
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/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidManifest.xml b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidManifest.xml
index 4552326..79dd129 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidManifest.xml
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.inputmethod.imetests">
+ package="com.android.inputmethodservice">
<!-- Permissions required for granting and logging -->
<uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/>
@@ -33,7 +33,7 @@
</application>
<!-- The "targetPackage" reference the instruments APK package, which is the SimpleTestIme.apk,
- while the test package is "com.android.inputmethod.imetests" (FrameworksImeTests.apk).-->
+ while the test package is "com.android.inputmethodservice" (FrameworksImeTests.apk).-->
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.apps.inputmethod.simpleime"
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml
index 8e6954b..5fe5b23 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml
@@ -34,14 +34,15 @@
<option name="test-tag" value="FrameworksImeTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
- <option name="package" value="com.android.inputmethod.imetests" />
+ <option name="package" value="com.android.inputmethodservice" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
- <!-- Collect the files in the dump directory for debugging -->
+ <!-- Collect output of DumpOnFailure -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
- <option name="directory-keys" value="/sdcard/FrameworksImeTests/" />
+ <option name="directory-keys" value="/data/user/0/com.android.apps.inputmethod.simpleime/files" />
<option name="collect-on-run-ended-only" value="true" />
+ <option name="clean-up" value="true" />
</metrics_collector>
</configuration>
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
index ae5e851..6e16d29 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
@@ -40,10 +40,15 @@
import android.graphics.Insets;
import android.os.Build;
import android.os.RemoteException;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
+import android.server.wm.DumpOnFailure;
import android.server.wm.WindowManagerStateHelper;
import android.util.Log;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowInsetsAnimation;
import android.view.WindowManagerGlobal;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.Flags;
@@ -72,6 +77,7 @@
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -118,6 +124,9 @@
@Rule
public final TestName mName = new TestName();
+ @Rule
+ public final DumpOnFailure mDumpOnFailure = new DumpOnFailure();
+
private Instrumentation mInstrumentation;
private UiDevice mUiDevice;
private InputMethodManager mImm;
@@ -139,10 +148,9 @@
if (!mOriginalVerboseImeTrackerLoggingEnabled) {
setVerboseImeTrackerLogging(true);
}
+ mUiDevice.setOrientationNatural();
prepareIme();
prepareActivity();
- mUiDevice.freezeRotation();
- mUiDevice.setOrientationNatural();
// Waits for input binding ready.
eventually(() -> {
mInputMethodService = InputMethodServiceWrapper.getInstance();
@@ -169,6 +177,9 @@
@After
public void tearDown() throws Exception {
+ if (!mUiDevice.isNaturalOrientation()) {
+ mUiDevice.setOrientationNatural();
+ }
mUiDevice.unfreezeRotation();
if (!mOriginalVerboseImeTrackerLoggingEnabled) {
setVerboseImeTrackerLogging(false);
@@ -245,6 +256,61 @@
}
/**
+ * This checks that the surface is removed after the window was hidden in
+ * InputMethodService#hideSoftInput
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
+ public void testSurfaceRemovedAfterHideSoftInput() {
+ setShowImeWithHardKeyboard(true /* enabled */);
+
+ // Triggers to show IME via public API.
+ verifyInputViewStatusOnMainSync(() -> mActivity.showImeWithWindowInsetsController(),
+ EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown");
+ assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue();
+
+ final var window = mInputMethodService.getWindow().getWindow();
+ assertWithMessage("IME window exists").that(window).isNotNull();
+ eventually(() -> assertWithMessage("IME window showing").that(
+ window.getDecorView().getVisibility()).isEqualTo(View.VISIBLE));
+
+ mActivity.getWindow().getDecorView().setWindowInsetsAnimationCallback(
+ new WindowInsetsAnimation.Callback(
+ WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE) {
+ @NonNull
+ @Override
+ public WindowInsetsAnimation.Bounds onStart(
+ @NonNull WindowInsetsAnimation animation,
+ @NonNull WindowInsetsAnimation.Bounds bounds) {
+ return super.onStart(animation, bounds);
+ }
+
+ @NonNull
+ @Override
+ public WindowInsets onProgress(@NonNull WindowInsets insets,
+ @NonNull List<WindowInsetsAnimation> runningAnimations) {
+ assertWithMessage("IME surface not removed during the animation").that(
+ window.getDecorView().getVisibility()).isEqualTo(View.VISIBLE);
+ return insets;
+ }
+
+ @Override
+ public void onEnd(@NonNull WindowInsetsAnimation animation) {
+ assertWithMessage(
+ "IME surface not removed before the end of the animation").that(
+ window.getDecorView().getVisibility()).isEqualTo(View.VISIBLE);
+ super.onEnd(animation);
+ }
+ });
+
+ // Triggers to hide IME via public API.
+ verifyInputViewStatusOnMainSync(() -> mActivity.hideImeWithWindowInsetsController(),
+ EVENT_HIDE, true /* eventExpected */, false /* shown */, "IME is not shown");
+ eventually(() -> assertWithMessage("IME window not showing").that(
+ window.getDecorView().getVisibility()).isEqualTo(View.GONE));
+ }
+
+ /**
* This checks the result of calling IMS#requestShowSelf and IMS#requestHideSelf.
*/
@Test
@@ -1222,8 +1288,13 @@
@NonNull
private UiObject2 getUiObject(@NonNull BySelector bySelector) {
+ final var preScreenshot = mInstrumentation.getUiAutomation().takeScreenshot();
+ mDumpOnFailure.dumpOnFailure("pre-getUiObject", preScreenshot);
final var uiObject = mUiDevice.wait(Until.findObject(bySelector), TIMEOUT_MS);
- assertWithMessage("UiObject with " + bySelector + " was found").that(uiObject).isNotNull();
+ mInstrumentation.waitForIdleSync();
+ final var postScreenshot = mInstrumentation.getUiAutomation().takeScreenshot();
+ mDumpOnFailure.dumpOnFailure("post-getUiObject", postScreenshot);
+ assertWithMessage("UiObject with " + bySelector + " was found").that(uiObject).isNull();
return uiObject;
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java
index 9e3d9ec..d98a0d1 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java
@@ -28,6 +28,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.NonNull;
import android.content.pm.PackageManagerInternal;
import android.os.Handler;
import android.os.IBinder;
@@ -55,8 +56,7 @@
private static final String SOME_PACKAGE_NAME = "some.package";
@Rule
- public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
- .setProvideMainThread(true).build();
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
@Mock
private PackageManagerInternal mMockPackageManagerInternal;
@@ -65,7 +65,7 @@
private IInputMethodClient mClient;
@Mock
- private IRemoteInputConnection mConnection;
+ private IRemoteInputConnection mFallbackConnection;
private Handler mHandler;
@@ -81,23 +81,25 @@
}
// TODO(b/322895594): No need to directly invoke create$ravenwood once b/322895594 is fixed.
- private IInputMethodClientInvoker createInvoker(IInputMethodClient client, Handler handler) {
+ @NonNull
+ private IInputMethodClientInvoker createInvoker(@NonNull IInputMethodClient client,
+ @NonNull Handler handler) {
return RavenwoodRule.isOnRavenwood()
- ? IInputMethodClientInvoker.create$ravenwood(client, handler) :
- IInputMethodClientInvoker.create(client, handler);
+ ? IInputMethodClientInvoker.create$ravenwood(client, handler)
+ : IInputMethodClientInvoker.create(client, handler);
}
@Test
public void testAddClient_cannotAddTheSameClientTwice() {
final var invoker = createInvoker(mClient, mHandler);
synchronized (ImfLock.class) {
- mController.addClient(invoker, mConnection, ANY_DISPLAY_ID, ANY_CALLER_UID,
+ mController.addClient(invoker, mFallbackConnection, ANY_DISPLAY_ID, ANY_CALLER_UID,
ANY_CALLER_PID);
SecurityException thrown = assertThrows(SecurityException.class,
() -> {
synchronized (ImfLock.class) {
- mController.addClient(invoker, mConnection, ANY_DISPLAY_ID,
+ mController.addClient(invoker, mFallbackConnection, ANY_DISPLAY_ID,
ANY_CALLER_UID, ANY_CALLER_PID);
}
});
@@ -111,9 +113,8 @@
public void testAddClient() throws Exception {
final var invoker = createInvoker(mClient, mHandler);
synchronized (ImfLock.class) {
- final var added = mController.addClient(invoker, mConnection, ANY_DISPLAY_ID,
- ANY_CALLER_UID,
- ANY_CALLER_PID);
+ final var added = mController.addClient(invoker, mFallbackConnection, ANY_DISPLAY_ID,
+ ANY_CALLER_UID, ANY_CALLER_PID);
verify(invoker.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
assertThat(mController.getClient(invoker.asBinder())).isSameInstanceAs(added);
@@ -127,8 +128,8 @@
ClientState added;
synchronized (ImfLock.class) {
mController.addClientControllerCallback(callback);
- added = mController.addClient(invoker, mConnection, ANY_DISPLAY_ID, ANY_CALLER_UID,
- ANY_CALLER_PID);
+ added = mController.addClient(invoker, mFallbackConnection, ANY_DISPLAY_ID,
+ ANY_CALLER_UID, ANY_CALLER_PID);
assertThat(mController.getClient(invoker.asBinder())).isSameInstanceAs(added);
assertThat(mController.removeClient(mClient)).isTrue();
}
@@ -141,14 +142,14 @@
@Test
public void testVerifyClientAndPackageMatch() {
final var invoker = createInvoker(mClient, mHandler);
- when(mMockPackageManagerInternal.isSameApp(eq(SOME_PACKAGE_NAME), /* flags= */
- anyLong(), eq(ANY_CALLER_UID), /* userId= */ anyInt())).thenReturn(true);
+ when(mMockPackageManagerInternal.isSameApp(eq(SOME_PACKAGE_NAME), anyLong() /* flags */,
+ eq(ANY_CALLER_UID), anyInt() /* userId */)).thenReturn(true);
synchronized (ImfLock.class) {
- mController.addClient(invoker, mConnection, ANY_DISPLAY_ID, ANY_CALLER_UID,
+ mController.addClient(invoker, mFallbackConnection, ANY_DISPLAY_ID, ANY_CALLER_UID,
ANY_CALLER_PID);
- assertThat(
- mController.verifyClientAndPackageMatch(mClient, SOME_PACKAGE_NAME)).isTrue();
+ assertThat(mController.verifyClientAndPackageMatch(mClient, SOME_PACKAGE_NAME))
+ .isTrue();
}
}
@@ -171,7 +172,7 @@
private ClientState mRemoved;
@Override
- public void onClientRemoved(ClientState removed) {
+ public void onClientRemoved(@NonNull ClientState removed) {
mRemoved = removed;
mLatch.countDown();
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index 3aeab09..05615f6 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -196,7 +196,7 @@
@RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
public void testApplyImeVisibility_hideImeFromTargetOnSecondaryDisplay() {
// Init a IME target client on the secondary display to show IME.
- mInputMethodManagerService.addClient(mMockInputMethodClient, mMockRemoteInputConnection,
+ mInputMethodManagerService.addClient(mMockInputMethodClient, mMockFallbackInputConnection,
10 /* selfReportedDisplayId */);
synchronized (ImfLock.class) {
setAttachedClientLocked(null);
@@ -283,7 +283,7 @@
softInputMode /* softInputMode */,
0 /* windowFlags */,
mEditorInfo /* editorInfo */,
- mMockRemoteInputConnection /* inputConnection */,
+ mMockFallbackInputConnection /* fallbackInputConnection */,
mMockRemoteAccessibilityInputConnection /* remoteAccessibilityInputConnection */,
mTargetSdkVersion /* unverifiedTargetSdkVersion */,
mUserId /* userId */,
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
index 6af4064..9ce86ca 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
@@ -116,7 +116,7 @@
@Mock protected IInputMethodClient mMockInputMethodClient;
@Mock protected IInputMethodSession mMockInputMethodSession;
@Mock protected IBinder mWindowToken;
- @Mock protected IRemoteInputConnection mMockRemoteInputConnection;
+ @Mock protected IRemoteInputConnection mMockFallbackInputConnection;
@Mock protected IRemoteAccessibilityInputConnection mMockRemoteAccessibilityInputConnection;
@Mock protected ImeOnBackInvokedDispatcher mMockImeOnBackInvokedDispatcher;
@Mock protected IInputMethodManager.Stub mMockIInputMethodManager;
@@ -300,7 +300,8 @@
lifecycle.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
// Call InputMethodManagerService#addClient() as a preparation to start interacting with it.
- mInputMethodManagerService.addClient(mMockInputMethodClient, mMockRemoteInputConnection, 0);
+ mInputMethodManagerService.addClient(mMockInputMethodClient, mMockFallbackInputConnection,
+ 0 /* selfReportedDisplayId */);
createSessionForClient(mMockInputMethodClient);
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java
index c958bd3..11abc94 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java
@@ -316,7 +316,7 @@
softInputMode /* softInputMode */,
0 /* windowFlags */,
mEditorInfo /* editorInfo */,
- mMockRemoteInputConnection /* inputConnection */,
+ mMockFallbackInputConnection /* fallbackInputConnection */,
mMockRemoteAccessibilityInputConnection /* remoteAccessibilityInputConnection */,
mTargetSdkVersion /* unverifiedTargetSdkVersion */,
mUserId /* userId */,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index e0e4425..c151732 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -984,8 +984,7 @@
Handler handler = displayManager.getDisplayHandler();
waitForIdleHandler(handler);
- assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_BASIC_CHANGED,
- EVENT_DISPLAY_REFRESH_RATE_CHANGED);
+ assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_BASIC_CHANGED);
}
/**
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/mockingservicestests/src/com/android/server/crashrecovery/AndroidTest.xml b/services/tests/mockingservicestests/src/com/android/server/crashrecovery/AndroidTest.xml
index 7b06ebe..5043c47 100644
--- a/services/tests/mockingservicestests/src/com/android/server/crashrecovery/AndroidTest.xml
+++ b/services/tests/mockingservicestests/src/com/android/server/crashrecovery/AndroidTest.xml
@@ -31,4 +31,9 @@
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
+
+ <!-- Only run this Tests if the Crashrecovery Mainline module is installed. -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <option name="mainline-module-package-name" value="com.google.android.crashrecovery" />
+ </object>
</configuration>
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/AndroidTest.xml b/services/tests/mockingservicestests/src/com/android/server/rollback/AndroidTest.xml
index 635183c..6f6b017 100644
--- a/services/tests/mockingservicestests/src/com/android/server/rollback/AndroidTest.xml
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/AndroidTest.xml
@@ -31,4 +31,9 @@
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
</test>
+
+ <!-- Only run this Tests if the Crashrecovery Mainline module is installed. -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <option name="mainline-module-package-name" value="com.google.android.crashrecovery" />
+ </object>
</configuration>
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
index b53f6fb..49c37f1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
@@ -16,6 +16,7 @@
package com.android.server.wallpaper;
+import static android.app.WallpaperManager.FLAG_SYSTEM;
import static android.app.WallpaperManager.ORIENTATION_LANDSCAPE;
import static android.app.WallpaperManager.ORIENTATION_UNKNOWN;
import static android.app.WallpaperManager.ORIENTATION_PORTRAIT;
@@ -23,13 +24,20 @@
import static android.app.WallpaperManager.ORIENTATION_SQUARE_PORTRAIT;
import static android.app.WallpaperManager.getOrientation;
import static android.app.WallpaperManager.getRotatedOrientation;
+import static android.os.UserHandle.USER_SYSTEM;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.window.flags.Flags.FLAG_MULTI_CROP;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
@@ -37,15 +45,29 @@
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.util.Log;
import android.util.SparseArray;
+import android.view.Display;
+import android.view.DisplayInfo;
import androidx.test.runner.AndroidJUnit4;
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+
+import org.junit.AfterClass;
import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.quality.Strictness;
+import java.io.File;
+import java.io.IOException;
import java.util.Comparator;
import java.util.List;
@@ -57,6 +79,7 @@
@RunWith(AndroidJUnit4.class)
@RequiresFlagsEnabled(FLAG_MULTI_CROP)
public class WallpaperCropperTest {
+ private static final String TAG = "WallpaperCropperTest";
@Mock
private WallpaperDisplayHelper mWallpaperDisplayHelper;
@@ -94,6 +117,8 @@
private static final List<List<Point>> ALL_FOLDABLE_DISPLAYS = List.of(
FOLDABLE_ONE, FOLDABLE_TWO, FOLDABLE_THREE, FOLDABLE_FOUR);
+ private static StaticMockitoSession sMockitoSession;
+
private SparseArray<Point> mDisplaySizes = new SparseArray<>();
private int mFolded = ORIENTATION_UNKNOWN;
private int mFoldedRotated = ORIENTATION_UNKNOWN;
@@ -103,12 +128,53 @@
private static final List<Integer> ALL_MODES = List.of(
WallpaperCropper.ADD, WallpaperCropper.REMOVE, WallpaperCropper.BALANCE);
+ private final SparseArray<File> mTempDirs = new SparseArray<>();
+
+ private final TemporaryFolder mFolder = new TemporaryFolder();
+
+ @Rule
+ public RuleChain rules = RuleChain.outerRule(mFolder);
+
+ @BeforeClass
+ public static void setUpClass() {
+ sMockitoSession = mockitoSession()
+ .strictness(Strictness.LENIENT)
+ .spyStatic(WallpaperUtils.class)
+ .startMocking();
+ }
+
+ @AfterClass
+ public static void tearDownClass() {
+ if (sMockitoSession != null) {
+ sMockitoSession.finishMocking();
+ sMockitoSession = null;
+ }
+ }
+
@Before
public void setUp() {
initMocks(this);
+ ExtendedMockito.doAnswer(invocation -> {
+ int userId = (invocation.getArgument(0));
+ return getWallpaperTestDir(userId);
+ }).when(() -> WallpaperUtils.getWallpaperDir(anyInt()));
+
mWallpaperCropper = new WallpaperCropper(mWallpaperDisplayHelper);
}
+ private File getWallpaperTestDir(int userId) {
+ File tempDir = mTempDirs.get(userId);
+ if (tempDir == null) {
+ try {
+ tempDir = mFolder.newFolder(String.valueOf(userId));
+ mTempDirs.append(userId, tempDir);
+ } catch (IOException e) {
+ Log.e(TAG, "getWallpaperTestDir failed at userId= " + userId);
+ }
+ }
+ return tempDir;
+ }
+
private void setUpWithDisplays(List<Point> displaySizes) {
mDisplaySizes = new SparseArray<>();
displaySizes.forEach(size -> {
@@ -632,6 +698,134 @@
}
}
+ // Test isWallpaperCompatibleForDisplay always return true for the default display.
+ @Test
+ public void isWallpaperCompatibleForDisplay_defaultDisplay_returnTrue()
+ throws Exception {
+ DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.logicalWidth = 2560;
+ displayInfo.logicalHeight = 1044;
+ doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(DEFAULT_DISPLAY));
+ WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
+ new Point(100, 100));
+
+ assertThat(
+ mWallpaperCropper.isWallpaperCompatibleForDisplay(DEFAULT_DISPLAY,
+ wallpaperData)).isTrue();
+ }
+
+ // Test isWallpaperCompatibleForDisplay always return true for the stock wallpaper.
+ @Test
+ public void isWallpaperCompatibleForDisplay_stockWallpaper_returnTrue()
+ throws Exception {
+ final int displayId = 2;
+ DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.logicalWidth = 2560;
+ displayInfo.logicalHeight = 1044;
+ doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
+ WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ true,
+ new Point(100, 100));
+
+ assertThat(
+ mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
+ wallpaperData)).isTrue();
+ }
+
+ // Test isWallpaperCompatibleForDisplay wallpaper is suitable for the display and wallpaper
+ // aspect ratio meets the hard-coded aspect ratio.
+ @Test
+ public void isWallpaperCompatibleForDisplay_wallpaperSizeSuitableForDisplayAndMeetAspectRatio_returnTrue()
+ throws Exception {
+ final int displayId = 2;
+ DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.logicalWidth = 2560;
+ displayInfo.logicalHeight = 1044;
+ doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
+ WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
+ new Point(4000, 3000));
+
+ assertThat(
+ mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
+ wallpaperData)).isTrue();
+ }
+
+ // Test isWallpaperCompatibleForDisplay wallpaper is not suitable for the display and wallpaper
+ // aspect ratio meets the hard-coded aspect ratio.
+ @Test
+ public void isWallpaperCompatibleForDisplay_wallpaperSizeNotSuitableForDisplayAndMeetAspectRatio_returnFalse()
+ throws Exception {
+ final int displayId = 2;
+ DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.logicalWidth = 2560;
+ displayInfo.logicalHeight = 1044;
+ doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
+ WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
+ new Point(1000, 500));
+
+ assertThat(
+ mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
+ wallpaperData)).isFalse();
+ }
+
+ // Test isWallpaperCompatibleForDisplay wallpaper is suitable for the display and wallpaper
+ // aspect ratio doesn't meet the hard-coded aspect ratio.
+ @Test
+ public void isWallpaperCompatibleForDisplay_wallpaperSizeSuitableForDisplayAndDoNotMeetAspectRatio_returnFalse()
+ throws Exception {
+ final int displayId = 2;
+ DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.logicalWidth = 2560;
+ displayInfo.logicalHeight = 1044;
+ doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
+ WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
+ new Point(2000, 4000));
+
+ assertThat(
+ mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
+ wallpaperData)).isFalse();
+ }
+
+ // Test isWallpaperCompatibleForDisplay, portrait display, wallpaper is suitable for the display
+ // and wallpaper aspect ratio doesn't meet the hard-coded aspect ratio.
+ @Test
+ public void isWallpaperCompatibleForDisplay_portraitDisplay_wallpaperSizeSuitableForDisplayAndMeetAspectRatio_returnTrue()
+ throws Exception {
+ final int displayId = 2;
+ DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.logicalWidth = 1044;
+ displayInfo.logicalHeight = 2560;
+ doReturn(displayInfo).when(mWallpaperDisplayHelper).getDisplayInfo(eq(displayId));
+ WallpaperData wallpaperData = createWallpaperData(/* isStockWallpaper = */ false,
+ new Point(2000, 4000));
+
+ assertThat(
+ mWallpaperCropper.isWallpaperCompatibleForDisplay(displayId,
+ wallpaperData)).isTrue();
+ }
+
+ private void mockDisplay(int displayId, Point displayResolution) {
+ final Display mockDisplay = mock(Display.class);
+ when(mockDisplay.getDisplayInfo(any(DisplayInfo.class))).thenAnswer(invocation -> {
+ DisplayInfo displayInfo = invocation.getArgument(0);
+ displayInfo.displayId = displayId;
+ displayInfo.logicalWidth = displayResolution.x;
+ displayInfo.logicalHeight = displayResolution.y;
+ return true;
+ });
+ }
+
+ private WallpaperData createWallpaperData(boolean isStockWallpaper, Point wallpaperSize)
+ throws Exception {
+ WallpaperData wallpaperData = new WallpaperData(USER_SYSTEM, FLAG_SYSTEM);
+ File wallpaperFile = wallpaperData.getWallpaperFile();
+ if (!isStockWallpaper) {
+ wallpaperFile.getParentFile().mkdirs();
+ wallpaperFile.createNewFile();
+ }
+ wallpaperData.cropHint.set(0, 0, wallpaperSize.x, wallpaperSize.y);
+ return wallpaperData;
+ }
+
private static Rect centerOf(Rect container, Point point) {
checkSubset(container, point);
int diffWidth = container.width() - point.x;
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
index bc04fd9..bada337 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -123,6 +123,7 @@
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
+import java.util.Map;
/**
* Tests for the {@link WallpaperManagerService} class.
@@ -258,6 +259,8 @@
spyOn(mIpm);
spyOn(mResources);
doReturn(true).when(mResources).getBoolean(eq(R.bool.config_isDesktopModeSupported));
+ doReturn(true).when(mResources).getBoolean(
+ eq(R.bool.config_canInternalDisplayHostDesktops));
mService = new TestWallpaperManagerService(sContext);
spyOn(mService);
mService.systemReady();
@@ -742,7 +745,9 @@
wallpaper.connection.mService = mockIWallpaperService;
// GIVEN there are two displays: DEFAULT_DISPLAY, 2
final int testDisplayId = 2;
- setUpDisplays(List.of(DEFAULT_DISPLAY, testDisplayId));
+ setUpDisplays(Map.of(
+ DEFAULT_DISPLAY, true,
+ testDisplayId, true));
// WHEN display ID, 2, is ready.
WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
@@ -782,7 +787,9 @@
lockWallpaper.connection.mService = mockIWallpaperService;
// GIVEN there are two displays: DEFAULT_DISPLAY, 2
final int testDisplayId = 2;
- setUpDisplays(List.of(DEFAULT_DISPLAY, testDisplayId));
+ setUpDisplays(Map.of(
+ DEFAULT_DISPLAY, true,
+ testDisplayId, true));
// WHEN display ID, 2, is ready.
WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
@@ -823,13 +830,15 @@
throws Exception {
final int testUserId = USER_SYSTEM;
mService.switchUser(testUserId, null);
+ mService.mLastWallpaper.connection.mWallpaper.cropHint.set(0, 0, 4000, 2250);
IWallpaperService mockIWallpaperService = mock(IWallpaperService.class);
mService.mFallbackWallpaper.connection.mService = mockIWallpaperService;
// GIVEN there are two displays: DEFAULT_DISPLAY, 2
final int testDisplayId = 2;
- setUpDisplays(List.of(DEFAULT_DISPLAY, testDisplayId));
- // GIVEN the wallpaper isn't compatible with display ID, 2
- mService.removeWallpaperCompatibleDisplayForTest(testDisplayId);
+ setUpDisplays(Map.of(
+ DEFAULT_DISPLAY, true,
+ // Given the wallpaper is smaller thn the display resolution.
+ testDisplayId, false));
// WHEN display ID, 2, is ready.
WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
@@ -868,7 +877,10 @@
wallpaper.connection.mService = mockIWallpaperService;
// GIVEN there are two displays: DEFAULT_DISPLAY, 2
final int testDisplayId = 2;
- setUpDisplays(List.of(DEFAULT_DISPLAY, testDisplayId));
+ mService.mLastWallpaper.connection.mWallpaper.cropHint.set(0, 0, 4000, 2250);
+ setUpDisplays(Map.of(
+ DEFAULT_DISPLAY, true,
+ testDisplayId, true));
// GIVEN wallpaper connections have been established for display ID, 2.
WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
WallpaperManagerInternal.class);
@@ -906,7 +918,9 @@
lockWallpaper.connection.mService = mockIWallpaperService;
// GIVEN there are two displays: DEFAULT_DISPLAY, 2
final int testDisplayId = 2;
- setUpDisplays(List.of(DEFAULT_DISPLAY, testDisplayId));
+ setUpDisplays(Map.of(
+ DEFAULT_DISPLAY, true,
+ testDisplayId, true));
// GIVEN wallpaper connections have been established for display ID, 2.
WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
WallpaperManagerInternal.class);
@@ -940,9 +954,9 @@
mService.mFallbackWallpaper.connection.mService = mockIWallpaperService;
// GIVEN there are two displays: DEFAULT_DISPLAY, 2
final int testDisplayId = 2;
- setUpDisplays(List.of(DEFAULT_DISPLAY, testDisplayId));
- // GIVEN display ID, 2, is incompatible with the wallpaper.
- mService.removeWallpaperCompatibleDisplayForTest(testDisplayId);
+ setUpDisplays(Map.of(
+ DEFAULT_DISPLAY, true,
+ testDisplayId, false));
// GIVEN wallpaper connections have been established for display ID, 2.
WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
WallpaperManagerInternal.class);
@@ -989,7 +1003,9 @@
wallpaper.connection.mService = mockIWallpaperService;
// GIVEN there are two displays: DEFAULT_DISPLAY, 2
final int testDisplayId = 2;
- setUpDisplays(List.of(DEFAULT_DISPLAY, testDisplayId));
+ setUpDisplays(Map.of(
+ DEFAULT_DISPLAY, true,
+ testDisplayId, true));
// GIVEN wallpaper connections have been established for displayID, 2.
WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
WallpaperManagerInternal.class);
@@ -1023,7 +1039,9 @@
lockWallpaper.connection.mService = mockIWallpaperService;
// GIVEN there are two displays: DEFAULT_DISPLAY, 2
final int testDisplayId = 2;
- setUpDisplays(List.of(DEFAULT_DISPLAY, testDisplayId));
+ setUpDisplays(Map.of(
+ DEFAULT_DISPLAY, true,
+ testDisplayId, true));
// GIVEN wallpaper connections have been established for displayID, 2.
WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
WallpaperManagerInternal.class);
@@ -1051,12 +1069,14 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER)
public void deviceBooted_multiDisplays_shouldHaveExpectedConnections() {
+ final int testUserId = USER_SYSTEM;
final int incompatibleDisplayId = 2;
final int compatibleDisplayId = 3;
- setUpDisplays(List.of(DEFAULT_DISPLAY, incompatibleDisplayId, compatibleDisplayId));
- mService.removeWallpaperCompatibleDisplayForTest(incompatibleDisplayId);
+ setUpDisplays(Map.of(
+ DEFAULT_DISPLAY, true,
+ compatibleDisplayId, true,
+ incompatibleDisplayId, false));
- final int testUserId = USER_SYSTEM;
// After reboot, a switch user triggers the wallpapers initialization.
mService.switchUser(testUserId, null);
@@ -1087,15 +1107,18 @@
public void setWallpaperComponent_multiDisplays_displayBecomeCompatible_shouldHaveExpectedConnections() {
final int display2 = 2;
final int display3 = 3;
- setUpDisplays(List.of(DEFAULT_DISPLAY, display2, display3));
- mService.removeWallpaperCompatibleDisplayForTest(display2);
final int testUserId = USER_SYSTEM;
+ setUpDisplays(Map.of(
+ DEFAULT_DISPLAY, true,
+ display2, false,
+ display3, true));
+
mService.switchUser(testUserId, null);
+ doReturn(true).when(mService.mWallpaperCropper).isWallpaperCompatibleForDisplay(
+ eq(display2), any());
// Switch to a test wallpaper and then image wallpaper later to simulate a wallpaper change.
mService.setWallpaperComponent(TEST_WALLPAPER_COMPONENT, sContext.getOpPackageName(),
FLAG_SYSTEM | FLAG_LOCK, testUserId);
- mService.addWallpaperCompatibleDisplayForTest(display2);
-
mService.setWallpaperComponent(sImageWallpaperComponentName, sContext.getOpPackageName(),
FLAG_SYSTEM | FLAG_LOCK, testUserId);
@@ -1124,15 +1147,19 @@
public void setWallpaperComponent_multiDisplays_displayBecomeIncompatible_shouldHaveExpectedConnections() {
final int display2 = 2;
final int display3 = 3;
- setUpDisplays(List.of(DEFAULT_DISPLAY, display2, display3));
- mService.removeWallpaperCompatibleDisplayForTest(display2);
final int testUserId = USER_SYSTEM;
+ setUpDisplays(Map.of(
+ DEFAULT_DISPLAY, true,
+ display2, true,
+ display3, true));
mService.switchUser(testUserId, null);
+ doReturn(false).when(mService.mWallpaperCropper).isWallpaperCompatibleForDisplay(
+ eq(display2), any());
+ doReturn(false).when(mService.mWallpaperCropper).isWallpaperCompatibleForDisplay(
+ eq(display3), any());
// Switch to a test wallpaper and then image wallpaper later to simulate a wallpaper change.
mService.setWallpaperComponent(TEST_WALLPAPER_COMPONENT, sContext.getOpPackageName(),
FLAG_SYSTEM | FLAG_LOCK, testUserId);
- mService.removeWallpaperCompatibleDisplayForTest(display3);
-
mService.setWallpaperComponent(sImageWallpaperComponentName, sContext.getOpPackageName(),
FLAG_SYSTEM | FLAG_LOCK, testUserId);
@@ -1159,14 +1186,15 @@
public void setWallpaperComponent_systemAndLockWallpapers_multiDisplays_shouldHaveExpectedConnections() {
final int incompatibleDisplayId = 2;
final int compatibleDisplayId = 3;
- setUpDisplays(List.of(DEFAULT_DISPLAY, incompatibleDisplayId, compatibleDisplayId));
+ setUpDisplays(Map.of(
+ DEFAULT_DISPLAY, true,
+ incompatibleDisplayId, false,
+ compatibleDisplayId, true));
final int testUserId = USER_SYSTEM;
mService.switchUser(testUserId, null);
// Switch to a test wallpaper and then image wallpaper later to simulate a wallpaper change.
mService.setWallpaperComponent(TEST_WALLPAPER_COMPONENT, sContext.getOpPackageName(),
FLAG_SYSTEM | FLAG_LOCK, testUserId);
- mService.removeWallpaperCompatibleDisplayForTest(incompatibleDisplayId);
-
mService.setWallpaperComponent(sImageWallpaperComponentName, sContext.getOpPackageName(),
FLAG_SYSTEM, testUserId);
@@ -1258,21 +1286,27 @@
* to return them. It also sets up the {@link WindowManagerInternal} to indicate that all
* displays support home.
*
- * @param displayIds A list of display IDs to create mock displays for.
+ * @param displayIdsToWallpaperCompatibility A map of display IDs to wallpaper compatibility.
*/
- private void setUpDisplays(List<Integer> displayIds) {
+ private void setUpDisplays(Map<Integer, Boolean> displayIdsToWallpaperCompatibility) {
+ spyOn(mService.mWallpaperCropper);
doReturn(true).when(sWindowManagerInternal).isHomeSupportedOnDisplay(anyInt());
- Display[] mockDisplays = new Display[displayIds.size()];
- for (int i = 0; i < displayIds.size(); i++) {
- final int displayId = displayIds.get(i);
+ Display[] mockDisplays = new Display[displayIdsToWallpaperCompatibility.size()];
+ int counter = 0;
+ for (Map.Entry<Integer, Boolean> entry : displayIdsToWallpaperCompatibility.entrySet()) {
+ final int displayId = entry.getKey();
+ final boolean compatibleWithWallpaper = entry.getValue();
final Display mockDisplay = mock(Display.class);
- mockDisplays[i] = mockDisplay;
+ mockDisplays[counter] = mockDisplay;
doReturn(DISPLAY_SIZE_DIMENSION).when(mockDisplay).getMaximumSizeDimension();
doReturn(mockDisplay).when(mDisplayManager).getDisplay(eq(displayId));
doReturn(displayId).when(mockDisplay).getDisplayId();
doReturn(true).when(mockDisplay).hasAccess(anyInt());
- mService.addWallpaperCompatibleDisplayForTest(displayId);
+ doReturn(compatibleWithWallpaper).when(
+ mService.mWallpaperCropper).isWallpaperCompatibleForDisplay(eq(displayId),
+ any());
+ counter++;
}
doReturn(mockDisplays).when(mDisplayManager).getDisplays();
@@ -1295,6 +1329,4 @@
assertEquals(pfdContents, fileContents);
}
}
-
-
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AggregatedPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AggregatedPowerStatsTest.java
index 0e73329..978fd1a 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AggregatedPowerStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AggregatedPowerStatsTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
+import android.annotation.SuppressLint;
import android.os.BatteryConsumer;
import android.os.PersistableBundle;
import android.util.SparseArray;
@@ -280,6 +281,7 @@
.isEqualTo(new long[]{250, 300});
}
+ @SuppressLint("CheckResult")
private static long[] getDeviceStats(
AggregatedPowerStats stats, int powerComponentId,
int... states) {
@@ -290,6 +292,7 @@
return out;
}
+ @SuppressLint("CheckResult")
private static long[] getStateStats(
AggregatedPowerStats stats, int key, int... states) {
PowerComponentAggregatedPowerStats powerComponentStats =
@@ -299,6 +302,7 @@
return out;
}
+ @SuppressLint("CheckResult")
private static long[] getUidDeviceStats(
AggregatedPowerStats stats, int powerComponentId,
int uid, int... states) {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessorTest.java
index 21e615f..58e9d1e 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessorTest.java
@@ -27,6 +27,7 @@
import static org.mockito.Mockito.when;
+import android.annotation.SuppressLint;
import android.hardware.power.stats.EnergyConsumerType;
import android.os.BatteryConsumer;
import android.os.Handler;
@@ -166,6 +167,7 @@
return stats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY);
}
+ @SuppressLint("CheckResult")
private void assertPowerEstimate(
PowerComponentAggregatedPowerStats aggregatedStats,
int powerState, int screenState, double expectedPowerEstimate) {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BasePowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BasePowerStatsProcessorTest.java
index cca6033..58784d7 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BasePowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BasePowerStatsProcessorTest.java
@@ -34,6 +34,7 @@
import static org.mockito.Mockito.mock;
+import android.annotation.SuppressLint;
import android.os.BatteryConsumer;
import android.os.BatteryUsageStats;
import android.os.Process;
@@ -68,6 +69,7 @@
.setProcessorSupplier(() -> new BasePowerStatsProcessor(() -> 4000));
}
+ @SuppressLint("CheckResult")
@Test
public void processPowerStats() {
AggregatedPowerStats aggregatedPowerStats = prepareAggregatedPowerStats(true);
@@ -95,9 +97,11 @@
stats.getUidStats(uidStats, APP_UID1,
states(POWER_STATE_BATTERY, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
assertThat(statsLayout.getUidUsageDuration(uidStats)).isEqualTo(5000);
- stats.getUidStats(uidStats, APP_UID1,
+ boolean nonZero = stats.getUidStats(uidStats, APP_UID1,
states(POWER_STATE_BATTERY, SCREEN_STATE_OTHER, PROCESS_STATE_UNSPECIFIED));
- assertThat(statsLayout.getUidUsageDuration(uidStats)).isEqualTo(0);
+ if (nonZero) {
+ assertThat(statsLayout.getUidUsageDuration(uidStats)).isEqualTo(0);
+ }
stats.getUidStats(uidStats, APP_UID2,
states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
@@ -105,11 +109,14 @@
stats.getUidStats(uidStats, APP_UID2,
states(POWER_STATE_BATTERY, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
assertThat(statsLayout.getUidUsageDuration(uidStats)).isEqualTo(8500);
- stats.getUidStats(uidStats, APP_UID2,
+ nonZero = stats.getUidStats(uidStats, APP_UID2,
states(POWER_STATE_BATTERY, SCREEN_STATE_OTHER, PROCESS_STATE_UNSPECIFIED));
- assertThat(statsLayout.getUidUsageDuration(uidStats)).isEqualTo(0);
+ if (nonZero) {
+ assertThat(statsLayout.getUidUsageDuration(uidStats)).isEqualTo(0);
+ }
}
+ @SuppressLint("CheckResult")
@Test
public void fuelgaugeAvailable() {
AggregatedPowerStats aggregatedPowerStats = prepareAggregatedPowerStats(true);
@@ -138,6 +145,7 @@
assertThat(dischargeDuration).isWithin(5).of(6000);
}
+ @SuppressLint("CheckResult")
@Test
public void fuelgaugeUnavailable() {
AggregatedPowerStats aggregatedPowerStats = prepareAggregatedPowerStats(false);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessorTest.java
index 2ff7dde..e6e7f6e 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessorTest.java
@@ -30,6 +30,7 @@
import static com.google.common.truth.Truth.assertThat;
+import android.annotation.SuppressLint;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.PersistableBundle;
@@ -74,6 +75,7 @@
}
}
+ @SuppressLint("CheckResult")
@Test
public void powerProfileModel() {
BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout();
@@ -138,12 +140,14 @@
assertThat(statsLayout.getUidPowerEstimate(uidStats))
.isWithin(PRECISION).of(expectedPower2);
- stats.getUidStats(uidStats, APP_UID2,
- states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
- assertThat(statsLayout.getUidPowerEstimate(uidStats))
- .isWithin(PRECISION).of(0);
+ if (stats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED))) {
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0);
+ }
}
+ @SuppressLint("CheckResult")
@Test
public void energyConsumerModel() {
BinaryStatePowerStatsLayout
@@ -232,10 +236,11 @@
assertThat(statsLayout.getUidPowerEstimate(uidStats))
.isWithin(PRECISION).of(expectedPower2);
- stats.getUidStats(uidStats, APP_UID2,
- states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
- assertThat(statsLayout.getUidPowerEstimate(uidStats))
- .isWithin(PRECISION).of(0);
+ if (stats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED))) {
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0);
+ }
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BluetoothPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BluetoothPowerStatsProcessorTest.java
index 6013186..6d7119d 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BluetoothPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BluetoothPowerStatsProcessorTest.java
@@ -33,6 +33,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.annotation.SuppressLint;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.UidTraffic;
@@ -161,6 +162,7 @@
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)).thenReturn(true);
}
+ @SuppressLint("CheckResult")
@Test
public void powerProfileModel_mostlyDataTransfer() {
// No power monitoring hardware
@@ -262,6 +264,7 @@
.isWithin(PRECISION).of(expectedPower2 * 0.75);
}
+ @SuppressLint("CheckResult")
@Test
public void powerProfileModel_mostlyScan() {
// No power monitoring hardware
@@ -361,6 +364,7 @@
.isWithin(PRECISION).of(expectedPower2 * 0.75);
}
+ @SuppressLint("CheckResult")
@Test
public void consumedEnergyModel() {
when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(VOLTAGE_MV);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CameraPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CameraPowerStatsTest.java
index 23642de..a959632 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CameraPowerStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CameraPowerStatsTest.java
@@ -33,6 +33,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
+import android.annotation.SuppressLint;
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.os.BatteryConsumer;
@@ -117,6 +118,7 @@
mMonotonicClock = new MonotonicClock(0, mStatsRule.getMockClock());
}
+ @SuppressLint("CheckResult")
@Test
public void energyConsumerModel() {
when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(VOLTAGE_MV);
@@ -211,10 +213,11 @@
assertThat(statsLayout.getUidPowerEstimate(uidStats))
.isWithin(PRECISION).of(expectedPower2);
- stats.getUidStats(uidStats, APP_UID2,
- states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
- assertThat(statsLayout.getUidPowerEstimate(uidStats))
- .isWithin(PRECISION).of(0);
+ if (stats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED))) {
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0);
+ }
}
private BatteryStats.HistoryItem buildHistoryItem(int timestamp, boolean stateOn,
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsTest.java
index 42baba7..a421675 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsTest.java
@@ -33,6 +33,7 @@
import static org.mockito.Mockito.when;
+import android.annotation.SuppressLint;
import android.hardware.power.stats.EnergyConsumerAttribution;
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
@@ -154,6 +155,7 @@
.isEqualTo(6000);
}
+ @SuppressLint("CheckResult")
@Test
public void processStats() throws Exception {
AggregatedPowerStats aggregatedPowerStats = createAggregatedPowerStats();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/GnssPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/GnssPowerStatsTest.java
index c63267e..b4f2113 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/GnssPowerStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/GnssPowerStatsTest.java
@@ -33,6 +33,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
+import android.annotation.SuppressLint;
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.location.GnssSignalQuality;
@@ -122,6 +123,7 @@
mHistoryItem.clear();
}
+ @SuppressLint("CheckResult")
@Test
public void powerProfileModel() {
// ODPM unsupported
@@ -206,12 +208,14 @@
assertThat(statsLayout.getUidPowerEstimate(uidStats))
.isWithin(PRECISION).of(0.51111);
- stats.getUidStats(uidStats, APP_UID2,
- states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
- assertThat(statsLayout.getUidPowerEstimate(uidStats))
- .isWithin(PRECISION).of(0);
+ if (stats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED))) {
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0);
+ }
}
+ @SuppressLint("CheckResult")
@Test
public void initialStateGnssOn() {
// ODPM unsupported
@@ -285,12 +289,14 @@
assertThat(statsLayout.getUidPowerEstimate(uidStats))
.isWithin(PRECISION).of(0.51111);
- stats.getUidStats(uidStats, APP_UID2,
- states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
- assertThat(statsLayout.getUidPowerEstimate(uidStats))
- .isWithin(PRECISION).of(0);
+ if (stats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED))) {
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0);
+ }
}
+ @SuppressLint("CheckResult")
@Test
public void energyConsumerModel() {
when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(VOLTAGE_MV);
@@ -386,10 +392,11 @@
assertThat(statsLayout.getUidPowerEstimate(uidStats))
.isWithin(PRECISION).of(expectedPower2);
- stats.getUidStats(uidStats, APP_UID2,
- states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
- assertThat(statsLayout.getUidPowerEstimate(uidStats))
- .isWithin(PRECISION).of(0);
+ if (stats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED))) {
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0);
+ }
}
private BatteryStats.HistoryItem buildHistoryItemInitialStateGpsOn(long timestamp) {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessorTest.java
index 6acd368..3dc4017 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessorTest.java
@@ -39,6 +39,7 @@
import static org.mockito.Mockito.when;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.power.stats.EnergyConsumerResult;
@@ -170,6 +171,7 @@
.thenAnswer(invocation -> invocation.getArgument(0));
}
+ @SuppressLint("CheckResult")
@Test
public void powerProfileModel() {
// No power monitoring hardware
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStateStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStateStatsTest.java
index 3b614bd..9abb06f 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStateStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStateStatsTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertThrows;
+import android.annotation.SuppressLint;
import android.os.BatteryConsumer;
import androidx.test.filters.SmallTest;
@@ -124,6 +125,7 @@
assertThat(e.getMessage()).contains("40");
}
+ @SuppressLint("CheckResult")
@Test
public void multiStateStats_aggregation() {
MultiStateStats.Factory factory = makeFactory(true, true, false);
@@ -159,9 +161,9 @@
// (400 - 100) * 0 + (600 - 400) * 0.5
assertThat(stats).isEqualTo(new long[]{100, 100});
- multiStateStats.getStats(stats, new int[]{1, BatteryConsumer.PROCESS_STATE_BACKGROUND, 0});
// Never been in this composite state
- assertThat(stats).isEqualTo(new long[]{0, 0});
+ assertThat(multiStateStats.getStats(stats,
+ new int[]{1, BatteryConsumer.PROCESS_STATE_BACKGROUND, 0})).isFalse();
}
@Test
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessorTest.java
index a20274f..2f742d7 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessorTest.java
@@ -29,6 +29,7 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.when;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.power.stats.EnergyConsumerType;
@@ -159,6 +160,7 @@
mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem");
}
+ @SuppressLint("CheckResult")
@Test
public void copyEstimatesFromMobileRadioPowerStats() {
AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/ScreenPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/ScreenPowerStatsProcessorTest.java
index 1852165..31456a15 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/ScreenPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/ScreenPowerStatsProcessorTest.java
@@ -29,6 +29,7 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.when;
+import android.annotation.SuppressLint;
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.os.BatteryConsumer;
@@ -278,6 +279,7 @@
.of(expectedDozePowerEstimate);
}
+ @SuppressLint("CheckResult")
private void assertUidPowerEstimate(
PowerComponentAggregatedPowerStats aggregatedStats, int uid,
int powerState, int screenState, double expectedScreenPowerEstimate) {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/SensorPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/SensorPowerStatsProcessorTest.java
index d972604..c2f01d1 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/SensorPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/SensorPowerStatsProcessorTest.java
@@ -33,6 +33,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.annotation.SuppressLint;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.input.InputSensorInfo;
@@ -91,6 +92,7 @@
List.of(sensor1, sensor2, sensor3));
}
+ @SuppressLint("CheckResult")
@Test
public void testPowerEstimation() {
PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WakelockPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WakelockPowerStatsProcessorTest.java
index 8257d71..5ac7216 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WakelockPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WakelockPowerStatsProcessorTest.java
@@ -31,6 +31,7 @@
import static org.mockito.Mockito.mock;
+import android.annotation.SuppressLint;
import android.os.BatteryConsumer;
import android.os.PersistableBundle;
import android.os.Process;
@@ -123,6 +124,7 @@
return history;
}
+ @SuppressLint("CheckResult")
private void assertAggregatedPowerStats(AggregatedPowerStats aggregatedPowerStats) {
PowerComponentAggregatedPowerStats stats =
aggregatedPowerStats.getPowerComponentStats(POWER_COMPONENT_WAKELOCK);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WifiPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WifiPowerStatsProcessorTest.java
index bd92a84..e36056a 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WifiPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WifiPowerStatsProcessorTest.java
@@ -39,6 +39,7 @@
import static org.mockito.Mockito.when;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.power.stats.EnergyConsumerResult;
@@ -198,6 +199,7 @@
mBatteryStats = mStatsRule.getBatteryStats();
}
+ @SuppressLint("CheckResult")
@Test
public void powerProfileModel_powerController() {
when(mWifiManager.isEnhancedPowerReportingSupported()).thenReturn(true);
@@ -310,6 +312,7 @@
.isWithin(PRECISION).of(expectedPower2 * 0.75);
}
+ @SuppressLint("CheckResult")
@Test
public void consumedEnergyModel_powerController() {
when(mWifiManager.isEnhancedPowerReportingSupported()).thenReturn(true);
@@ -424,6 +427,7 @@
.isWithin(PRECISION).of(expectedPower2 * 0.75);
}
+ @SuppressLint("CheckResult")
@Test
public void powerProfileModel_noPowerController() {
when(mWifiManager.isEnhancedPowerReportingSupported()).thenReturn(false);
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index d702cae..009ce88 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -33,6 +33,9 @@
"test-apps/DisplayManagerTestApp/src/**/*.java",
],
+ kotlincflags: [
+ "-Werror",
+ ],
static_libs: [
"a11ychecker",
"aatf",
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
index 7f60caa..0745c68 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
@@ -25,6 +25,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.Context;
import android.platform.test.annotations.DisableFlags;
@@ -400,6 +401,46 @@
.isNotEqualTo(initialScheduledTime);
}
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void pauseButton_flagOn_clickNotTriggeredWhenPaused() {
+ injectFakeMouseActionHoverMoveEvent();
+
+ // Pause autoclick.
+ AutoclickTypePanel mockAutoclickTypePanel = mock(AutoclickTypePanel.class);
+ when(mockAutoclickTypePanel.isPaused()).thenReturn(true);
+ mController.mAutoclickTypePanel = mockAutoclickTypePanel;
+
+ // Send hover move event.
+ MotionEvent hoverMove = MotionEvent.obtain(
+ /* downTime= */ 0,
+ /* eventTime= */ 100,
+ /* action= */ MotionEvent.ACTION_HOVER_MOVE,
+ /* x= */ 30f,
+ /* y= */ 0f,
+ /* metaState= */ 0);
+ hoverMove.setSource(InputDevice.SOURCE_MOUSE);
+ mController.onMotionEvent(hoverMove, hoverMove, /* policyFlags= */ 0);
+
+ // Verify there is not a pending click.
+ assertThat(mController.mClickScheduler.getIsActiveForTesting()).isFalse();
+ assertThat(mController.mClickScheduler.getScheduledClickTimeForTesting()).isEqualTo(-1);
+
+ // Resume autoclick.
+ when(mockAutoclickTypePanel.isPaused()).thenReturn(false);
+
+ // Send initial move event again. Because this is the first recorded move, a click won't be
+ // scheduled.
+ injectFakeMouseActionHoverMoveEvent();
+ assertThat(mController.mClickScheduler.getIsActiveForTesting()).isFalse();
+ assertThat(mController.mClickScheduler.getScheduledClickTimeForTesting()).isEqualTo(-1);
+
+ // Send move again to trigger click and verify there is now a pending click.
+ mController.onMotionEvent(hoverMove, hoverMove, /* policyFlags= */ 0);
+ assertThat(mController.mClickScheduler.getIsActiveForTesting()).isTrue();
+ assertThat(mController.mClickScheduler.getScheduledClickTimeForTesting()).isNotEqualTo(-1);
+ }
+
private void injectFakeMouseActionHoverMoveEvent() {
MotionEvent event = getFakeMotionHoverMoveEvent();
event.setSource(InputDevice.SOURCE_MOUSE);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java
index f3016f4..c60c4b6 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java
@@ -21,6 +21,9 @@
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_LEFT_CLICK;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_SCROLL;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AutoclickType;
+import static com.android.server.accessibility.autoclick.AutoclickTypePanel.CORNER_BOTTOM_LEFT;
+import static com.android.server.accessibility.autoclick.AutoclickTypePanel.CORNER_BOTTOM_RIGHT;
+import static com.android.server.accessibility.autoclick.AutoclickTypePanel.CORNER_TOP_RIGHT;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.ClickPanelControllerInterface;
import static com.google.common.truth.Truth.assertThat;
@@ -31,6 +34,7 @@
import android.testing.TestableContext;
import android.testing.TestableLooper;
import android.view.Gravity;
+import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.LinearLayout;
@@ -68,6 +72,7 @@
private LinearLayout mPositionButton;
private @AutoclickType int mActiveClickType = AUTOCLICK_TYPE_LEFT_CLICK;
+ private boolean mPaused;
private final ClickPanelControllerInterface clickPanelController =
new ClickPanelControllerInterface() {
@@ -77,7 +82,9 @@
}
@Override
- public void toggleAutoclickPause() {}
+ public void toggleAutoclickPause(boolean paused) {
+ mPaused = paused;
+ }
};
@Before
@@ -199,6 +206,116 @@
}
}
+ @Test
+ public void pauseButton_onClick() {
+ mPauseButton.callOnClick();
+ assertThat(mPaused).isTrue();
+ assertThat(mAutoclickTypePanel.isPaused()).isTrue();
+
+ mPauseButton.callOnClick();
+ assertThat(mPaused).isFalse();
+ assertThat(mAutoclickTypePanel.isPaused()).isFalse();
+ }
+
+ @Test
+ public void onTouch_dragMove_updatesPosition() {
+ View contentView = mAutoclickTypePanel.getContentViewForTesting();
+ WindowManager.LayoutParams params = mAutoclickTypePanel.getLayoutParamsForTesting();
+ int[] panelLocation = new int[2];
+ contentView.getLocationOnScreen(panelLocation);
+
+ // Define movement delta for both x and y directions.
+ int delta = 15;
+
+ // Dispatch initial down event.
+ float touchX = panelLocation[0] + 10;
+ float touchY = panelLocation[1] + 10;
+ MotionEvent downEvent = MotionEvent.obtain(
+ 0, 0,
+ MotionEvent.ACTION_DOWN, touchX, touchY, 0);
+ contentView.dispatchTouchEvent(downEvent);
+
+ // Create move event with delta, move from (x, y) to (x + delta, y + delta)
+ MotionEvent moveEvent = MotionEvent.obtain(
+ 0, 0,
+ MotionEvent.ACTION_MOVE, touchX + delta, touchY + delta, 0);
+ contentView.dispatchTouchEvent(moveEvent);
+
+ // Verify position update.
+ assertThat(mAutoclickTypePanel.getIsDraggingForTesting()).isTrue();
+ assertThat(params.gravity).isEqualTo(Gravity.LEFT | Gravity.TOP);
+ assertThat(params.x).isEqualTo(panelLocation[0] + delta);
+ assertThat(params.y).isEqualTo(panelLocation[1] + delta);
+ }
+
+ @Test
+ public void dragAndEndAtRight_snapsToRightSide() {
+ View contentView = mAutoclickTypePanel.getContentViewForTesting();
+ WindowManager.LayoutParams params = mAutoclickTypePanel.getLayoutParamsForTesting();
+ int[] panelLocation = new int[2];
+ contentView.getLocationOnScreen(panelLocation);
+
+ int screenWidth = mTestableContext.getResources().getDisplayMetrics().widthPixels;
+
+ // Verify initial corner is bottom-right.
+ assertThat(mAutoclickTypePanel.getCurrentCornerIndexForTesting())
+ .isEqualTo(CORNER_BOTTOM_RIGHT);
+
+ dispatchDragSequence(contentView,
+ /* startX =*/ panelLocation[0] + 10, /* startY =*/ panelLocation[1] + 10,
+ /* endX =*/ (float) (screenWidth * 3) / 4, /* endY =*/ panelLocation[1] + 10);
+
+ // Verify snapping to the right.
+ assertThat(params.gravity).isEqualTo(Gravity.END | Gravity.TOP);
+ assertThat(mAutoclickTypePanel.getCurrentCornerIndexForTesting())
+ .isEqualTo(CORNER_TOP_RIGHT);
+ }
+
+ @Test
+ public void dragAndEndAtLeft_snapsToLeftSide() {
+ View contentView = mAutoclickTypePanel.getContentViewForTesting();
+ WindowManager.LayoutParams params = mAutoclickTypePanel.getLayoutParamsForTesting();
+ int[] panelLocation = new int[2];
+ contentView.getLocationOnScreen(panelLocation);
+
+ int screenWidth = mTestableContext.getResources().getDisplayMetrics().widthPixels;
+
+ // Verify initial corner is bottom-right.
+ assertThat(mAutoclickTypePanel.getCurrentCornerIndexForTesting())
+ .isEqualTo(CORNER_BOTTOM_RIGHT);
+
+ dispatchDragSequence(contentView,
+ /* startX =*/ panelLocation[0] + 10, /* startY =*/ panelLocation[1] + 10,
+ /* endX =*/ (float) screenWidth / 4, /* endY =*/ panelLocation[1] + 10);
+
+ // Verify snapping to the left.
+ assertThat(params.gravity).isEqualTo(Gravity.START | Gravity.TOP);
+ assertThat(mAutoclickTypePanel.getCurrentCornerIndexForTesting())
+ .isEqualTo(CORNER_BOTTOM_LEFT);
+ }
+
+ // Helper method to handle drag event sequences
+ private void dispatchDragSequence(View view, float startX, float startY, float endX,
+ float endY) {
+ // Down event
+ MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, startX, startY,
+ 0);
+ view.dispatchTouchEvent(downEvent);
+
+ // Move event
+ MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, endX, endY, 0);
+ view.dispatchTouchEvent(moveEvent);
+
+ // Up event
+ MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, endX, endY, 0);
+ view.dispatchTouchEvent(upEvent);
+
+ // Clean up
+ downEvent.recycle();
+ moveEvent.recycle();
+ upEvent.recycle();
+ }
+
private void verifyButtonHasSelectedStyle(@NonNull LinearLayout button) {
GradientDrawable gradientDrawable = (GradientDrawable) button.getBackground();
assertThat(gradientDrawable.getColor().getDefaultColor())
@@ -206,7 +323,7 @@
}
private void verifyPanelPosition(int[] expectedPosition) {
- WindowManager.LayoutParams params = mAutoclickTypePanel.getLayoutParams();
+ WindowManager.LayoutParams params = mAutoclickTypePanel.getLayoutParamsForTesting();
assertThat(mAutoclickTypePanel.getCurrentCornerIndexForTesting()).isEqualTo(
expectedPosition[0]);
assertThat(params.gravity).isEqualTo(expectedPosition[1]);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 9f5dd93..5c126d1 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -311,8 +311,7 @@
mMockFullScreenMagnificationVibrationHelper,
mMockMagnificationLogger,
ViewConfiguration.get(mContext),
- mMockOneFingerPanningSettingsProvider,
- new MouseEventHandler(mFullScreenMagnificationController));
+ mMockOneFingerPanningSettingsProvider);
// OverscrollHandler is only supported on watches.
// @See config_enable_a11y_fullscreen_magnification_overscroll_handler
if (isWatch()) {
@@ -482,8 +481,8 @@
@Test
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
public void testDisablingTripleTap_removesInputLag() {
- mMgh = newInstance(/* detectSingleFingerTripleTap */ false,
- /* detectTwoFingerTripleTap */ true, /* detectShortcut */ true);
+ mMgh = newInstance(/* detectSingleFingerTripleTap= */ false,
+ /* detectTwoFingerTripleTap= */ true, /* detectShortcutTrigger= */ true);
goFromStateIdleTo(STATE_IDLE);
allowEventDelegation();
tap();
@@ -494,8 +493,8 @@
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
public void testDisablingSingleFingerTripleTapAndTwoFingerTripleTap_removesInputLag() {
- mMgh = newInstance(/* detectSingleFingerTripleTap */ false,
- /* detectTwoFingerTripleTap */ false, /* detectShortcut */ true);
+ mMgh = newInstance(/* detectSingleFingerTripleTap= */ false,
+ /* detectTwoFingerTripleTap= */ false, /* detectShortcutTrigger= */ true);
goFromStateIdleTo(STATE_IDLE);
allowEventDelegation();
tap();
@@ -1420,12 +1419,6 @@
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
- public void testMouseMoveEventsDoNotMoveMagnifierViewport() {
- runMoveEventsDoNotMoveMagnifierViewport(InputDevice.SOURCE_MOUSE);
- }
-
- @Test
public void testStylusMoveEventsDoNotMoveMagnifierViewport() {
runMoveEventsDoNotMoveMagnifierViewport(InputDevice.SOURCE_STYLUS);
}
@@ -1441,7 +1434,7 @@
(INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f;
float scale = 5.6f; // value is unimportant but unique among tests to increase coverage.
mFullScreenMagnificationController.setScaleAndCenter(
- DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1);
+ DISPLAY_0, scale, centerX, centerY, true, /* animate= */ false, 1);
centerX = mFullScreenMagnificationController.getCenterX(DISPLAY_0);
centerY = mFullScreenMagnificationController.getCenterY(DISPLAY_0);
@@ -1474,55 +1467,36 @@
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
- public void testMouseHoverMoveEventsDoNotMoveMagnifierViewport() {
- runHoverMoveEventsDoNotMoveMagnifierViewport(InputDevice.SOURCE_MOUSE);
- }
-
- @Test
- @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
- public void testStylusHoverMoveEventsDoNotMoveMagnifierViewport() {
- runHoverMoveEventsDoNotMoveMagnifierViewport(InputDevice.SOURCE_STYLUS);
- }
-
- @Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void testMouseHoverMoveEventsMoveMagnifierViewport() {
runHoverMovesViewportTest(InputDevice.SOURCE_MOUSE);
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void testStylusHoverMoveEventsMoveMagnifierViewport() {
runHoverMovesViewportTest(InputDevice.SOURCE_STYLUS);
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void testMouseDownEventsDoNotMoveMagnifierViewport() {
runDownDoesNotMoveViewportTest(InputDevice.SOURCE_MOUSE);
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void testStylusDownEventsDoNotMoveMagnifierViewport() {
runDownDoesNotMoveViewportTest(InputDevice.SOURCE_STYLUS);
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void testMouseUpEventsDoNotMoveMagnifierViewport() {
runUpDoesNotMoveViewportTest(InputDevice.SOURCE_MOUSE);
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void testStylusUpEventsDoNotMoveMagnifierViewport() {
runUpDoesNotMoveViewportTest(InputDevice.SOURCE_STYLUS);
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void testMouseMoveEventsMoveMagnifierViewport() {
final EventCaptor eventCaptor = new EventCaptor();
mMgh.setNext(eventCaptor);
@@ -1533,7 +1507,7 @@
(INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f;
float scale = 6.2f; // value is unimportant but unique among tests to increase coverage.
mFullScreenMagnificationController.setScaleAndCenter(
- DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1);
+ DISPLAY_0, scale, centerX, centerY, true, /* animate= */ false, 1);
MotionEvent event = mouseEvent(centerX, centerY, ACTION_HOVER_MOVE);
send(event, InputDevice.SOURCE_MOUSE);
fastForward(20);
@@ -1574,7 +1548,7 @@
(INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f;
float scale = 4.0f; // value is unimportant but unique among tests to increase coverage.
mFullScreenMagnificationController.setScaleAndCenter(
- DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1);
+ DISPLAY_0, scale, centerX, centerY, true, /* animate= */ false, 1);
// HOVER_MOVE should change magnifier viewport.
MotionEvent event = motionEvent(centerX + 20, centerY, ACTION_HOVER_MOVE);
@@ -1618,7 +1592,7 @@
(INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f;
float scale = 5.3f; // value is unimportant but unique among tests to increase coverage.
mFullScreenMagnificationController.setScaleAndCenter(
- DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1);
+ DISPLAY_0, scale, centerX, centerY, true, /* animate= */ false, 1);
MotionEvent event = motionEvent(centerX, centerY, ACTION_HOVER_MOVE);
send(event, source);
fastForward(20);
@@ -1652,7 +1626,7 @@
(INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f;
float scale = 2.7f; // value is unimportant but unique among tests to increase coverage.
mFullScreenMagnificationController.setScaleAndCenter(
- DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1);
+ DISPLAY_0, scale, centerX, centerY, true, /* animate= */ false, 1);
MotionEvent event = motionEvent(centerX, centerY, ACTION_HOVER_MOVE);
send(event, source);
fastForward(20);
@@ -1688,7 +1662,7 @@
(INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f;
float scale = 3.8f; // value is unimportant but unique among tests to increase coverage.
mFullScreenMagnificationController.setScaleAndCenter(
- DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1);
+ DISPLAY_0, scale, centerX, centerY, true, /* animate= */ false, 1);
centerX = mFullScreenMagnificationController.getCenterX(DISPLAY_0);
centerY = mFullScreenMagnificationController.getCenterY(DISPLAY_0);
@@ -1725,7 +1699,7 @@
(INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f;
float scale = 4.0f; // value is unimportant but unique among tests to increase coverage.
mFullScreenMagnificationController.setScaleAndCenter(
- DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1);
+ DISPLAY_0, scale, centerX, centerY, true, /* animate= */ false, 1);
centerX = mFullScreenMagnificationController.getCenterX(DISPLAY_0);
centerY = mFullScreenMagnificationController.getCenterY(DISPLAY_0);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
index 45c157d..203e655 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
@@ -21,15 +21,11 @@
import static android.view.MotionEvent.ACTION_HOVER_MOVE;
import static android.view.MotionEvent.ACTION_UP;
-import static junit.framework.Assert.assertFalse;
-
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.testng.AssertJUnit.assertTrue;
import android.annotation.NonNull;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
@@ -39,7 +35,6 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.server.accessibility.AccessibilityTraceManager;
-import com.android.server.accessibility.Flags;
import org.junit.Before;
import org.junit.Rule;
@@ -93,7 +88,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void onMotionEvent_isFromMouse_handleMouseOrStylusEvent() {
final MotionEvent mouseEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0);
mouseEvent.setSource(InputDevice.SOURCE_MOUSE);
@@ -108,7 +102,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void onMotionEvent_isFromStylus_handleMouseOrStylusEvent() {
final MotionEvent stylusEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0);
stylusEvent.setSource(InputDevice.SOURCE_STYLUS);
@@ -123,36 +116,6 @@
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
- public void onMotionEvent_isFromMouse_handleMouseOrStylusEventNotCalled() {
- final MotionEvent mouseEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0);
- mouseEvent.setSource(InputDevice.SOURCE_MOUSE);
-
- mMgh.onMotionEvent(mouseEvent, mouseEvent, /* policyFlags= */ 0);
-
- try {
- assertFalse(mMgh.mIsHandleMouseOrStylusEventCalled);
- } finally {
- mouseEvent.recycle();
- }
- }
-
- @Test
- @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
- public void onMotionEvent_isFromStylus_handleMouseOrStylusEventNotCalled() {
- final MotionEvent stylusEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0);
- stylusEvent.setSource(InputDevice.SOURCE_STYLUS);
-
- mMgh.onMotionEvent(stylusEvent, stylusEvent, /* policyFlags= */ 0);
-
- try {
- assertFalse(mMgh.mIsHandleMouseOrStylusEventCalled);
- } finally {
- stylusEvent.recycle();
- }
- }
-
- @Test
public void onMotionEvent_downEvent_handleInteractionStart() {
final MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
downEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
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/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
index ad3855f..b1cf905 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
@@ -37,10 +37,12 @@
import android.util.Xml;
import androidx.annotation.NonNull;
-import androidx.test.runner.AndroidJUnit4;
import com.android.modules.utils.TypedXmlPullParser;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -56,7 +58,7 @@
import javax.annotation.Nullable;
-@RunWith(AndroidJUnit4.class)
+@RunWith(JUnitParamsRunner.class)
public class OverlayManagerSettingsTests {
private OverlayManagerSettings mSettings;
private static final int USER_0 = 0;
@@ -439,6 +441,39 @@
}
@Test
+ @Parameters(method = "getPreviousVersions")
+ public void testRestoreWithPreviousVersion(int version) throws Exception {
+ final String xml =
+ "<?xml version='1.0' encoding='utf-8' standalone='yes'?>\n"
+ + "<overlays version='" + version + "'>\n"
+ + "<item packageName='com.test.overlay'\n"
+ + " overlayName='test'\n"
+ + " userId='1234'\n"
+ + " targetPackageName='com.test.target'\n"
+ + " baseCodePath='/data/app/com.test.overlay-1/base.apk'\n"
+ + " state='" + STATE_DISABLED + "'\n"
+ + " isEnabled='false'\n"
+ + " category='test-category'\n"
+ + " isStatic='false'\n"
+ + " priority='0' />\n"
+ + "</overlays>\n";
+ ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes(UTF_8));
+
+ mSettings.restore(is);
+ final OverlayIdentifier identifier = new OverlayIdentifier("com.test.overlay", "test");
+ OverlayInfo oi = mSettings.getOverlayInfo(identifier, 1234);
+ assertNotNull(oi);
+ assertEquals("com.test.overlay", oi.packageName);
+ assertEquals("test", oi.overlayName);
+ assertEquals("com.test.target", oi.targetPackageName);
+ assertEquals("/data/app/com.test.overlay-1/base.apk", oi.baseCodePath);
+ assertEquals(1234, oi.userId);
+ assertEquals(STATE_DISABLED, oi.state);
+ assertFalse(mSettings.getEnabled(identifier, 1234));
+ assertTrue(oi.constraints.isEmpty());
+ }
+
+ @Test
public void testPersistAndRestore() throws Exception {
insertSetting(OVERLAY_A_USER0);
insertSetting(OVERLAY_B_USER1);
@@ -585,4 +620,11 @@
TextUtils.join(",", expected), TextUtils.join(",", actual)));
}
}
+
+ private static Integer[] getPreviousVersions() {
+ return new Integer[]{
+ 3,
+ 4,
+ };
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 58f7622..ad1537e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -261,7 +261,7 @@
// === Test for app side APIs ===
/** Test for {@link android.content.pm.ShortcutManager#getMaxShortcutCountForActivity()} */
- public void testGetMaxDynamicShortcutCount() {
+ public void disabled_testGetMaxDynamicShortcutCount() {
assertEquals(MAX_SHORTCUTS, mManager.getMaxShortcutCountForActivity());
}
@@ -793,7 +793,7 @@
assertEquals(2, mManager.getRemainingCallCount());
}
- public void testDeleteAllDynamicShortcuts() {
+ public void disabled_testDeleteAllDynamicShortcuts() {
final ShortcutInfo si1 = makeShortcut("shortcut1");
final ShortcutInfo si2 = makeShortcut("shortcut2");
final ShortcutInfo si3 = makeShortcut("shortcut3");
@@ -1036,7 +1036,7 @@
*/
}
- public void testCleanupDanglingBitmaps() throws Exception {
+ public void disabled_testCleanupDanglingBitmaps() throws Exception {
assertBitmapDirectories(USER_10, EMPTY_STRINGS);
assertBitmapDirectories(USER_11, EMPTY_STRINGS);
@@ -1702,7 +1702,7 @@
"s2");
}
- public void testCachedShortcuts_accessShortcutsPermission() {
+ public void disabled_testCachedShortcuts_accessShortcutsPermission() {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"),
makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"),
@@ -1744,7 +1744,7 @@
assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), "s3");
}
- public void testCachedShortcuts_canPassShortcutLimit() {
+ public void disabled_testCachedShortcuts_canPassShortcutLimit() {
// Change the max number of shortcuts.
mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=4");
@@ -2362,7 +2362,7 @@
* This is similar to the above test, except it used "disable" instead of "remove". It also
* does "enable".
*/
- public void testDisableAndEnableShortcuts() {
+ public void disabled_testDisableAndEnableShortcuts() {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000);
final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000);
@@ -2487,7 +2487,7 @@
});
}
- public void testDisableShortcuts_thenRepublish() {
+ public void disabled_testDisableShortcuts_thenRepublish() {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
@@ -4230,7 +4230,7 @@
// TODO Check all other fields
}
- public void testCleanupPackage() {
+ public void disabled_testCleanupPackage() {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s0_1"))));
@@ -4507,7 +4507,7 @@
mService.saveDirtyInfo();
}
- public void testCleanupPackage_republishManifests() {
+ public void disabled_testCleanupPackage_republishManifests() {
addManifestShortcutResource(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_2);
@@ -4847,7 +4847,7 @@
assertEquals(expected, spi.canRestoreTo(mService, pi, true));
}
- public void testCanRestoreTo() {
+ public void disabled_testCanRestoreTo() {
addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sig1");
addPackage(CALLING_PACKAGE_2, CALLING_UID_2, 10, "sig1", "sig2");
addPackage(CALLING_PACKAGE_3, CALLING_UID_3, 10, "sig1");
@@ -5785,7 +5785,7 @@
checkBackupAndRestore_success(/*firstRestore=*/ true);
}
- public void testBackupAndRestore_restoreToSuperSetSignatures() {
+ public void disabled_testBackupAndRestore_restoreToSuperSetSignatures() {
prepareForBackupTest();
// Change package signatures.
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/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 0ab11e0..f8387a4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -300,7 +300,7 @@
@Parameters(name = "{0}")
public static List<FlagsParameterization> getParams() {
- return FlagsParameterization.allCombinationsOf(FLAG_MODES_UI, FLAG_BACKUP_RESTORE_LOGGING,
+ return FlagsParameterization.allCombinationsOf(FLAG_BACKUP_RESTORE_LOGGING,
com.android.server.notification.Flags.FLAG_FIX_CALLING_UID_FROM_CPS);
}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java b/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java
index d5548a4..f091a65 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java
@@ -497,19 +497,19 @@
private VibratorController createEmptyVibratorController(int vibratorId) {
return new FakeVibratorControllerProvider(mTestLooper.getLooper())
- .newVibratorController(vibratorId, (id, vibrationId) -> {});
+ .newVibratorController(vibratorId, (id, vibrationId, stepId) -> {});
}
private VibratorController createBasicVibratorController(int vibratorId) {
FakeVibratorControllerProvider provider = createVibratorProviderWithEffects(
IVibrator.CAP_COMPOSE_EFFECTS);
- return provider.newVibratorController(vibratorId, (id, vibrationId) -> {});
+ return provider.newVibratorController(vibratorId, (id, vibrationId, stepId) -> {});
}
private VibratorController createPwleWithoutFrequenciesVibratorController(int vibratorId) {
FakeVibratorControllerProvider provider = createVibratorProviderWithEffects(
IVibrator.CAP_COMPOSE_EFFECTS, IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
- return provider.newVibratorController(vibratorId, (id, vibrationId) -> {});
+ return provider.newVibratorController(vibratorId, (id, vibrationId, stepId) -> {});
}
private VibratorController createPwleVibratorController(int vibratorId) {
@@ -519,7 +519,7 @@
provider.setMinFrequency(TEST_MIN_FREQUENCY);
provider.setFrequencyResolution(TEST_FREQUENCY_RESOLUTION);
provider.setMaxAmplitudes(TEST_AMPLITUDE_MAP);
- return provider.newVibratorController(vibratorId, (id, vibrationId) -> {});
+ return provider.newVibratorController(vibratorId, (id, vibrationId, stepId) -> {});
}
private VibratorController createPwleV2VibratorController(int vibratorId) {
@@ -538,7 +538,7 @@
provider.setMinEnvelopeEffectControlPointDurationMillis(
TEST_MIN_ENVELOPE_EFFECT_CONTROL_POINT_DURATION_MILLIS);
- return provider.newVibratorController(vibratorId, (id, vibrationId) -> {});
+ return provider.newVibratorController(vibratorId, (id, vibrationId, stepId) -> {});
}
private FakeVibratorControllerProvider createVibratorProviderWithEffects(int... capabilities) {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
index 5f2af0a..04335df 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -17,14 +17,17 @@
package com.android.server.vibrator;
import static android.os.VibrationAttributes.USAGE_RINGTONE;
+import static android.os.VibrationEffect.Composition.PRIMITIVE_CLICK;
+import static android.os.VibrationEffect.Composition.PRIMITIVE_SPIN;
+import static android.os.VibrationEffect.Composition.PRIMITIVE_TICK;
+import static android.os.VibrationEffect.EFFECT_CLICK;
+import static android.os.VibrationEffect.EFFECT_TICK;
import static android.os.VibrationEffect.VibrationParameter.targetAmplitude;
import static android.os.VibrationEffect.VibrationParameter.targetFrequency;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -66,7 +69,6 @@
import android.os.vibrator.VibrationEffectSegment;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -153,9 +155,10 @@
when(mPackageManagerInternalMock.getSystemUiServiceComponent())
.thenReturn(new ComponentName("", ""));
doAnswer(answer -> {
- mVibrationConductor.notifyVibratorComplete(answer.getArgument(0));
+ mVibrationConductor.notifyVibratorComplete(
+ answer.getArgument(0), answer.getArgument(2));
return null;
- }).when(mControllerCallbacks).onComplete(anyInt(), anyLong());
+ }).when(mControllerCallbacks).onComplete(anyInt(), anyLong(), anyLong());
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);
@@ -186,24 +189,24 @@
public void vibrate_noVibrator_ignoresVibration() {
mVibratorProviders.clear();
CombinedVibration effect = CombinedVibration.createParallel(
- VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
+ VibrationEffect.get(EFFECT_CLICK));
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
- verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibration.id));
+ verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.IGNORED_UNSUPPORTED);
}
@Test
public void vibrate_missingVibrators_ignoresVibration() {
CombinedVibration effect = CombinedVibration.startSequential()
- .addNext(2, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
- .addNext(3, VibrationEffect.get(VibrationEffect.EFFECT_TICK))
+ .addNext(2, VibrationEffect.get(EFFECT_CLICK))
+ .addNext(3, VibrationEffect.get(EFFECT_TICK))
.combine();
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
- verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibration.id));
+ verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.IGNORED_UNSUPPORTED);
}
@@ -217,13 +220,14 @@
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
- assertEquals(Arrays.asList(expectedOneShot(10)),
- mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
- assertEquals(expectedAmplitudes(100), mVibratorProviders.get(VIBRATOR_ID).getAmplitudes());
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id))
+ .containsExactly(expectedOneShot(10)).inOrder();
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes())
+ .containsExactlyElementsIn(expectedAmplitudes(100)).inOrder();
}
@Test
@@ -234,13 +238,13 @@
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
- assertEquals(Arrays.asList(expectedOneShot(10)),
- mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
- assertTrue(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().isEmpty());
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id))
+ .containsExactly(expectedOneShot(10)).inOrder();
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()).isEmpty();
}
@Test
@@ -254,18 +258,18 @@
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(15L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
- assertEquals(Arrays.asList(expectedOneShot(15)),
- mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
- assertEquals(expectedAmplitudes(1, 2, 3),
- mVibratorProviders.get(VIBRATOR_ID).getAmplitudes());
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id))
+ .containsExactly(expectedOneShot(15)).inOrder();
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes())
+ .containsExactlyElementsIn(expectedAmplitudes(1, 2, 3)).inOrder();
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+ @EnableFlags(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
public void vibrate_singleWaveformWithAdaptiveHapticsScaling_scalesAmplitudesProperly() {
// No user settings scale.
setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
@@ -282,16 +286,17 @@
waitForCompletion();
verify(mStatsLoggerMock, never()).logVibrationParamRequestTimeout(UID);
- assertEquals(Arrays.asList(expectedOneShot(15)),
- mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id))
+ .containsExactly(expectedOneShot(15)).inOrder();
List<Float> amplitudes = mVibratorProviders.get(VIBRATOR_ID).getAmplitudes();
for (int i = 0; i < amplitudes.size(); i++) {
- assertTrue(amplitudes.get(i) < 1 / 255f);
+ assertWithMessage("For amplitude index %s", i)
+ .that(amplitudes.get(i)).isLessThan(1 / 255f);
}
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+ @EnableFlags(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
public void vibrate_withVibrationParamsRequestStalling_timeoutRequestAndApplyNoScaling() {
// No user settings scale.
setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
@@ -306,10 +311,10 @@
waitForCompletion();
verify(mStatsLoggerMock).logVibrationParamRequestTimeout(UID);
- assertEquals(Arrays.asList(expectedOneShot(15)),
- mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
- assertEquals(expectedAmplitudes(1, 1, 1),
- mVibratorProviders.get(VIBRATOR_ID).getAmplitudes());
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id))
+ .containsExactly(expectedOneShot(15)).inOrder();
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes())
+ .containsExactlyElementsIn(expectedAmplitudes(1, 1, 1)).inOrder();
}
@Test
@@ -322,31 +327,33 @@
VibrationEffect effect = VibrationEffect.createWaveform(new long[]{5, 5, 5}, amplitudes, 0);
HalVibration vibration = startThreadAndDispatcher(effect);
- assertTrue(
+ assertThat(
waitUntil(() -> fakeVibrator.getAmplitudes().size() > 2 * amplitudes.length,
- TEST_TIMEOUT_MILLIS));
+ TEST_TIMEOUT_MILLIS)).isTrue();
// Vibration still running after 2 cycles.
- assertTrue(mThread.isRunningVibrationId(vibration.id));
- assertTrue(mControllers.get(VIBRATOR_ID).isVibrating());
+ assertThat(mThread.isRunningVibrationId(vibration.id)).isTrue();
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isTrue();
Vibration.EndInfo cancelVibrationInfo = new Vibration.EndInfo(Status.CANCELLED_SUPERSEDED,
new CallerInfo(VibrationAttributes.createForUsage(VibrationAttributes.USAGE_ALARM),
/* uid= */ 1, /* deviceId= */ -1, /* opPkg= */ null, /* reason= */ null));
mVibrationConductor.notifyCancelled(cancelVibrationInfo, /* immediate= */ false);
waitForCompletion();
- assertFalse(mThread.isRunningVibrationId(vibration.id));
+ assertThat(mThread.isRunningVibrationId(vibration.id)).isFalse();
verify(mManagerHooks).noteVibratorOn(eq(UID), anyLong());
verify(mManagerHooks).noteVibratorOff(eq(UID));
verifyCallbacksTriggered(vibration, Status.CANCELLED_SUPERSEDED);
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
List<Float> playedAmplitudes = fakeVibrator.getAmplitudes();
- assertFalse(fakeVibrator.getEffectSegments(vibration.id).isEmpty());
- assertFalse(playedAmplitudes.isEmpty());
+ assertThat(fakeVibrator.getEffectSegments(vibration.id)).isNotEmpty();
+ assertThat(playedAmplitudes).isNotEmpty();
for (int i = 0; i < playedAmplitudes.size(); i++) {
- assertEquals(amplitudes[i % amplitudes.length] / 255f, playedAmplitudes.get(i), 1e-5);
+ assertWithMessage("For amplitude index %s", i)
+ .that(amplitudes[i % amplitudes.length] / 255f)
+ .isWithin(1e-5f).of(playedAmplitudes.get(i));
}
}
@@ -361,15 +368,16 @@
new long[]{1, 10, 100}, amplitudes, 0);
HalVibration vibration = startThreadAndDispatcher(effect);
- assertTrue(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS));
+ assertThat(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS))
+ .isTrue();
mVibrationConductor.notifyCancelled(
new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
waitForCompletion();
verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_USER);
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
- assertEquals(Arrays.asList(expectedOneShot(5000)),
- fakeVibrator.getEffectSegments(vibration.id));
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
+ assertThat(fakeVibrator.getEffectSegments(vibration.id))
+ .containsExactly(expectedOneShot(5000)).inOrder();
}
@Test
@@ -388,7 +396,7 @@
assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id))
- .isEqualTo(expectedOneShots(100L, 150L));
+ .containsExactlyElementsIn(expectedOneShots(100L, 150L)).inOrder();
}
@Test
@@ -409,9 +417,10 @@
assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id))
- .isEqualTo(expectedOneShots(200L, 50L));
+ .containsExactlyElementsIn(expectedOneShots(200L, 50L)).inOrder();
}
+ @EnableFlags(Flags.FLAG_FIX_VIBRATION_THREAD_CALLBACK_HANDLING)
@LargeTest
@Test
public void vibrate_singleVibratorRepeatingPatternWithZeroDurationSteps_repeatsEffectCorrectly()
@@ -429,8 +438,8 @@
// 300ms ON (100ms + 200ms looping to the start and skipping first 0ms)
// 150ms ON (100ms + 50ms, skips 0ms)
// 300ms ON (100ms + 200ms looping to the start and skipping first 0ms)
- assertTrue(waitUntil(() -> fakeVibrator.getEffectSegments(vibration.id).size() >= 5,
- 5000L + TEST_TIMEOUT_MILLIS));
+ assertThat(waitUntil(() -> fakeVibrator.getEffectSegments(vibration.id).size() >= 5,
+ 5000L + TEST_TIMEOUT_MILLIS)).isTrue();
mVibrationConductor.notifyCancelled(
new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
waitForCompletion();
@@ -440,7 +449,32 @@
assertThat(
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id).subList(0, 5))
- .isEqualTo(expectedOneShots(200L, 150L, 300L, 150L, 300L));
+ .containsExactlyElementsIn(expectedOneShots(200L, 150L, 300L, 150L, 300L))
+ .inOrder();
+ }
+
+ @EnableFlags(Flags.FLAG_FIX_VIBRATION_THREAD_CALLBACK_HANDLING)
+ @Test
+ public void vibrate_singleVibratorPatternWithCallbackDelay_oldCallbacksIgnored() {
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
+ fakeVibrator.setCompletionCallbackDelay(100); // 100ms delay to notify service.
+ fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[]{0, 200, 50, 400}, /* repeat= */ -1);
+ HalVibration vibration = startThreadAndDispatcher(effect);
+ waitForCompletion(800 + TEST_TIMEOUT_MILLIS); // 200 + 50 + 400 + 100ms delay
+
+ verifyCallbacksTriggered(vibration, Status.FINISHED);
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
+
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), eq(1L));
+ // Step id = 2 skipped by the 50ms OFF step after the 200ms ON step.
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), eq(3L));
+
+ // First callback ignored, did not cause the vibrator to turn back on during the 400ms step.
+ assertThat(fakeVibrator.getEffectSegments(vibration.id))
+ .containsExactlyElementsIn(expectedOneShots(200L, 400L)).inOrder();
}
@Test
@@ -462,16 +496,16 @@
.compose();
HalVibration vibration = startThreadAndDispatcher(repeatingEffect);
- assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibration.id).isEmpty(),
- TEST_TIMEOUT_MILLIS));
+ assertThat(waitUntil(() -> !fakeVibrator.getEffectSegments(vibration.id).isEmpty(),
+ TEST_TIMEOUT_MILLIS)).isTrue();
mVibrationConductor.notifyCancelled(
new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
waitForCompletion();
// PWLE size max was used to generate a single vibrate call with 10 segments.
verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_USER);
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
- assertEquals(10, fakeVibrator.getEffectSegments(vibration.id).size());
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
+ assertThat(fakeVibrator.getEffectSegments(vibration.id)).hasSize(10);
}
@Test
@@ -479,28 +513,28 @@
throws Exception {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- fakeVibrator.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK);
+ fakeVibrator.setSupportedPrimitives(PRIMITIVE_CLICK);
fakeVibrator.setCompositionSizeMax(10);
VibrationEffect effect = VibrationEffect.startComposition()
// Very long delay so thread will be cancelled after first PWLE is triggered.
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
+ .addPrimitive(PRIMITIVE_CLICK, 1f, 100)
.compose();
VibrationEffect repeatingEffect = VibrationEffect.startComposition()
.repeatEffectIndefinitely(effect)
.compose();
HalVibration vibration = startThreadAndDispatcher(repeatingEffect);
- assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibration.id).isEmpty(),
- TEST_TIMEOUT_MILLIS));
+ assertThat(waitUntil(() -> !fakeVibrator.getEffectSegments(vibration.id).isEmpty(),
+ TEST_TIMEOUT_MILLIS)).isTrue();
mVibrationConductor.notifyCancelled(
new Vibration.EndInfo(Status.CANCELLED_BY_SCREEN_OFF), /* immediate= */ false);
waitForCompletion();
// Composition size max was used to generate a single vibrate call with 10 primitives.
verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_SCREEN_OFF);
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
- assertEquals(10, fakeVibrator.getEffectSegments(vibration.id).size());
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
+ assertThat(fakeVibrator.getEffectSegments(vibration.id)).hasSize(10);
}
@Test
@@ -514,15 +548,16 @@
new long[]{5000, 500, 50}, amplitudes, 0);
HalVibration vibration = startThreadAndDispatcher(effect);
- assertTrue(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS));
+ assertThat(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS))
+ .isTrue();
mVibrationConductor.notifyCancelled(
new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
waitForCompletion();
verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_USER);
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
- assertEquals(Arrays.asList(expectedOneShot(5550)),
- fakeVibrator.getEffectSegments(vibration.id));
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
+ assertThat(fakeVibrator.getEffectSegments(vibration.id))
+ .containsExactly(expectedOneShot(5550)).inOrder();
}
@LargeTest
@@ -538,42 +573,42 @@
/* amplitudes= */ new int[]{1, 2}, /* repeat= */ 0);
HalVibration vibration = startThreadAndDispatcher(effect);
- assertTrue(waitUntil(() -> fakeVibrator.getEffectSegments(vibration.id).size() > 1,
- expectedOnDuration + TEST_TIMEOUT_MILLIS));
+ assertThat(waitUntil(() -> fakeVibrator.getEffectSegments(vibration.id).size() > 1,
+ expectedOnDuration + TEST_TIMEOUT_MILLIS)).isTrue();
mVibrationConductor.notifyCancelled(
new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
waitForCompletion();
verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_USER);
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
List<VibrationEffectSegment> effectSegments = fakeVibrator.getEffectSegments(vibration.id);
// First time, turn vibrator ON for the expected fixed duration.
- assertEquals(expectedOnDuration, effectSegments.get(0).getDuration());
+ assertThat(effectSegments.get(0).getDuration()).isEqualTo(expectedOnDuration);
// Vibrator turns off in the middle of the second execution of the first step. Expect it to
// be turned back ON at least for the fixed duration + the remaining duration of the step.
- assertTrue(expectedOnDuration < effectSegments.get(1).getDuration());
+ assertThat(effectSegments.get(1).getDuration()).isGreaterThan(expectedOnDuration);
// Set amplitudes for a cycle {1, 2}, start second loop then turn it back on to same value.
- assertEquals(expectedAmplitudes(1, 2, 1, 1),
- mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().subList(0, 4));
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().subList(0, 4))
+ .containsExactlyElementsIn(expectedAmplitudes(1, 2, 1, 1))
+ .inOrder();
}
@Test
public void vibrate_singleVibratorPredefinedCancel_cancelsVibrationImmediately()
throws Exception {
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK);
+ mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives(PRIMITIVE_CLICK);
VibrationEffect effect = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
+ .addPrimitive(PRIMITIVE_CLICK, 1f, 100)
+ .addPrimitive(PRIMITIVE_CLICK, 1f, 100)
+ .addPrimitive(PRIMITIVE_CLICK, 1f, 100)
.compose();
HalVibration vibration = startThreadAndDispatcher(effect);
- assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
- TEST_TIMEOUT_MILLIS));
- assertTrue(mThread.isRunningVibrationId(vibration.id));
+ assertThat(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
+ TEST_TIMEOUT_MILLIS)).isTrue();
+ assertThat(mThread.isRunningVibrationId(vibration.id)).isTrue();
// Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
@@ -587,11 +622,11 @@
cancellingThread.join();
verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_SETTINGS_UPDATE);
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
}
@Test
- @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ @EnableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
public void vibrate_singleVibratorVendorEffectCancel_cancelsVibrationImmediately()
throws Exception {
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS);
@@ -601,9 +636,9 @@
VibrationEffect effect = VibrationEffect.createVendorEffect(createTestVendorData());
HalVibration vibration = startThreadAndDispatcher(effect);
- assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
- TEST_TIMEOUT_MILLIS));
- assertTrue(mThread.isRunningVibrationId(vibration.id));
+ assertThat(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
+ TEST_TIMEOUT_MILLIS)).isTrue();
+ assertThat(mThread.isRunningVibrationId(vibration.id)).isTrue();
// Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
@@ -617,7 +652,7 @@
cancellingThread.join();
verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_SETTINGS_UPDATE);
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
}
@Test
@@ -628,9 +663,9 @@
VibrationEffect effect = VibrationEffect.createWaveform(new long[]{100}, new int[]{100}, 0);
HalVibration vibration = startThreadAndDispatcher(effect);
- assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
- TEST_TIMEOUT_MILLIS));
- assertTrue(mThread.isRunningVibrationId(vibration.id));
+ assertThat(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
+ TEST_TIMEOUT_MILLIS)).isTrue();
+ assertThat(mThread.isRunningVibrationId(vibration.id)).isTrue();
// Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
@@ -644,7 +679,7 @@
cancellingThread.join();
verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_SCREEN_OFF);
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
}
@Test
@@ -657,12 +692,12 @@
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
- assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_THUD)),
- mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id))
+ .containsExactly(expectedPrebaked(VibrationEffect.EFFECT_THUD)).inOrder();
}
@Test
@@ -671,37 +706,39 @@
VibrationEffect fallback = VibrationEffect.createOneShot(10, 100);
HalVibration vibration = createVibration(CombinedVibration.createParallel(
- VibrationEffect.get(VibrationEffect.EFFECT_CLICK)));
+ VibrationEffect.get(EFFECT_CLICK)));
vibration.fillFallbacks(unused -> fallback);
startThreadAndDispatcher(vibration);
waitForCompletion();
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
- assertEquals(Arrays.asList(expectedOneShot(10)),
- mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
- assertEquals(expectedAmplitudes(100), mVibratorProviders.get(VIBRATOR_ID).getAmplitudes());
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id))
+ .containsExactly(expectedOneShot(10)).inOrder();
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes())
+ .containsExactlyElementsIn(expectedAmplitudes(100)).inOrder();
}
@Test
public void vibrate_singleVibratorPrebakedAndUnsupportedEffect_ignoresVibration() {
- VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+ VibrationEffect effect = VibrationEffect.get(EFFECT_CLICK);
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(0L));
verify(mManagerHooks, never()).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks, never())
+ .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.IGNORED_UNSUPPORTED);
- assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id).isEmpty());
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)).isEmpty();
}
@Test
- @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ @EnableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
public void vibrate_singleVibratorVendorEffect_runsVibration() {
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS);
@@ -712,104 +749,101 @@
verify(mManagerHooks).noteVibratorOn(eq(UID),
eq(PerformVendorEffectVibratorStep.VENDOR_EFFECT_MAX_DURATION_MS));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
assertThat(mVibratorProviders.get(VIBRATOR_ID).getVendorEffects(vibration.id))
- .containsExactly(effect)
- .inOrder();
+ .containsExactly(effect).inOrder();
}
@Test
public void vibrate_singleVibratorComposed_runsVibration() {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- fakeVibrator.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK,
- VibrationEffect.Composition.PRIMITIVE_TICK);
+ fakeVibrator.setSupportedPrimitives(PRIMITIVE_CLICK, PRIMITIVE_TICK);
VibrationEffect effect = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
+ .addPrimitive(PRIMITIVE_CLICK, 1f)
+ .addPrimitive(PRIMITIVE_TICK, 0.5f)
.compose();
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(40L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
- assertEquals(Arrays.asList(
- expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
- expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, 0)),
- fakeVibrator.getEffectSegments(vibration.id));
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
+ assertThat(fakeVibrator.getEffectSegments(vibration.id))
+ .containsExactly(
+ expectedPrimitive(PRIMITIVE_CLICK, 1, 0),
+ expectedPrimitive(PRIMITIVE_TICK, 0.5f, 0))
+ .inOrder();
}
@Test
@DisableFlags(Flags.FLAG_PRIMITIVE_COMPOSITION_ABSOLUTE_DELAY)
public void vibrate_singleVibratorComposedAndNoCapability_triggersHalAndReturnsUnsupported() {
VibrationEffect effect = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
+ .addPrimitive(PRIMITIVE_CLICK, 1f)
.compose();
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(0L));
verify(mManagerHooks, never()).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks, never())
+ .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.IGNORED_UNSUPPORTED);
- assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id).isEmpty());
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)).isEmpty();
}
@Test
@EnableFlags(Flags.FLAG_PRIMITIVE_COMPOSITION_ABSOLUTE_DELAY)
public void vibrate_singleVibratorComposedAndNoCapability_ignoresVibration() {
VibrationEffect effect = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
+ .addPrimitive(PRIMITIVE_CLICK, 1f)
.compose();
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
verify(mManagerHooks, never()).noteVibratorOn(eq(UID), anyLong());
verify(mManagerHooks, never()).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks, never())
+ .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.IGNORED_UNSUPPORTED);
- assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id).isEmpty());
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id)).isEmpty();
}
@Test
public void vibrate_singleVibratorLargeComposition_splitsVibratorComposeCalls() {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- fakeVibrator.setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK,
- VibrationEffect.Composition.PRIMITIVE_TICK,
- VibrationEffect.Composition.PRIMITIVE_SPIN);
+ fakeVibrator.setSupportedPrimitives(PRIMITIVE_CLICK, PRIMITIVE_TICK, PRIMITIVE_SPIN);
fakeVibrator.setCompositionSizeMax(2);
VibrationEffect effect = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SPIN, 0.8f)
+ .addPrimitive(PRIMITIVE_CLICK, 1f)
+ .addPrimitive(PRIMITIVE_TICK, 0.5f)
+ .addPrimitive(PRIMITIVE_SPIN, 0.8f)
.compose();
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
verifyCallbacksTriggered(vibration, Status.FINISHED);
// Vibrator compose called twice.
- verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
- assertEquals(3, fakeVibrator.getEffectSegments(vibration.id).size());
+ verify(mControllerCallbacks, times(2))
+ .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
+ assertThat(fakeVibrator.getEffectSegments(vibration.id)).hasSize(3);
}
@Test
- @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ @DisableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
public void vibrate_singleVibratorComposedEffects_runsDifferentVibrations() {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
- fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
- fakeVibrator.setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK,
- VibrationEffect.Composition.PRIMITIVE_TICK);
+ fakeVibrator.setSupportedEffects(EFFECT_CLICK);
+ fakeVibrator.setSupportedPrimitives(PRIMITIVE_CLICK, PRIMITIVE_TICK);
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS,
IVibrator.CAP_COMPOSE_PWLE_EFFECTS, IVibrator.CAP_AMPLITUDE_CONTROL);
fakeVibrator.setMinFrequency(100);
@@ -820,15 +854,15 @@
VibrationEffect effect = VibrationEffect.startComposition()
.addEffect(VibrationEffect.createOneShot(10, 100))
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
- .addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addPrimitive(PRIMITIVE_CLICK, 1f)
+ .addPrimitive(PRIMITIVE_TICK, 0.5f)
+ .addEffect(VibrationEffect.get(EFFECT_CLICK))
.addEffect(VibrationEffect.startWaveform()
.addTransition(Duration.ofMillis(10),
targetAmplitude(1), targetFrequency(100))
.addTransition(Duration.ofMillis(20), targetFrequency(120))
.build())
- .addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addEffect(VibrationEffect.get(EFFECT_CLICK))
.compose();
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
@@ -836,38 +870,41 @@
// Use first duration the vibrator is turned on since we cannot estimate the clicks.
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks, times(5)).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks, times(5))
+ .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
- assertEquals(Arrays.asList(
- expectedOneShot(10),
- expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
- expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, 0),
- expectedPrebaked(VibrationEffect.EFFECT_CLICK),
- expectedRamp(/* startAmplitude= */ 0, /* endAmplitude= */ 0.5f,
- /* startFrequencyHz= */ 150, /* endFrequencyHz= */ 100, /* duration= */ 10),
- expectedRamp(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.7f,
- /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 120, /* duration= */ 20),
- expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
- mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
- assertEquals(expectedAmplitudes(100), mVibratorProviders.get(VIBRATOR_ID).getAmplitudes());
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id))
+ .containsExactly(
+ expectedOneShot(10),
+ expectedPrimitive(PRIMITIVE_CLICK, 1, 0),
+ expectedPrimitive(PRIMITIVE_TICK, 0.5f, 0),
+ expectedPrebaked(EFFECT_CLICK),
+ expectedRamp(/* startAmplitude= */ 0, /* endAmplitude= */ 0.5f,
+ /* startFrequencyHz= */ 150, /* endFrequencyHz= */ 100,
+ /* duration= */ 10),
+ expectedRamp(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.7f,
+ /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 120,
+ /* duration= */ 20),
+ expectedPrebaked(EFFECT_CLICK))
+ .inOrder();
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes())
+ .containsExactlyElementsIn(expectedAmplitudes(100)).inOrder();
}
@Test
public void vibrate_singleVibratorComposedWithFallback_replacedInTheMiddleOfComposition() {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
- fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
- fakeVibrator.setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK,
- VibrationEffect.Composition.PRIMITIVE_TICK);
+ fakeVibrator.setSupportedEffects(EFFECT_CLICK);
+ fakeVibrator.setSupportedPrimitives(PRIMITIVE_CLICK, PRIMITIVE_TICK);
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
VibrationEffect fallback = VibrationEffect.createOneShot(10, 100);
VibrationEffect effect = VibrationEffect.startComposition()
- .addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
- .addEffect(VibrationEffect.get(VibrationEffect.EFFECT_TICK))
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
+ .addEffect(VibrationEffect.get(EFFECT_CLICK))
+ .addPrimitive(PRIMITIVE_CLICK, 1f)
+ .addEffect(VibrationEffect.get(EFFECT_TICK))
+ .addPrimitive(PRIMITIVE_TICK, 0.5f)
.compose();
HalVibration vibration = createVibration(CombinedVibration.createParallel(effect));
vibration.fillFallbacks(unused -> fallback);
@@ -877,24 +914,26 @@
// Use first duration the vibrator is turned on since we cannot estimate the clicks.
verify(mManagerHooks).noteVibratorOn(eq(UID), anyLong());
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks, times(4)).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks, times(4))
+ .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
List<VibrationEffectSegment> segments =
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id);
- assertTrue("Wrong segments: " + segments, segments.size() >= 4);
- assertTrue(segments.get(0) instanceof PrebakedSegment);
- assertTrue(segments.get(1) instanceof PrimitiveSegment);
+ assertWithMessage("Wrong segments: %s", segments).that(segments.size()).isGreaterThan(3);
+ assertThat(segments.get(0)).isInstanceOf(PrebakedSegment.class);
+ assertThat(segments.get(1)).isInstanceOf(PrimitiveSegment.class);
for (int i = 2; i < segments.size() - 1; i++) {
// One or more step segments as fallback for the EFFECT_TICK.
- assertTrue(segments.get(i) instanceof StepSegment);
+ assertWithMessage("For segment index %s", i)
+ .that(segments.get(i)).isInstanceOf(StepSegment.class);
}
- assertTrue(segments.get(segments.size() - 1) instanceof PrimitiveSegment);
+ assertThat(segments.get(segments.size() - 1)).isInstanceOf(PrimitiveSegment.class);
}
@Test
- @EnableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
public void vibrate_singleVibratorPwle_runsComposePwleV2() {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2);
@@ -915,21 +954,22 @@
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(100L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
- assertEquals(Arrays.asList(
- expectedPwle(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 0),
- expectedPwle(/*amplitude=*/ 0.1f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20),
- expectedPwle(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30),
- expectedPwle(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 20),
- expectedPwle(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 30)
- ), fakeVibrator.getEffectPwlePoints(vibration.id));
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
+ assertThat(fakeVibrator.getEffectPwlePoints(vibration.id))
+ .containsExactly(
+ expectedPwle(0.0f, 60f, 0),
+ expectedPwle(0.1f, 60f, 20),
+ expectedPwle(0.3f, 100f, 30),
+ expectedPwle(0.4f, 120f, 20),
+ expectedPwle(0.0f, 120f, 30))
+ .inOrder();
}
@Test
- @EnableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
public void vibrate_singleVibratorBasicPwle_runsComposePwleV2() {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2);
@@ -951,20 +991,21 @@
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(220L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
- assertEquals(Arrays.asList(
- expectedPwle(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 150f, /*timeMillis=*/ 0),
- expectedPwle(/*amplitude=*/ 1.0f, /*frequencyHz=*/ 150f, /*timeMillis=*/ 20),
- expectedPwle(/*amplitude=*/ 1.0f, /*frequencyHz=*/ 150f, /*timeMillis=*/ 100),
- expectedPwle(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 150f, /*timeMillis=*/ 100)
- ), fakeVibrator.getEffectPwlePoints(vibration.id));
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
+ assertThat(fakeVibrator.getEffectPwlePoints(vibration.id))
+ .containsExactly(
+ expectedPwle(0.0f, 150f, 0),
+ expectedPwle(1.0f, 150f, 20),
+ expectedPwle(1.0f, 150f, 100),
+ expectedPwle(0.0f, 150f, 100))
+ .inOrder();
}
@Test
- @EnableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
public void vibrate_singleVibratorPwle_withInitialFrequency_runsComposePwleV2() {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2);
@@ -987,21 +1028,21 @@
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(100L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
- assertEquals(Arrays.asList(
- expectedPwle(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 30f, /*timeMillis=*/ 0),
- expectedPwle(/*amplitude=*/ 0.1f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20),
- expectedPwle(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30),
- expectedPwle(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 20),
- expectedPwle(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 30)
- ), fakeVibrator.getEffectPwlePoints(vibration.id));
-
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
+ assertThat(fakeVibrator.getEffectPwlePoints(vibration.id))
+ .containsExactly(
+ expectedPwle(0.0f, 30f, 0),
+ expectedPwle(0.1f, 60f, 20),
+ expectedPwle(0.3f, 100f, 30),
+ expectedPwle(0.4f, 120f, 20),
+ expectedPwle(0.0f, 120f, 30))
+ .inOrder();
}
@Test
- @EnableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
public void vibrate_singleVibratorPwle_TooManyControlPoints_splitsAndRunsComposePwleV2() {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2);
@@ -1027,23 +1068,23 @@
verifyCallbacksTriggered(vibration, Status.FINISHED);
// Vibrator compose called 3 times with 2 segments instead of 2 times with 3 segments.
// Using best split points instead of max-packing PWLEs.
- verify(mControllerCallbacks, times(3)).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
-
- assertEquals(Arrays.asList(
- expectedPwle(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 0),
- expectedPwle(/*amplitude=*/ 0.8f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30),
- expectedPwle(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30),
- expectedPwle(/*amplitude=*/ 0.9f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 0),
- expectedPwle(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30),
- expectedPwle(/*amplitude=*/ 0.6f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 0),
- expectedPwle(/*amplitude=*/ 0.7f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30)
- ), fakeVibrator.getEffectPwlePoints(vibration.id));
-
+ verify(mControllerCallbacks, times(3))
+ .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
+ assertThat(fakeVibrator.getEffectPwlePoints(vibration.id))
+ .containsExactly(
+ expectedPwle(0.0f, 100f, 0),
+ expectedPwle(0.8f, 100f, 30),
+ expectedPwle(0.0f, 100f, 30),
+ expectedPwle(0.9f, 100f, 0),
+ expectedPwle(0.4f, 100f, 30),
+ expectedPwle(0.6f, 100f, 0),
+ expectedPwle(0.7f, 100f, 30))
+ .inOrder();
}
@Test
- @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ @DisableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
public void vibrate_singleVibratorPwle_runsComposePwle() {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
@@ -1066,19 +1107,23 @@
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(100L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
- assertEquals(Arrays.asList(
- expectedRamp(/* amplitude= */ 1, /* frequencyHz= */ 150, /* duration= */ 10),
- expectedRamp(/* startAmplitude= */ 1, /* endAmplitude= */ 0,
- /* startFrequencyHz= */ 150, /* endFrequencyHz= */ 150, /* duration= */ 20),
- expectedRamp(/* amplitude= */ 0.5f, /* frequencyHz= */ 100, /* duration= */ 30),
- expectedRamp(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.6f,
- /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 200,
- /* duration= */ 40)),
- fakeVibrator.getEffectSegments(vibration.id));
- assertEquals(Arrays.asList(Braking.CLAB), fakeVibrator.getBraking(vibration.id));
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
+ assertThat(fakeVibrator.getEffectSegments(vibration.id))
+ .containsExactly(
+ expectedRamp(/* amplitude= */ 1, /* frequencyHz= */ 150,
+ /* duration= */ 10),
+ expectedRamp(/* startAmplitude= */ 1, /* endAmplitude= */ 0,
+ /* startFrequencyHz= */ 150, /* endFrequencyHz= */ 150,
+ /* duration= */ 20),
+ expectedRamp(/* amplitude= */ 0.5f, /* frequencyHz= */ 100,
+ /* duration= */ 30),
+ expectedRamp(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.6f,
+ /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 200,
+ /* duration= */ 40))
+ .inOrder();
+ assertThat(fakeVibrator.getBraking(vibration.id)).containsExactly(Braking.CLAB).inOrder();
}
@Test
@@ -1109,8 +1154,9 @@
// Vibrator compose called 3 times with 2 segments instead of 2 times with 3 segments.
// Using best split points instead of max-packing PWLEs.
- verify(mControllerCallbacks, times(3)).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
- assertEquals(6, fakeVibrator.getEffectSegments(vibration.id).size());
+ verify(mControllerCallbacks, times(3))
+ .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
+ assertThat(fakeVibrator.getEffectSegments(vibration.id)).hasSize(6);
}
@Test
@@ -1121,15 +1167,16 @@
VibrationEffect effect = VibrationEffect.createWaveform(new long[]{5}, new int[]{100}, 0);
HalVibration vibration = startThreadAndDispatcher(effect);
- assertTrue(waitUntil(() -> fakeVibrator.getAmplitudes().size() > 2, TEST_TIMEOUT_MILLIS));
+ assertThat(waitUntil(() -> fakeVibrator.getAmplitudes().size() > 2, TEST_TIMEOUT_MILLIS))
+ .isTrue();
// Vibration still running after 2 cycles.
- assertTrue(mThread.isRunningVibrationId(vibration.id));
- assertTrue(mControllers.get(VIBRATOR_ID).isVibrating());
+ assertThat(mThread.isRunningVibrationId(vibration.id)).isTrue();
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isTrue();
mVibrationConductor.notifyCancelled(
new Vibration.EndInfo(Status.CANCELLED_BINDER_DIED), /* immediate= */ false);
waitForCompletion();
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
verifyCallbacksTriggered(vibration, Status.CANCELLED_BINDER_DIED);
}
@@ -1149,72 +1196,71 @@
@Test
public void vibrate_multipleExistingAndMissingVibrators_vibratesOnlyExistingOnes() {
- mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_TICK);
+ mVibratorProviders.get(1).setSupportedEffects(EFFECT_TICK);
CombinedVibration effect = CombinedVibration.startParallel()
- .addVibrator(VIBRATOR_ID, VibrationEffect.get(VibrationEffect.EFFECT_TICK))
- .addVibrator(2, VibrationEffect.get(VibrationEffect.EFFECT_TICK))
+ .addVibrator(VIBRATOR_ID, VibrationEffect.get(EFFECT_TICK))
+ .addVibrator(2, VibrationEffect.get(EFFECT_TICK))
.combine();
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
- verify(mControllerCallbacks, never()).onComplete(eq(2), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
+ verify(mControllerCallbacks, never()).onComplete(eq(2), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
- assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_TICK)),
- mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id))
+ .containsExactly(expectedPrebaked(EFFECT_TICK)).inOrder();
}
@Test
public void vibrate_multipleMono_runsSameEffectInAllVibrators() {
mockVibrators(1, 2, 3);
- mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
- mVibratorProviders.get(2).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
- mVibratorProviders.get(3).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ mVibratorProviders.get(1).setSupportedEffects(EFFECT_CLICK);
+ mVibratorProviders.get(2).setSupportedEffects(EFFECT_CLICK);
+ mVibratorProviders.get(3).setSupportedEffects(EFFECT_CLICK);
CombinedVibration effect = CombinedVibration.createParallel(
- VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
+ VibrationEffect.get(EFFECT_CLICK));
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(1), eq(vibration.id));
- verify(mControllerCallbacks).onComplete(eq(2), eq(vibration.id));
- verify(mControllerCallbacks).onComplete(eq(3), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(1), eq(vibration.id), anyLong());
+ verify(mControllerCallbacks).onComplete(eq(2), eq(vibration.id), anyLong());
+ verify(mControllerCallbacks).onComplete(eq(3), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
- assertFalse(mControllers.get(1).isVibrating());
- assertFalse(mControllers.get(2).isVibrating());
- assertFalse(mControllers.get(3).isVibrating());
+ assertThat(mControllers.get(1).isVibrating()).isFalse();
+ assertThat(mControllers.get(2).isVibrating()).isFalse();
+ assertThat(mControllers.get(3).isVibrating()).isFalse();
- VibrationEffectSegment expected = expectedPrebaked(VibrationEffect.EFFECT_CLICK);
- assertEquals(Arrays.asList(expected),
- mVibratorProviders.get(1).getEffectSegments(vibration.id));
- assertEquals(Arrays.asList(expected),
- mVibratorProviders.get(2).getEffectSegments(vibration.id));
- assertEquals(Arrays.asList(expected),
- mVibratorProviders.get(3).getEffectSegments(vibration.id));
+ VibrationEffectSegment expected = expectedPrebaked(EFFECT_CLICK);
+ assertThat(mVibratorProviders.get(1).getEffectSegments(vibration.id))
+ .containsExactly(expected).inOrder();
+ assertThat(mVibratorProviders.get(2).getEffectSegments(vibration.id))
+ .containsExactly(expected).inOrder();
+ assertThat(mVibratorProviders.get(3).getEffectSegments(vibration.id))
+ .containsExactly(expected).inOrder();
}
@Test
public void vibrate_multipleStereo_runsVibrationOnRightVibrators() {
mockVibrators(1, 2, 3, 4);
- mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ mVibratorProviders.get(1).setSupportedEffects(EFFECT_CLICK);
mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
mVibratorProviders.get(3).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
mVibratorProviders.get(4).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- mVibratorProviders.get(4).setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK);
+ mVibratorProviders.get(4).setSupportedPrimitives(PRIMITIVE_CLICK);
VibrationEffect composed = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(PRIMITIVE_CLICK)
.compose();
CombinedVibration effect = CombinedVibration.startParallel()
- .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addVibrator(1, VibrationEffect.get(EFFECT_CLICK))
.addVibrator(2, VibrationEffect.createOneShot(10, 100))
.addVibrator(3, VibrationEffect.createWaveform(
new long[]{10, 10}, new int[]{1, 2}, -1))
@@ -1225,27 +1271,28 @@
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(1), eq(vibration.id));
- verify(mControllerCallbacks).onComplete(eq(2), eq(vibration.id));
- verify(mControllerCallbacks).onComplete(eq(3), eq(vibration.id));
- verify(mControllerCallbacks).onComplete(eq(4), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(1), eq(vibration.id), anyLong());
+ verify(mControllerCallbacks).onComplete(eq(2), eq(vibration.id), anyLong());
+ verify(mControllerCallbacks).onComplete(eq(3), eq(vibration.id), anyLong());
+ verify(mControllerCallbacks).onComplete(eq(4), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
- assertFalse(mControllers.get(1).isVibrating());
- assertFalse(mControllers.get(2).isVibrating());
- assertFalse(mControllers.get(3).isVibrating());
- assertFalse(mControllers.get(4).isVibrating());
+ assertThat(mControllers.get(1).isVibrating()).isFalse();
+ assertThat(mControllers.get(2).isVibrating()).isFalse();
+ assertThat(mControllers.get(3).isVibrating()).isFalse();
+ assertThat(mControllers.get(4).isVibrating()).isFalse();
- assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
- mVibratorProviders.get(1).getEffectSegments(vibration.id));
- assertEquals(Arrays.asList(expectedOneShot(10)),
- mVibratorProviders.get(2).getEffectSegments(vibration.id));
- assertEquals(expectedAmplitudes(100), mVibratorProviders.get(2).getAmplitudes());
- assertEquals(Arrays.asList(expectedOneShot(20)),
- mVibratorProviders.get(3).getEffectSegments(vibration.id));
- assertEquals(expectedAmplitudes(1, 2), mVibratorProviders.get(3).getAmplitudes());
- assertEquals(Arrays.asList(
- expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0)),
- mVibratorProviders.get(4).getEffectSegments(vibration.id));
+ assertThat(mVibratorProviders.get(1).getEffectSegments(vibration.id))
+ .containsExactly(expectedPrebaked(EFFECT_CLICK)).inOrder();
+ assertThat(mVibratorProviders.get(2).getEffectSegments(vibration.id))
+ .containsExactly(expectedOneShot(10)).inOrder();
+ assertThat(mVibratorProviders.get(2).getAmplitudes())
+ .containsExactlyElementsIn(expectedAmplitudes(100)).inOrder();
+ assertThat(mVibratorProviders.get(3).getEffectSegments(vibration.id))
+ .containsExactly(expectedOneShot(20)).inOrder();
+ assertThat(mVibratorProviders.get(3).getAmplitudes())
+ .containsExactlyElementsIn(expectedAmplitudes(1, 2)).inOrder();
+ assertThat(mVibratorProviders.get(4).getEffectSegments(vibration.id))
+ .containsExactly(expectedPrimitive(PRIMITIVE_CLICK, 1, 0)).inOrder();
}
@Test
@@ -1253,25 +1300,24 @@
mockVibrators(1, 2, 3);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- mVibratorProviders.get(2).setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK);
- mVibratorProviders.get(3).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ mVibratorProviders.get(2).setSupportedPrimitives(PRIMITIVE_CLICK);
+ mVibratorProviders.get(3).setSupportedEffects(EFFECT_CLICK);
VibrationEffect composed = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(PRIMITIVE_CLICK)
.compose();
CombinedVibration effect = CombinedVibration.startSequential()
- .addNext(3, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), /* delay= */ 50)
+ .addNext(3, VibrationEffect.get(EFFECT_CLICK), /* delay= */ 50)
.addNext(1, VibrationEffect.createOneShot(10, 100), /* delay= */ 50)
.addNext(2, composed, /* delay= */ 50)
.combine();
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
- InOrder controllerVerifier = inOrder(mControllerCallbacks);
- controllerVerifier.verify(mControllerCallbacks).onComplete(eq(3), eq(vibration.id));
- controllerVerifier.verify(mControllerCallbacks).onComplete(eq(1), eq(vibration.id));
- controllerVerifier.verify(mControllerCallbacks).onComplete(eq(2), eq(vibration.id));
+ InOrder verifier = inOrder(mControllerCallbacks);
+ verifier.verify(mControllerCallbacks).onComplete(eq(3), eq(vibration.id), anyLong());
+ verifier.verify(mControllerCallbacks).onComplete(eq(1), eq(vibration.id), anyLong());
+ verifier.verify(mControllerCallbacks).onComplete(eq(2), eq(vibration.id), anyLong());
InOrder batteryVerifier = inOrder(mManagerHooks);
batteryVerifier.verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
@@ -1282,18 +1328,18 @@
batteryVerifier.verify(mManagerHooks).noteVibratorOff(eq(UID));
verifyCallbacksTriggered(vibration, Status.FINISHED);
- assertFalse(mControllers.get(1).isVibrating());
- assertFalse(mControllers.get(2).isVibrating());
- assertFalse(mControllers.get(3).isVibrating());
+ assertThat(mControllers.get(1).isVibrating()).isFalse();
+ assertThat(mControllers.get(2).isVibrating()).isFalse();
+ assertThat(mControllers.get(3).isVibrating()).isFalse();
- assertEquals(Arrays.asList(expectedOneShot(10)),
- mVibratorProviders.get(1).getEffectSegments(vibration.id));
- assertEquals(expectedAmplitudes(100), mVibratorProviders.get(1).getAmplitudes());
- assertEquals(Arrays.asList(
- expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0)),
- mVibratorProviders.get(2).getEffectSegments(vibration.id));
- assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
- mVibratorProviders.get(3).getEffectSegments(vibration.id));
+ assertThat(mVibratorProviders.get(1).getEffectSegments(vibration.id))
+ .containsExactly(expectedOneShot(10)).inOrder();
+ assertThat(mVibratorProviders.get(1).getAmplitudes())
+ .containsExactlyElementsIn(expectedAmplitudes(100)).inOrder();
+ assertThat(mVibratorProviders.get(2).getEffectSegments(vibration.id))
+ .containsExactly(expectedPrimitive(PRIMITIVE_CLICK, 1, 0)).inOrder();
+ assertThat(mVibratorProviders.get(3).getEffectSegments(vibration.id))
+ .containsExactly(expectedPrebaked(EFFECT_CLICK)).inOrder();
}
@Test
@@ -1301,15 +1347,13 @@
int[] vibratorIds = new int[]{1, 2};
mockVibrators(vibratorIds);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- mVibratorProviders.get(1).setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK);
+ mVibratorProviders.get(1).setSupportedPrimitives(PRIMITIVE_CLICK);
mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- mVibratorProviders.get(2).setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK);
+ mVibratorProviders.get(2).setSupportedPrimitives(PRIMITIVE_CLICK);
when(mManagerHooks.prepareSyncedVibration(anyLong(), eq(vibratorIds))).thenReturn(true);
VibrationEffect composed = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100)
+ .addPrimitive(PRIMITIVE_CLICK, 1, 100)
.compose();
CombinedVibration effect = CombinedVibration.createParallel(composed);
// We create the HalVibration here to obtain the vibration id and use it to mock the
@@ -1318,10 +1362,10 @@
when(mManagerHooks.triggerSyncedVibration(eq(vibration.id))).thenReturn(true);
startThreadAndDispatcher(vibration);
- assertTrue(waitUntil(
+ assertThat(waitUntil(
() -> !mVibratorProviders.get(1).getEffectSegments(vibration.id).isEmpty()
&& !mVibratorProviders.get(2).getEffectSegments(vibration.id).isEmpty(),
- TEST_TIMEOUT_MILLIS));
+ TEST_TIMEOUT_MILLIS)).isTrue();
mVibrationConductor.notifySyncedVibrationComplete();
waitForCompletion();
@@ -1331,29 +1375,27 @@
verify(mManagerHooks, never()).cancelSyncedVibration();
verifyCallbacksTriggered(vibration, Status.FINISHED);
- VibrationEffectSegment expected = expectedPrimitive(
- VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100);
- assertEquals(Arrays.asList(expected),
- mVibratorProviders.get(1).getEffectSegments(vibration.id));
- assertEquals(Arrays.asList(expected),
- mVibratorProviders.get(2).getEffectSegments(vibration.id));
+ VibrationEffectSegment expected = expectedPrimitive(PRIMITIVE_CLICK, 1, 100);
+ assertThat(mVibratorProviders.get(1).getEffectSegments(vibration.id))
+ .containsExactly(expected).inOrder();
+ assertThat(mVibratorProviders.get(2).getEffectSegments(vibration.id))
+ .containsExactly(expected).inOrder();
}
@Test
public void vibrate_multipleSynced_callsPrepareAndTriggerCallbacks() {
int[] vibratorIds = new int[]{1, 2, 3, 4};
mockVibrators(vibratorIds);
- mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ mVibratorProviders.get(1).setSupportedEffects(EFFECT_CLICK);
mVibratorProviders.get(4).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- mVibratorProviders.get(4).setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK);
+ mVibratorProviders.get(4).setSupportedPrimitives(PRIMITIVE_CLICK);
when(mManagerHooks.prepareSyncedVibration(anyLong(), any())).thenReturn(true);
VibrationEffect composed = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(PRIMITIVE_CLICK)
.compose();
CombinedVibration effect = CombinedVibration.startParallel()
- .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addVibrator(1, VibrationEffect.get(EFFECT_CLICK))
.addVibrator(2, VibrationEffect.createOneShot(10, 100))
.addVibrator(3, VibrationEffect.createWaveform(new long[]{10}, new int[]{100}, -1))
.addVibrator(4, composed)
@@ -1398,24 +1440,26 @@
verify(mManagerHooks, never()).triggerSyncedVibration(eq(vibration.id));
verify(mManagerHooks, never()).cancelSyncedVibration();
- assertEquals(Arrays.asList(expectedOneShot(10)),
- mVibratorProviders.get(1).getEffectSegments(vibration.id));
- assertEquals(expectedAmplitudes(100), mVibratorProviders.get(1).getAmplitudes());
- assertEquals(Arrays.asList(expectedOneShot(5)),
- mVibratorProviders.get(2).getEffectSegments(vibration.id));
- assertEquals(expectedAmplitudes(200), mVibratorProviders.get(2).getAmplitudes());
+ assertThat(mVibratorProviders.get(1).getEffectSegments(vibration.id))
+ .containsExactly(expectedOneShot(10)).inOrder();
+ assertThat(mVibratorProviders.get(1).getAmplitudes())
+ .containsExactlyElementsIn(expectedAmplitudes(100)).inOrder();
+ assertThat(mVibratorProviders.get(2).getEffectSegments(vibration.id))
+ .containsExactly(expectedOneShot(5)).inOrder();
+ assertThat(mVibratorProviders.get(2).getAmplitudes())
+ .containsExactlyElementsIn(expectedAmplitudes(200)).inOrder();
}
@Test
public void vibrate_multipleSyncedTriggerFailed_cancelPreparedVibrationAndSkipSetAmplitude() {
int[] vibratorIds = new int[]{1, 2};
mockVibrators(vibratorIds);
- mVibratorProviders.get(2).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ mVibratorProviders.get(2).setSupportedEffects(EFFECT_CLICK);
when(mManagerHooks.prepareSyncedVibration(anyLong(), any())).thenReturn(true);
CombinedVibration effect = CombinedVibration.startParallel()
.addVibrator(1, VibrationEffect.createOneShot(10, 100))
- .addVibrator(2, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addVibrator(2, VibrationEffect.get(EFFECT_CLICK))
.combine();
// We create the HalVibration here to obtain the vibration id and use it to mock the
// required response when calling triggerSyncedVibration.
@@ -1432,7 +1476,7 @@
verify(mManagerHooks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
verify(mManagerHooks).triggerSyncedVibration(eq(vibration.id));
verify(mManagerHooks).cancelSyncedVibration();
- assertTrue(mVibratorProviders.get(1).getAmplitudes().isEmpty());
+ assertThat(mVibratorProviders.get(1).getAmplitudes()).isEmpty();
}
@Test
@@ -1453,33 +1497,37 @@
HalVibration vibration = startThreadAndDispatcher(effect);
// All vibrators are turned on in parallel.
- assertTrue(waitUntil(
+ assertThat(waitUntil(
() -> mControllers.get(1).isVibrating()
&& mControllers.get(2).isVibrating()
&& mControllers.get(3).isVibrating(),
- TEST_TIMEOUT_MILLIS));
+ TEST_TIMEOUT_MILLIS)).isTrue();
waitForCompletion();
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(80L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(1), eq(vibration.id));
- verify(mControllerCallbacks).onComplete(eq(2), eq(vibration.id));
- verify(mControllerCallbacks).onComplete(eq(3), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(1), eq(vibration.id), anyLong());
+ verify(mControllerCallbacks).onComplete(eq(2), eq(vibration.id), anyLong());
+ verify(mControllerCallbacks).onComplete(eq(3), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
- assertFalse(mControllers.get(1).isVibrating());
- assertFalse(mControllers.get(2).isVibrating());
- assertFalse(mControllers.get(3).isVibrating());
+ assertThat(mControllers.get(1).isVibrating()).isFalse();
+ assertThat(mControllers.get(2).isVibrating()).isFalse();
+ assertThat(mControllers.get(3).isVibrating()).isFalse();
- assertEquals(Arrays.asList(expectedOneShot(25)),
- mVibratorProviders.get(1).getEffectSegments(vibration.id));
- assertEquals(Arrays.asList(expectedOneShot(80)),
- mVibratorProviders.get(2).getEffectSegments(vibration.id));
- assertEquals(Arrays.asList(expectedOneShot(60)),
- mVibratorProviders.get(3).getEffectSegments(vibration.id));
- assertEquals(expectedAmplitudes(1, 2, 3), mVibratorProviders.get(1).getAmplitudes());
- assertEquals(expectedAmplitudes(4, 5), mVibratorProviders.get(2).getAmplitudes());
- assertEquals(expectedAmplitudes(6), mVibratorProviders.get(3).getAmplitudes());
+ assertThat(mVibratorProviders.get(1).getEffectSegments(vibration.id))
+ .containsExactly(expectedOneShot(25)).inOrder();
+ assertThat(mVibratorProviders.get(2).getEffectSegments(vibration.id))
+ .containsExactly(expectedOneShot(80)).inOrder();
+ assertThat(mVibratorProviders.get(3).getEffectSegments(vibration.id))
+ .containsExactly(expectedOneShot(60)).inOrder();
+ assertThat(mVibratorProviders.get(1).getAmplitudes())
+ .containsExactlyElementsIn(expectedAmplitudes(1, 2, 3)).inOrder();
+ assertThat(mVibratorProviders.get(2).getAmplitudes())
+ .containsExactlyElementsIn(expectedAmplitudes(4, 5)).inOrder();
+ assertThat(mVibratorProviders.get(3).getAmplitudes())
+ .containsExactlyElementsIn(expectedAmplitudes(6)).inOrder();
+
}
@Test
@@ -1505,7 +1553,7 @@
waitForCompletion(rampDownDuration + TEST_TIMEOUT_MILLIS);
long completionTime = SystemClock.elapsedRealtime();
- verify(mControllerCallbacks).onComplete(VIBRATOR_ID, vibration.id);
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
// Vibration ends after duration, thread completed after ramp down
assertThat(vibrationEndTime - startTime).isAtLeast(expectedDuration);
assertThat(vibrationEndTime - startTime).isLessThan(expectedDuration + rampDownDuration);
@@ -1526,15 +1574,15 @@
VibrationEffect.createOneShot(
expectedDuration, VibrationEffect.DEFAULT_AMPLITUDE)));
- startThreadAndDispatcher(vibration);
long startTime = SystemClock.elapsedRealtime();
+ startThreadAndDispatcher(vibration);
vibration.waitForEnd();
long vibrationEndTime = SystemClock.elapsedRealtime();
waitForCompletion(TEST_TIMEOUT_MILLIS);
- verify(mControllerCallbacks).onComplete(VIBRATOR_ID, vibration.id);
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
assertThat(vibrationEndTime - startTime).isAtLeast(expectedDuration + callbackDelay);
}
@@ -1563,7 +1611,8 @@
waitForCompletion(callbackDelay + TEST_TIMEOUT_MILLIS);
long completionTime = SystemClock.elapsedRealtime();
- verify(mControllerCallbacks, never()).onComplete(VIBRATOR_ID, vibration.id);
+ verify(mControllerCallbacks, never())
+ .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
// Vibration ends and thread completes after timeout, before the HAL callback
assertThat(vibrationEndTime - startTime).isAtLeast(expectedDuration + callbackTimeout);
assertThat(vibrationEndTime - startTime).isLessThan(expectedDuration + callbackDelay);
@@ -1596,26 +1645,24 @@
// Allow some delay for thread scheduling and callback triggering.
int maxDelay = (int) (0.05 * totalDuration); // < 5% of total duration
- assertTrue("Waveform with perceived delay of " + delay + "ms,"
- + " expected less than " + maxDelay + "ms",
- delay < maxDelay);
+ assertThat(delay).isLessThan(maxDelay);
}
@LargeTest
@Test
public void vibrate_cancelSlowVibrator_cancelIsNotBlockedByVibrationThread() throws Exception {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
- fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ fakeVibrator.setSupportedEffects(EFFECT_CLICK);
long latency = 5_000; // 5s
fakeVibrator.setOnLatency(latency);
- VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+ VibrationEffect effect = VibrationEffect.get(EFFECT_CLICK);
HalVibration vibration = startThreadAndDispatcher(effect);
- assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibration.id).isEmpty(),
- TEST_TIMEOUT_MILLIS));
- assertTrue(mThread.isRunningVibrationId(vibration.id));
+ assertThat(waitUntil(() -> !fakeVibrator.getEffectSegments(vibration.id).isEmpty(),
+ TEST_TIMEOUT_MILLIS)).isTrue();
+ assertThat(mThread.isRunningVibrationId(vibration.id)).isTrue();
// Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
// fail at waitForCompletion(cancellingThread).
@@ -1631,29 +1678,29 @@
// After the vibrator call ends the vibration is cancelled and the vibrator is turned off.
waitForCompletion(/* timeout= */ latency + TEST_TIMEOUT_MILLIS);
verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_USER);
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
}
@Test
public void vibrate_multiplePredefinedCancel_cancelsVibrationImmediately() throws Exception {
mockVibrators(1, 2);
- mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ mVibratorProviders.get(1).setSupportedEffects(EFFECT_CLICK);
mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- mVibratorProviders.get(2).setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK);
+ mVibratorProviders.get(2).setSupportedPrimitives(PRIMITIVE_CLICK);
CombinedVibration effect = CombinedVibration.startParallel()
- .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
+ .addVibrator(1, VibrationEffect.get(EFFECT_CLICK))
.addVibrator(2, VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
+ .addPrimitive(PRIMITIVE_CLICK, 1f, 100)
+ .addPrimitive(PRIMITIVE_CLICK, 1f, 100)
+ .addPrimitive(PRIMITIVE_CLICK, 1f, 100)
.compose())
.combine();
HalVibration vibration = startThreadAndDispatcher(effect);
- assertTrue(waitUntil(() -> mControllers.get(2).isVibrating(), TEST_TIMEOUT_MILLIS));
- assertTrue(mThread.isRunningVibrationId(vibration.id));
+ assertThat(waitUntil(() -> mControllers.get(2).isVibrating(), TEST_TIMEOUT_MILLIS))
+ .isTrue();
+ assertThat(mThread.isRunningVibrationId(vibration.id)).isTrue();
// Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
@@ -1667,12 +1714,12 @@
cancellingThread.join();
verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_SCREEN_OFF);
- assertFalse(mControllers.get(1).isVibrating());
- assertFalse(mControllers.get(2).isVibrating());
+ assertThat(mControllers.get(1).isVibrating()).isFalse();
+ assertThat(mControllers.get(2).isVibrating()).isFalse();
}
@Test
- @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ @EnableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
public void vibrate_multipleVendorEffectCancel_cancelsVibrationImmediately() throws Exception {
mockVibrators(1, 2);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS);
@@ -1686,8 +1733,9 @@
.combine();
HalVibration vibration = startThreadAndDispatcher(effect);
- assertTrue(waitUntil(() -> mControllers.get(2).isVibrating(), TEST_TIMEOUT_MILLIS));
- assertTrue(mThread.isRunningVibrationId(vibration.id));
+ assertThat(waitUntil(() -> mControllers.get(2).isVibrating(), TEST_TIMEOUT_MILLIS))
+ .isTrue();
+ assertThat(mThread.isRunningVibrationId(vibration.id)).isTrue();
// Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
@@ -1701,8 +1749,8 @@
cancellingThread.join();
verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_SCREEN_OFF);
- assertFalse(mControllers.get(1).isVibrating());
- assertFalse(mControllers.get(2).isVibrating());
+ assertThat(mControllers.get(1).isVibrating()).isFalse();
+ assertThat(mControllers.get(2).isVibrating()).isFalse();
}
@Test
@@ -1718,10 +1766,10 @@
.combine();
HalVibration vibration = startThreadAndDispatcher(effect);
- assertTrue(waitUntil(() -> mControllers.get(1).isVibrating()
+ assertThat(waitUntil(() -> mControllers.get(1).isVibrating()
&& mControllers.get(2).isVibrating(),
- TEST_TIMEOUT_MILLIS));
- assertTrue(mThread.isRunningVibrationId(vibration.id));
+ TEST_TIMEOUT_MILLIS)).isTrue();
+ assertThat(mThread.isRunningVibrationId(vibration.id)).isTrue();
// Run cancel in a separate thread so if VibrationThread.cancel blocks then this test should
// fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
@@ -1735,8 +1783,8 @@
cancellingThread.join();
verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_SCREEN_OFF);
- assertFalse(mControllers.get(1).isVibrating());
- assertFalse(mControllers.get(2).isVibrating());
+ assertThat(mControllers.get(1).isVibrating()).isFalse();
+ assertThat(mControllers.get(2).isVibrating()).isFalse();
}
@Test
@@ -1744,17 +1792,18 @@
VibrationEffect effect = VibrationEffect.createWaveform(new long[]{5}, new int[]{100}, 0);
HalVibration vibration = startThreadAndDispatcher(effect);
- assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
- TEST_TIMEOUT_MILLIS));
- assertTrue(mThread.isRunningVibrationId(vibration.id));
+ assertThat(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
+ TEST_TIMEOUT_MILLIS)).isTrue();
+ assertThat(mThread.isRunningVibrationId(vibration.id)).isTrue();
mVibrationConductor.notifyCancelled(
new Vibration.EndInfo(Status.CANCELLED_BINDER_DIED), /* immediate= */ false);
waitForCompletion();
verifyCallbacksTriggered(vibration, Status.CANCELLED_BINDER_DIED);
- assertFalse(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id).isEmpty());
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id))
+ .isNotEmpty();
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
}
@Test
@@ -1767,17 +1816,20 @@
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
// Duration extended for 5 + 5 + 5 + 15.
- assertEquals(Arrays.asList(expectedOneShot(30)),
- mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id))
+ .containsExactly(expectedOneShot(30)).inOrder();
List<Float> amplitudes = mVibratorProviders.get(VIBRATOR_ID).getAmplitudes();
- assertTrue(amplitudes.size() > 3);
- assertEquals(expectedAmplitudes(60, 120, 240), amplitudes.subList(0, 3));
+ assertThat(amplitudes.size()).isGreaterThan(3);
+ assertThat(amplitudes.subList(0, 3))
+ .containsExactlyElementsIn(expectedAmplitudes(60, 120, 240))
+ .inOrder();
for (int i = 3; i < amplitudes.size(); i++) {
- assertTrue(amplitudes.get(i) < amplitudes.get(i - 1));
+ assertWithMessage("For amplitude index %s", i)
+ .that(amplitudes.get(i)).isLessThan(amplitudes.get(i - 1));
}
}
@@ -1795,11 +1847,11 @@
verify(mManagerHooks, never()).onVibrationThreadReleased(anyLong());
// Thread still running ramp down.
- assertTrue(mThread.isRunningVibrationId(vibration.id));
+ assertThat(mThread.isRunningVibrationId(vibration.id)).isTrue();
// Duration extended for 10 + 10000.
- assertEquals(Arrays.asList(expectedOneShot(10_010)),
- mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id))
+ .containsExactly(expectedOneShot(10_010)).inOrder();
// Will stop the ramp down right away.
mVibrationConductor.notifyCancelled(
@@ -1819,8 +1871,8 @@
VibrationEffect effect = VibrationEffect.createOneShot(10_000, 240);
HalVibration vibration = startThreadAndDispatcher(effect);
- assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
- TEST_TIMEOUT_MILLIS));
+ assertThat(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
+ TEST_TIMEOUT_MILLIS)).isTrue();
mVibrationConductor.notifyCancelled(
new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
waitForCompletion();
@@ -1828,12 +1880,13 @@
verifyCallbacksTriggered(vibration, Status.CANCELLED_BY_USER);
// Duration extended for 10000 + 15.
- assertEquals(Arrays.asList(expectedOneShot(10_015)),
- mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id))
+ .containsExactly(expectedOneShot(10_015)).inOrder();
List<Float> amplitudes = mVibratorProviders.get(VIBRATOR_ID).getAmplitudes();
- assertTrue(amplitudes.size() > 1);
+ assertThat(amplitudes.size()).isGreaterThan(1);
for (int i = 1; i < amplitudes.size(); i++) {
- assertTrue(amplitudes.get(i) < amplitudes.get(i - 1));
+ assertWithMessage("For amplitude index %s", i)
+ .that(amplitudes.get(i)).isLessThan(amplitudes.get(i - 1));
}
}
@@ -1841,22 +1894,22 @@
public void vibrate_predefinedWithRampDown_doesNotAddRampDown() {
when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15);
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
- mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(EFFECT_CLICK);
- VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+ VibrationEffect effect = VibrationEffect.get(EFFECT_CLICK);
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
- assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
- mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
- assertTrue(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().isEmpty());
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id))
+ .containsExactly(expectedPrebaked(EFFECT_CLICK)).inOrder();
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()).isEmpty();
}
@Test
- @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ @EnableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
public void vibrate_vendorEffectWithRampDown_doesNotAddRampDown() {
when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15);
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS);
@@ -1865,12 +1918,11 @@
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
assertThat(mVibratorProviders.get(VIBRATOR_ID).getVendorEffects(vibration.id))
- .containsExactly(effect)
- .inOrder();
+ .containsExactly(effect).inOrder();
assertThat(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()).isEmpty();
}
@@ -1879,26 +1931,24 @@
when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15);
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL,
IVibrator.CAP_COMPOSE_EFFECTS);
- mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK);
+ mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives(PRIMITIVE_CLICK);
VibrationEffect effect = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(PRIMITIVE_CLICK)
.compose();
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
- assertEquals(
- Arrays.asList(expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0)),
- mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
- assertTrue(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().isEmpty());
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id))
+ .containsExactly(expectedPrimitive(PRIMITIVE_CLICK, 1, 0)).inOrder();
+ assertThat(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes()).isEmpty();
}
@Test
- @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ @DisableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
public void vibrate_pwleWithRampDown_doesNotAddRampDown() {
when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15);
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
@@ -1916,34 +1966,32 @@
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
- assertEquals(Arrays.asList(expectedRamp(0, 1, 150, 150, 1)),
- fakeVibrator.getEffectSegments(vibration.id));
- assertTrue(fakeVibrator.getAmplitudes().isEmpty());
+ assertThat(fakeVibrator.getEffectSegments(vibration.id))
+ .containsExactly(expectedRamp(0, 1, 150, 150, 1)).inOrder();
+ assertThat(fakeVibrator.getAmplitudes()).isEmpty();
}
@Test
public void vibrate_multipleVibrations_withCancel() throws Exception {
- mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(
- VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_TICK);
- mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK);
+ mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(EFFECT_CLICK, EFFECT_TICK);
+ mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives(PRIMITIVE_CLICK);
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL,
IVibrator.CAP_COMPOSE_EFFECTS);
// A simple effect, followed by a repeating effect that gets cancelled, followed by another
// simple effect.
- VibrationEffect effect1 = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+ VibrationEffect effect1 = VibrationEffect.get(EFFECT_CLICK);
VibrationEffect effect2 = VibrationEffect.startComposition()
- .repeatEffectIndefinitely(VibrationEffect.get(VibrationEffect.EFFECT_TICK))
+ .repeatEffectIndefinitely(VibrationEffect.get(EFFECT_TICK))
.compose();
VibrationEffect effect3 = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(PRIMITIVE_CLICK)
.compose();
VibrationEffect effect4 = VibrationEffect.createOneShot(8000, 100);
- VibrationEffect effect5 = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+ VibrationEffect effect5 = VibrationEffect.get(EFFECT_CLICK);
HalVibration vibration1 = startThreadAndDispatcher(effect1);
waitForCompletion();
@@ -1971,42 +2019,42 @@
waitForCompletion();
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
- assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
// Effect1
- verify(mControllerCallbacks).onComplete(VIBRATOR_ID, vibration1.id);
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration1.id), anyLong());
verifyCallbacksTriggered(vibration1, Status.FINISHED);
- assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
- fakeVibrator.getEffectSegments(vibration1.id));
+ assertThat(fakeVibrator.getEffectSegments(vibration1.id))
+ .containsExactly(expectedPrebaked(EFFECT_CLICK)).inOrder();
// Effect2: repeating, cancelled.
- verify(mControllerCallbacks, atLeast(2)).onComplete(VIBRATOR_ID, vibration2.id);
+ verify(mControllerCallbacks, atLeast(2))
+ .onComplete(eq(VIBRATOR_ID), eq(vibration2.id), anyLong());
verifyCallbacksTriggered(vibration2, Status.CANCELLED_BY_USER);
// The exact count of segments might vary, so just check that there's more than 2 and
// all elements are the same segment.
List<VibrationEffectSegment> actualSegments2 =
fakeVibrator.getEffectSegments(vibration2.id);
- assertTrue(actualSegments2.size() + " > 2", actualSegments2.size() > 2);
+ assertThat(actualSegments2.size()).isGreaterThan(2);
for (VibrationEffectSegment segment : actualSegments2) {
- assertEquals(expectedPrebaked(VibrationEffect.EFFECT_TICK), segment);
+ assertThat(segment).isEqualTo(expectedPrebaked(EFFECT_TICK));
}
// Effect3
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration3.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration3.id), anyLong());
verifyCallbacksTriggered(vibration3, Status.FINISHED);
- assertEquals(Arrays.asList(
- expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0)),
- fakeVibrator.getEffectSegments(vibration3.id));
+ assertThat(fakeVibrator.getEffectSegments(vibration3.id))
+ .containsExactly(expectedPrimitive(PRIMITIVE_CLICK, 1, 0)).inOrder();
// Effect4: cancelled quickly.
verifyCallbacksTriggered(vibration4, Status.CANCELLED_BY_SCREEN_OFF);
- assertTrue("Tested duration=" + duration4, duration4 < 2000);
+ assertThat(duration4).isLessThan(2000);
// Effect5: played normally after effect4, which may or may not have played.
- assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
- fakeVibrator.getEffectSegments(vibration5.id));
+ assertThat(fakeVibrator.getEffectSegments(vibration5.id))
+ .containsExactly(expectedPrebaked(EFFECT_CLICK)).inOrder();
}
@Test
@@ -2014,23 +2062,18 @@
mockVibrators(1, 2, 3);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- mVibratorProviders.get(2).setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK);
- mVibratorProviders.get(3).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+ mVibratorProviders.get(2).setSupportedPrimitives(PRIMITIVE_CLICK);
+ mVibratorProviders.get(3).setSupportedEffects(EFFECT_CLICK);
CombinedVibration effect = CombinedVibration.startSequential()
- .addNext(3,
- VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
- /* delay= */ TEST_TIMEOUT_MILLIS)
+ .addNext(3, VibrationEffect.get(EFFECT_CLICK), /* delay= */ TEST_TIMEOUT_MILLIS)
.addNext(1,
VibrationEffect.createWaveform(
- new long[] {TEST_TIMEOUT_MILLIS, TEST_TIMEOUT_MILLIS},
- /* repeat= */ -1),
+ new long[] {TEST_TIMEOUT_MILLIS, TEST_TIMEOUT_MILLIS}, -1),
/* delay= */ TEST_TIMEOUT_MILLIS)
.addNext(2,
VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1,
- /* delay= */ TEST_TIMEOUT_MILLIS)
+ .addPrimitive(PRIMITIVE_CLICK, 1, /* delay= */ TEST_TIMEOUT_MILLIS)
.compose(),
/* delay= */ TEST_TIMEOUT_MILLIS)
.combine();
@@ -2041,21 +2084,22 @@
// Vibrating state remains ON until session resets it.
verifyCallbacksTriggered(vibration, Status.FINISHED);
- assertTrue(mControllers.get(1).isVibrating());
- assertTrue(mControllers.get(2).isVibrating());
- assertTrue(mControllers.get(3).isVibrating());
+ assertThat(mControllers.get(1).isVibrating()).isTrue();
+ assertThat(mControllers.get(2).isVibrating()).isTrue();
+ assertThat(mControllers.get(3).isVibrating()).isTrue();
- assertEquals(0, mVibratorProviders.get(1).getOffCount());
- assertEquals(0, mVibratorProviders.get(2).getOffCount());
- assertEquals(0, mVibratorProviders.get(3).getOffCount());
- assertEquals(Arrays.asList(expectedOneShot(TEST_TIMEOUT_MILLIS)),
- mVibratorProviders.get(1).getEffectSegments(vibration.id));
- assertEquals(expectedAmplitudes(255), mVibratorProviders.get(1).getAmplitudes());
- assertEquals(Arrays.asList(expectedPrimitive(
- VibrationEffect.Composition.PRIMITIVE_CLICK, 1, TEST_TIMEOUT_MILLIS)),
- mVibratorProviders.get(2).getEffectSegments(vibration.id));
- assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
- mVibratorProviders.get(3).getEffectSegments(vibration.id));
+ assertThat(mVibratorProviders.get(1).getOffCount()).isEqualTo(0);
+ assertThat(mVibratorProviders.get(2).getOffCount()).isEqualTo(0);
+ assertThat(mVibratorProviders.get(3).getOffCount()).isEqualTo(0);
+ assertThat(mVibratorProviders.get(1).getEffectSegments(vibration.id))
+ .containsExactly(expectedOneShot(TEST_TIMEOUT_MILLIS)).inOrder();
+ assertThat(mVibratorProviders.get(1).getAmplitudes())
+ .containsExactlyElementsIn(expectedAmplitudes(255)).inOrder();
+ assertThat(mVibratorProviders.get(2).getEffectSegments(vibration.id))
+ .containsExactly(expectedPrimitive(PRIMITIVE_CLICK, 1, TEST_TIMEOUT_MILLIS))
+ .inOrder();
+ assertThat(mVibratorProviders.get(3).getEffectSegments(vibration.id))
+ .containsExactly(expectedPrebaked(EFFECT_CLICK)).inOrder();
}
private void mockVibrators(int... vibratorIds) {
@@ -2109,7 +2153,7 @@
mVibrationConductor = new VibrationStepConductor(vib, isInSession, mVibrationSettings,
deviceAdapter, mVibrationScaler, mStatsLoggerMock, requestVibrationParamsFuture,
mManagerHooks);
- assertTrue(mThread.runVibrationOnVibrationThread(mVibrationConductor));
+ assertThat(mThread.runVibrationOnVibrationThread(mVibrationConductor)).isTrue();
return mVibrationConductor.getVibration();
}
@@ -2129,8 +2173,8 @@
}
private void waitForCompletion(long timeout) {
- assertTrue("Timed out waiting for VibrationThread to become idle",
- mThread.waitForThreadIdle(timeout));
+ assertWithMessage("Timed out waiting for VibrationThread to become idle")
+ .that(mThread.waitForThreadIdle(timeout)).isTrue();
mTestLooper.dispatchAll(); // Flush callbacks
}
@@ -2205,7 +2249,7 @@
private void verifyCallbacksTriggered(HalVibration vibration, Status expectedStatus) {
assertThat(vibration.getStatus()).isEqualTo(expectedStatus);
- verify(mManagerHooks).onVibrationThreadReleased(vibration.id);
+ verify(mManagerHooks).onVibrationThreadReleased(eq(vibration.id));
}
private static final class TestLooperAutoDispatcher extends Thread {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerTest.java
index 0978f48..4df13de 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerTest.java
@@ -203,9 +203,10 @@
@Test
public void setAmplitude_vibratorVibrating_setsAmplitude() {
- when(mNativeWrapperMock.on(anyLong(), anyLong())).thenAnswer(args -> args.getArgument(0));
+ when(mNativeWrapperMock.on(anyLong(), anyLong(), anyLong()))
+ .thenAnswer(args -> args.getArgument(0));
VibratorController controller = createController();
- controller.on(100, /* vibrationId= */ 1);
+ controller.on(100, 1, 1);
assertTrue(controller.isVibrating());
assertEquals(-1, controller.getCurrentAmplitude(), /* delta= */ 0);
@@ -215,81 +216,84 @@
@Test
public void on_withDuration_turnsVibratorOn() {
- when(mNativeWrapperMock.on(anyLong(), anyLong())).thenAnswer(args -> args.getArgument(0));
+ when(mNativeWrapperMock.on(anyLong(), anyLong(), anyLong()))
+ .thenAnswer(args -> args.getArgument(0));
VibratorController controller = createController();
- controller.on(100, 10);
+ controller.on(100, 10, 20);
assertTrue(controller.isVibrating());
- verify(mNativeWrapperMock).on(eq(100L), eq(10L));
+ verify(mNativeWrapperMock).on(eq(100L), eq(10L), eq(20L));
}
@Test
public void on_withPrebaked_performsEffect() {
- when(mNativeWrapperMock.perform(anyLong(), anyLong(), anyLong())).thenReturn(10L);
+ when(mNativeWrapperMock.perform(anyLong(), anyLong(), anyLong(), anyLong()))
+ .thenReturn(10L);
VibratorController controller = createController();
PrebakedSegment prebaked = createPrebaked(VibrationEffect.EFFECT_CLICK,
VibrationEffect.EFFECT_STRENGTH_MEDIUM);
- assertEquals(10L, controller.on(prebaked, 11));
+ assertEquals(10L, controller.on(prebaked, 11, 23));
assertTrue(controller.isVibrating());
verify(mNativeWrapperMock).perform(eq((long) VibrationEffect.EFFECT_CLICK),
- eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), eq(11L));
+ eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), eq(11L), eq(23L));
}
@Test
public void on_withComposed_performsEffect() {
mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- when(mNativeWrapperMock.compose(any(), anyLong())).thenReturn(15L);
+ when(mNativeWrapperMock.compose(any(), anyLong(), anyLong())).thenReturn(15L);
VibratorController controller = createController();
PrimitiveSegment[] primitives = new PrimitiveSegment[]{
new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10)
};
- assertEquals(15L, controller.on(primitives, 12));
+ assertEquals(15L, controller.on(primitives, 12, 34));
assertTrue(controller.isVibrating());
- verify(mNativeWrapperMock).compose(eq(primitives), eq(12L));
+ verify(mNativeWrapperMock).compose(eq(primitives), eq(12L), eq(34L));
}
@Test
public void on_withComposedPwle_performsEffect() {
mockVibratorCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
- when(mNativeWrapperMock.composePwle(any(), anyInt(), anyLong())).thenReturn(15L);
+ when(mNativeWrapperMock.composePwle(any(), anyInt(), anyLong(), anyLong())).thenReturn(15L);
VibratorController controller = createController();
RampSegment[] primitives = new RampSegment[]{
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1,
/* startFrequencyHz= */ 100, /* endFrequencyHz= */ 200, /* duration= */ 10)
};
- assertEquals(15L, controller.on(primitives, 12));
+ assertEquals(15L, controller.on(primitives, 12, 45));
assertTrue(controller.isVibrating());
- verify(mNativeWrapperMock).composePwle(eq(primitives), eq(Braking.NONE), eq(12L));
+ verify(mNativeWrapperMock).composePwle(eq(primitives), eq(Braking.NONE), eq(12L), eq(45L));
}
@Test
public void on_withComposedPwleV2_performsEffect() {
mockVibratorCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2);
- when(mNativeWrapperMock.composePwleV2(any(), anyLong())).thenReturn(15L);
+ when(mNativeWrapperMock.composePwleV2(any(), anyLong(), anyLong())).thenReturn(15L);
VibratorController controller = createController();
PwlePoint[] primitives = new PwlePoint[]{
new PwlePoint(/*amplitude=*/ 0, /*frequencyHz=*/ 100, /*timeMillis=*/ 0),
new PwlePoint(/*amplitude=*/ 1, /*frequencyHz=*/ 200, /*timeMillis=*/ 10)
};
- assertEquals(15L, controller.on(primitives, 12));
+ assertEquals(15L, controller.on(primitives, 12, 53));
assertTrue(controller.isVibrating());
- verify(mNativeWrapperMock).composePwleV2(eq(primitives), eq(12L));
+ verify(mNativeWrapperMock).composePwleV2(eq(primitives), eq(12L), eq(53L));
}
@Test
public void off_turnsOffVibrator() {
- when(mNativeWrapperMock.on(anyLong(), anyLong())).thenAnswer(args -> args.getArgument(0));
+ when(mNativeWrapperMock.on(anyLong(), anyLong(), anyLong()))
+ .thenAnswer(args -> args.getArgument(0));
VibratorController controller = createController();
- controller.on(100, 1);
+ controller.on(100, 1, 1);
assertTrue(controller.isVibrating());
controller.off();
@@ -301,10 +305,11 @@
@Test
public void reset_turnsOffVibratorAndDisablesExternalControl() {
mockVibratorCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
- when(mNativeWrapperMock.on(anyLong(), anyLong())).thenAnswer(args -> args.getArgument(0));
+ when(mNativeWrapperMock.on(anyLong(), anyLong(), anyLong()))
+ .thenAnswer(args -> args.getArgument(0));
VibratorController controller = createController();
- controller.on(100, 1);
+ controller.on(100, 1, 1);
assertTrue(controller.isVibrating());
controller.reset();
@@ -315,12 +320,13 @@
@Test
public void registerVibratorStateListener_callbacksAreTriggered() throws Exception {
- when(mNativeWrapperMock.on(anyLong(), anyLong())).thenAnswer(args -> args.getArgument(0));
+ when(mNativeWrapperMock.on(anyLong(), anyLong(), anyLong()))
+ .thenAnswer(args -> args.getArgument(0));
VibratorController controller = createController();
controller.registerVibratorStateListener(mVibratorStateListenerMock);
- controller.on(10, 1);
- controller.on(100, 2);
+ controller.on(10, 1, 1);
+ controller.on(100, 2, 1);
controller.off();
controller.off();
@@ -334,19 +340,20 @@
@Test
public void unregisterVibratorStateListener_callbackNotTriggeredAfter() throws Exception {
- when(mNativeWrapperMock.on(anyLong(), anyLong())).thenAnswer(args -> args.getArgument(0));
+ when(mNativeWrapperMock.on(anyLong(), anyLong(), anyLong()))
+ .thenAnswer(args -> args.getArgument(0));
VibratorController controller = createController();
controller.registerVibratorStateListener(mVibratorStateListenerMock);
verify(mVibratorStateListenerMock).onVibrating(false);
- controller.on(10, 1);
+ controller.on(10, 1, 1);
verify(mVibratorStateListenerMock).onVibrating(true);
controller.unregisterVibratorStateListener(mVibratorStateListenerMock);
Mockito.clearInvocations(mVibratorStateListenerMock);
- controller.on(10, 1);
+ controller.on(10, 1, 1);
verifyNoMoreInteractions(mVibratorStateListenerMock);
}
diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
index 3f34767..bd806b7 100644
--- a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -118,11 +118,11 @@
}
@Override
- public long on(long milliseconds, long vibrationId) {
+ public long on(long milliseconds, long vibrationId, long stepId) {
recordEffectSegment(vibrationId, new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE,
/* frequencyHz= */ 0, (int) milliseconds));
applyLatency(mOnLatency);
- scheduleListener(milliseconds, vibrationId);
+ scheduleListener(milliseconds, vibrationId, stepId);
return milliseconds;
}
@@ -139,7 +139,7 @@
}
@Override
- public long perform(long effect, long strength, long vibrationId) {
+ public long perform(long effect, long strength, long vibrationId, long stepId) {
if (mSupportedEffects == null
|| Arrays.binarySearch(mSupportedEffects, (int) effect) < 0) {
return 0;
@@ -147,13 +147,13 @@
recordEffectSegment(vibrationId,
new PrebakedSegment((int) effect, false, (int) strength));
applyLatency(mOnLatency);
- scheduleListener(EFFECT_DURATION, vibrationId);
+ scheduleListener(EFFECT_DURATION, vibrationId, stepId);
return EFFECT_DURATION;
}
@Override
public long performVendorEffect(Parcel vendorData, long strength, float scale,
- float adaptiveScale, long vibrationId) {
+ float adaptiveScale, long vibrationId, long stepId) {
if ((mCapabilities & IVibrator.CAP_PERFORM_VENDOR_EFFECTS) == 0) {
return 0;
}
@@ -161,13 +161,13 @@
recordVendorEffect(vibrationId,
new VibrationEffect.VendorEffect(bundle, (int) strength, scale, adaptiveScale));
applyLatency(mOnLatency);
- scheduleListener(mVendorEffectDuration, vibrationId);
+ scheduleListener(mVendorEffectDuration, vibrationId, stepId);
// HAL has unknown duration for vendor effects.
return Long.MAX_VALUE;
}
@Override
- public long compose(PrimitiveSegment[] primitives, long vibrationId) {
+ public long compose(PrimitiveSegment[] primitives, long vibrationId, long stepId) {
if (mSupportedPrimitives == null) {
return 0;
}
@@ -182,12 +182,13 @@
recordEffectSegment(vibrationId, primitive);
}
applyLatency(mOnLatency);
- scheduleListener(duration, vibrationId);
+ scheduleListener(duration, vibrationId, stepId);
return duration;
}
@Override
- public long composePwle(RampSegment[] primitives, int braking, long vibrationId) {
+ public long composePwle(RampSegment[] primitives, int braking, long vibrationId,
+ long stepId) {
long duration = 0;
for (RampSegment primitive : primitives) {
duration += primitive.getDuration();
@@ -195,19 +196,19 @@
}
recordBraking(vibrationId, braking);
applyLatency(mOnLatency);
- scheduleListener(duration, vibrationId);
+ scheduleListener(duration, vibrationId, stepId);
return duration;
}
@Override
- public long composePwleV2(PwlePoint[] pwlePoints, long vibrationId) {
+ public long composePwleV2(PwlePoint[] pwlePoints, long vibrationId, long stepId) {
long duration = 0;
for (PwlePoint pwlePoint: pwlePoints) {
duration += pwlePoint.getTimeMillis();
recordEffectPwlePoint(vibrationId, pwlePoint);
}
applyLatency(mOnLatency);
- scheduleListener(duration, vibrationId);
+ scheduleListener(duration, vibrationId, stepId);
return duration;
}
@@ -263,8 +264,8 @@
}
}
- private void scheduleListener(long vibrationDuration, long vibrationId) {
- mHandler.postDelayed(() -> listener.onComplete(vibratorId, vibrationId),
+ private void scheduleListener(long vibrationDuration, long vibrationId, long stepId) {
+ mHandler.postDelayed(() -> listener.onComplete(vibratorId, vibrationId, stepId),
vibrationDuration + mCompletionCallbackDelay);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index d53ba1d..301a754 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -56,8 +56,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.TRANSIT_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_PIP;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_LEGACY_SPLASH_SCREEN;
@@ -921,12 +919,6 @@
// animation and AR#takeSceneTransitionInfo also clear the AR#mPendingOptions
assertNull(activity.takeSceneTransitionInfo());
assertNull(activity.getOptions());
-
- final AppTransition appTransition = activity.mDisplayContent.mAppTransition;
- spyOn(appTransition);
- activity.applyOptionsAnimation();
-
- verify(appTransition).overridePendingAppTransitionRemote(any());
}
@Test
@@ -1190,7 +1182,6 @@
FINISH_RESULT_REQUESTED, activity.finishIfPossible("test", false /* oomAdj */));
assertEquals(PAUSING, activity.getState());
verify(activity).setVisibility(eq(false));
- verify(activity.mDisplayContent).prepareAppTransition(eq(TRANSIT_CLOSE));
}
/**
@@ -1237,7 +1228,6 @@
activity.finishIfPossible("test", false /* oomAdj */);
verify(activity).setVisibility(eq(false));
- verify(activity.mDisplayContent).prepareAppTransition(eq(TRANSIT_CLOSE));
verify(activity.mDisplayContent, never()).executeAppTransition();
}
@@ -1254,7 +1244,6 @@
activity.finishIfPossible("test", false /* oomAdj */);
verify(activity, atLeast(1)).setVisibility(eq(false));
- verify(activity.mDisplayContent).prepareAppTransition(eq(TRANSIT_CLOSE));
verify(activity.mDisplayContent).executeAppTransition();
}
@@ -1275,7 +1264,6 @@
activity.finishIfPossible("test", false /* oomAdj */);
- verify(activity.mDisplayContent, never()).prepareAppTransition(eq(TRANSIT_CLOSE));
assertFalse(activity.inTransition());
// finishIfPossible -> completeFinishing -> addToFinishingAndWaitForIdle
@@ -2657,10 +2645,6 @@
@Presubmit
public void testGetOrientation() {
mDisplayContent.setIgnoreOrientationRequest(false);
- // ActivityBuilder will resume top activities and cause the activity been added into
- // opening apps list. Since this test is focus on the effect of visible on getting
- // orientation, we skip app transition to avoid interference.
- doNothing().when(mDisplayContent).prepareAppTransition(anyInt());
final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
activity.setVisible(true);
@@ -2925,7 +2909,6 @@
sources.add(activity2);
doReturn(true).when(activity2).okToAnimate();
doReturn(true).when(activity2).isAnimating();
- assertTrue(activity2.applyAnimation(null, TRANSIT_OLD_ACTIVITY_OPEN, true, false, sources));
}
@Test
public void testTrackingStartingWindowThroughTrampoline() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
index 4ad1cd1..d4be7f8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
@@ -23,12 +23,14 @@
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA;
import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA;
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION;
+import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION;
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH;
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING;
+import static com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT;
import android.compat.testing.PlatformCompatChangeRule;
import android.platform.test.annotations.DisableFlags;
@@ -239,6 +241,34 @@
@Test
@EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+ @EnableFlags({FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING,
+ FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT})
+ public void testShouldApplyCameraCompatFreeformTreatment_propertyFalse_returnsFalse() {
+ runTestScenario((robot) -> {
+ robot.activity().createActivityWithComponentInNewTask();
+
+ robot.prop().disable(PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION);
+
+ robot.checkShouldApplyFreeformTreatmentForCameraCompat(false);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT)
+ public void testShouldApplyCameraCompatFreeformTreatment_optOutFlagNotEnabled_optOutIgnored() {
+ runTestScenario((robot) -> {
+ robot.activity().createActivityWithComponentInNewTask();
+
+ robot.prop().disable(PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION);
+
+ robot.checkShouldApplyFreeformTreatmentForCameraCompat(true);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
@EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
public void testShouldApplyCameraCompatFreeformTreatment_overrideAndFlagEnabled_returnsTrue() {
runTestScenario((robot) -> {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index d5b9751..a99bc49 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -37,7 +37,6 @@
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
-import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
@@ -56,6 +55,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
@@ -63,9 +63,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -1742,8 +1739,6 @@
public void testFixedRotationWithPip() {
final DisplayContent displayContent = mDefaultDisplay;
displayContent.setIgnoreOrientationRequest(false);
- // Unblock the condition in PinnedTaskController#continueOrientationChangeIfNeeded.
- doNothing().when(displayContent).prepareAppTransition(anyInt());
// Make resume-top really update the activity state.
setBooted(mAtm);
clearInvocations(mWm);
@@ -1826,7 +1821,7 @@
.setTask(nonTopVisible.getTask()).setVisible(false)
.setActivityTheme(android.R.style.Theme_Translucent).build();
final TestTransitionPlayer player = registerTestTransitionPlayer();
- mDisplayContent.requestTransitionAndLegacyPrepare(WindowManager.TRANSIT_OPEN, 0);
+ mDisplayContent.requestTransitionAndLegacyPrepare(WindowManager.TRANSIT_OPEN, 0, null);
translucentTop.setVisibility(true);
mDisplayContent.updateOrientation();
assertEquals("Non-top visible activity must be portrait",
@@ -2373,33 +2368,6 @@
@SetupWindows(addWindows = W_INPUT_METHOD)
@Test
- public void testShowImeScreenshot() {
- final Task rootTask = createTask(mDisplayContent);
- final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
- final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
- final WindowState win = newWindowBuilder("win", TYPE_BASE_APPLICATION).setWindowToken(
- activity).build();
- task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE);
- doReturn(true).when(task).okToAnimate();
- ArrayList<WindowContainer> sources = new ArrayList<>();
- sources.add(activity);
-
- mDisplayContent.setImeLayeringTarget(win);
- spyOn(mDisplayContent);
-
- // Expecting the IME screenshot only be attached when performing task closing transition.
- task.applyAnimation(null, TRANSIT_OLD_TASK_CLOSE, false /* enter */,
- false /* isVoiceInteraction */, sources);
- verify(mDisplayContent).showImeScreenshot();
-
- clearInvocations(mDisplayContent);
- activity.applyAnimation(null, TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE, false /* enter */,
- false /* isVoiceInteraction */, sources);
- verify(mDisplayContent, never()).showImeScreenshot();
- }
-
- @SetupWindows(addWindows = W_INPUT_METHOD)
- @Test
public void testShowImeScreenshot_removeCurSnapshotBeforeCreateNext() {
final Task rootTask = createTask(mDisplayContent);
final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
@@ -2427,32 +2395,6 @@
@UseTestDisplay(addWindows = {W_INPUT_METHOD})
@Test
- public void testRemoveImeScreenshot_whenTargetSurfaceWasInvisible() {
- final Task rootTask = createTask(mDisplayContent);
- final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
- final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
- final WindowState win = newWindowBuilder("win", TYPE_BASE_APPLICATION).setWindowToken(
- activity).build();
- win.onSurfaceShownChanged(true);
- makeWindowVisible(win, mDisplayContent.mInputMethodWindow);
- task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE);
- doReturn(true).when(task).okToAnimate();
- ArrayList<WindowContainer> sources = new ArrayList<>();
- sources.add(activity);
-
- mDisplayContent.setImeLayeringTarget(win);
- mDisplayContent.setImeInputTarget(win);
- mDisplayContent.getInsetsStateController().getImeSourceProvider().setImeShowing(true);
- task.applyAnimation(null, TRANSIT_OLD_TASK_CLOSE, false /* enter */,
- false /* isVoiceInteraction */, sources);
- assertNotNull(mDisplayContent.mImeScreenshot);
-
- win.onSurfaceShownChanged(false);
- assertNull(mDisplayContent.mImeScreenshot);
- }
-
- @UseTestDisplay(addWindows = {W_INPUT_METHOD})
- @Test
public void testRemoveImeScreenshot_whenWindowRemoveImmediately() {
final Task rootTask = createTask(mDisplayContent);
final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
@@ -2751,55 +2693,77 @@
@SetupWindows(addWindows = W_INPUT_METHOD)
@Test
- public void testImeChildWindowFocusWhenImeLayeringTargetChanges() {
- final WindowState imeChildWindow = newWindowBuilder("imeChildWindow",
+ public void testImeChildWindowFocusWhenImeParentWindowChanges() {
+ final var imeChildWindow = newWindowBuilder("imeChildWindow",
TYPE_APPLICATION_ATTACHED_DIALOG).setParent(mImeWindow).build();
- makeWindowVisibleAndDrawn(imeChildWindow, mImeWindow);
- assertTrue(imeChildWindow.canReceiveKeys());
- mDisplayContent.setInputMethodWindowLocked(mImeWindow);
-
- // Verify imeChildWindow can be focused window if the next IME target requests IME visible.
- final WindowState imeAppTarget = newWindowBuilder("imeAppTarget",
- TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
- mDisplayContent.setImeLayeringTarget(imeAppTarget);
- spyOn(imeAppTarget);
- doReturn(true).when(imeAppTarget).isRequestedVisible(ime());
- assertEquals(imeChildWindow, mDisplayContent.findFocusedWindow());
-
- // Verify imeChildWindow doesn't be focused window if the next IME target does not
- // request IME visible.
- final WindowState nextImeAppTarget = newWindowBuilder("nextImeAppTarget",
- TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
- mDisplayContent.setImeLayeringTarget(nextImeAppTarget);
- assertNotEquals(imeChildWindow, mDisplayContent.findFocusedWindow());
+ doTestImeWindowFocusWhenImeParentWindowChanged(imeChildWindow);
}
@SetupWindows(addWindows = W_INPUT_METHOD)
@Test
- public void testImeMenuDialogFocusWhenImeLayeringTargetChanges() {
- final WindowState imeMenuDialog = newWindowBuilder("imeMenuDialog",
+ public void testImeDialogWindowFocusWhenImeParentWindowChanges() {
+ final var imeDialogWindow = newWindowBuilder("imeMenuDialog",
TYPE_INPUT_METHOD_DIALOG).build();
- makeWindowVisibleAndDrawn(imeMenuDialog, mImeWindow);
- assertTrue(imeMenuDialog.canReceiveKeys());
+ doTestImeWindowFocusWhenImeParentWindowChanged(imeDialogWindow);
+ }
+
+ @SetupWindows(addWindows = W_INPUT_METHOD)
+ @Test
+ public void testImeWindowFocusWhenImeParentWindowChanges() {
+ // Verify focusable, non-child IME windows.
+ final var otherImeWindow = newWindowBuilder("otherImeWindow",
+ TYPE_INPUT_METHOD).build();
+ doTestImeWindowFocusWhenImeParentWindowChanged(otherImeWindow);
+ }
+
+ private void doTestImeWindowFocusWhenImeParentWindowChanged(@NonNull WindowState window) {
+ makeWindowVisibleAndDrawn(window, mImeWindow);
+ assertTrue("Window canReceiveKeys", window.canReceiveKeys());
mDisplayContent.setInputMethodWindowLocked(mImeWindow);
- // Verify imeMenuDialog can be focused window if the next IME target requests IME visible.
+ // Verify window can be focused if the IME parent is visible and the IME is visible.
final WindowState imeAppTarget = newWindowBuilder("imeAppTarget",
TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
mDisplayContent.setImeLayeringTarget(imeAppTarget);
- imeAppTarget.setRequestedVisibleTypes(ime());
- assertEquals(imeMenuDialog, mDisplayContent.findFocusedWindow());
+ mDisplayContent.updateImeInputAndControlTarget(imeAppTarget);
+ final var imeProvider = mDisplayContent.getInsetsStateController().getImeSourceProvider();
+ imeProvider.setImeShowing(true);
+ final var imeParentWindow = mDisplayContent.getImeParentWindow();
+ assertNotNull("IME parent window is not null", imeParentWindow);
+ assertTrue("IME parent window is visible", imeParentWindow.isVisibleRequested());
+ assertTrue("IME is visible", imeProvider.isImeShowing());
+ assertEquals("Window is the focused one", window, mDisplayContent.findFocusedWindow());
- // Verify imeMenuDialog doesn't be focused window if the next IME target is closing.
+ // Verify window can't be focused if the IME parent is not visible.
final WindowState nextImeAppTarget = newWindowBuilder("nextImeAppTarget",
TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
makeWindowVisibleAndDrawn(nextImeAppTarget);
- // Even if the app still requests IME, the ime dialog should not gain focus if the target
- // app is invisible.
- nextImeAppTarget.setRequestedVisibleTypes(ime());
- nextImeAppTarget.mActivityRecord.setVisibility(false);
+ // Change layering target but keep input target (and thus imeParent) the same.
mDisplayContent.setImeLayeringTarget(nextImeAppTarget);
- assertNotEquals(imeMenuDialog, mDisplayContent.findFocusedWindow());
+ // IME parent window is not visible, occluded by new layering target.
+ imeParentWindow.setVisibleRequested(false);
+ assertEquals("IME parent window did not change", imeParentWindow,
+ mDisplayContent.getImeParentWindow());
+ assertFalse("IME parent window is not visible", imeParentWindow.isVisibleRequested());
+ assertTrue("IME is visible", imeProvider.isImeShowing());
+ assertNotEquals("Window is not the focused one when imeParent is not visible", window,
+ mDisplayContent.findFocusedWindow());
+
+ // Verify window can be focused if the IME is not visible.
+ mDisplayContent.updateImeInputAndControlTarget(nextImeAppTarget);
+ imeProvider.setImeShowing(false);
+ final var nextImeParentWindow = mDisplayContent.getImeParentWindow();
+ assertNotNull("Next IME parent window is not null", nextImeParentWindow);
+ assertNotEquals("IME parent window changed", imeParentWindow, nextImeParentWindow);
+ assertTrue("Next IME parent window is visible", nextImeParentWindow.isVisibleRequested());
+ assertFalse("IME is not visible", imeProvider.isImeShowing());
+ if (window.isChildWindow()) {
+ assertNotEquals("Child window is not the focused on when the IME is not visible",
+ window, mDisplayContent.findFocusedWindow());
+ } else {
+ assertEquals("Window is the focused one when the IME is not visible",
+ window, mDisplayContent.findFocusedWindow());
+ }
}
@Test
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 7c8a883..2d4101e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PresentationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PresentationControllerTests.java
@@ -31,7 +31,7 @@
import android.annotation.NonNull;
import android.graphics.Rect;
import android.os.UserHandle;
-import android.os.UserManager;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.view.DisplayInfo;
@@ -68,32 +68,14 @@
@EnableFlags(FLAG_ENABLE_PRESENTATION_FOR_CONNECTED_DISPLAYS)
@Test
public void testPresentationShowAndHide() {
- final DisplayInfo displayInfo = new DisplayInfo();
- displayInfo.copyFrom(mDisplayInfo);
- displayInfo.flags = FLAG_PRESENTATION;
- final DisplayContent dc = createNewDisplay(displayInfo);
- final int displayId = dc.getDisplayId();
- doReturn(dc).when(mWm.mRoot).getDisplayContentOrCreate(displayId);
+ final DisplayContent dc = createPresentationDisplay();
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);
- final int userId = UserHandle.getUserId(uid);
- 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);
+ // Add a presentation window, which requests the activity to stop.
+ final WindowState window = addPresentationWindow(100000, dc.mDisplayId);
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));
@@ -117,6 +99,51 @@
assertTrue(activity.isVisible());
}
+ @DisableFlags(FLAG_ENABLE_PRESENTATION_FOR_CONNECTED_DISPLAYS)
+ @Test
+ public void testPresentationShowAndHide_flagDisabled() {
+ final DisplayContent dc = createPresentationDisplay();
+ final ActivityRecord activity = createActivityRecord(createTask(dc));
+ assertTrue(activity.isVisible());
+
+ final WindowState window = addPresentationWindow(100000, dc.mDisplayId);
+ assertFalse(window.mTransitionController.isCollecting());
+ assertTrue(activity.isVisibleRequested());
+ assertTrue(activity.isVisible());
+
+ window.removeIfPossible();
+ assertFalse(window.mTransitionController.isCollecting());
+ assertTrue(activity.isVisibleRequested());
+ assertTrue(activity.isVisible());
+ assertFalse(window.isAttached());
+ }
+
+ private WindowState addPresentationWindow(int uid, int displayId) {
+ final Session session = createTestSession(mAtm, 1234 /* pid */, uid);
+ final int userId = UserHandle.getUserId(uid);
+ doReturn(true).when(mWm.mUmInternal).isUserVisible(eq(userId), eq(displayId));
+ final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.TYPE_PRESENTATION);
+ final IWindow clientWindow = new TestIWindow();
+ final int res = mWm.addWindow(session, clientWindow, params, View.VISIBLE, displayId,
+ userId, WindowInsets.Type.defaultVisible(), null, new InsetsState(),
+ new InsetsSourceControl.Array(), new Rect(), new float[1]);
+ assertTrue(res >= WindowManagerGlobal.ADD_OKAY);
+ final WindowState window = mWm.windowForClientLocked(session, clientWindow, false);
+ window.mHasSurface = true;
+ return window;
+ }
+
+ private DisplayContent createPresentationDisplay() {
+ final DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.copyFrom(mDisplayInfo);
+ displayInfo.flags = FLAG_PRESENTATION;
+ final DisplayContent dc = createNewDisplay(displayInfo);
+ final int displayId = dc.getDisplayId();
+ doReturn(dc).when(mWm.mRoot).getDisplayContentOrCreate(displayId);
+ return dc;
+ }
+
private void completeTransition(@NonNull Transition transition, boolean abortSync) {
final ActionChain chain = ActionChain.testFinish(transition);
if (abortSync) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 9406779..3776b036 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -264,7 +264,7 @@
final DisplayManagerGlobal dmg = DisplayManagerGlobal.getInstance();
spyOn(dmg);
doNothing().when(dmg).registerDisplayListener(
- any(), any(Executor.class), anyLong(), anyString());
+ any(), any(Executor.class), anyLong(), anyString(), anyBoolean());
doNothing().when(dmg).registerTopologyListener(any(Executor.class), any(), anyString());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 2ee34d3..c0cb09f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -82,8 +82,6 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -104,7 +102,6 @@
import androidx.test.filters.SmallTest;
import com.android.internal.graphics.ColorUtils;
-import com.android.window.flags.Flags;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -683,7 +680,7 @@
app.onStartingWindowDrawn();
// The task appeared event should be deferred until transition ready.
assertFalse(task.taskAppearedReady());
- testPlayer.onTransactionReady(app.getSyncTransaction());
+ testPlayer.onTransactionReady();
assertTrue(task.taskAppearedReady());
assertTrue(playerProc.isRunningRemoteTransition());
assertTrue(controller.mRemotePlayer.reportRunning(delegateProc.getThread()));
@@ -1162,7 +1159,8 @@
screenDecor.updateSurfacePosition(mMockT);
assertEquals(prevPos, screenDecor.mLastSurfacePosition);
- final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction startTransaction = mTransaction;
+ clearInvocations(startTransaction);
final SurfaceControl.TransactionCommittedListener transactionCommittedListener =
onRotationTransactionReady(player, startTransaction);
@@ -1213,7 +1211,8 @@
assertFalse(statusBar.mToken.inTransition());
assertTrue(app.getTask().inTransition());
- final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction startTransaction = mTransaction;
+ clearInvocations(startTransaction);
final SurfaceControl leash = statusBar.mToken.getAnimationLeash();
doReturn(true).when(leash).isValid();
final SurfaceControl.TransactionCommittedListener transactionCommittedListener =
@@ -1287,7 +1286,8 @@
// Avoid DeviceStateController disturbing the test by triggering another rotation change.
doReturn(false).when(mDisplayContent).updateRotationUnchecked();
- onRotationTransactionReady(player, mWm.mTransactionFactory.get()).onTransactionCommitted();
+ clearInvocations(mTransaction);
+ onRotationTransactionReady(player, mTransaction).onTransactionCommitted();
assertEquals(ROTATION_ANIMATION_SEAMLESS, player.mLastReady.getChange(
mDisplayContent.mRemoteToken.toWindowContainerToken()).getRotationAnimation());
spyOn(navBarInsetsProvider);
@@ -1350,7 +1350,7 @@
mDisplayContent.setFixedRotationLaunchingAppUnchecked(home);
doReturn(true).when(home).hasFixedRotationTransform(any());
player.startTransition();
- player.onTransactionReady(mDisplayContent.getSyncTransaction());
+ player.onTransactionReady();
final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
final RemoteDisplayChangeController displayChangeController = mDisplayContent
@@ -2009,21 +2009,6 @@
assertEquals(expectedBackgroundColor, info.getChanges().get(1).getBackgroundColor());
}
- @DisableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
- @Test
- public void testOverrideAnimationOptionsToInfoIfNecessary_disableAnimOptionsPerChange() {
- ActivityRecord r = initializeOverrideAnimationOptionsTest();
- TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions
- .makeCommonAnimOptions("testPackage");
- mTransition.setOverrideAnimation(options, r, null /* startCallback */,
- null /* finishCallback */);
-
- mTransition.overrideAnimationOptionsToInfoIfNecessary(mInfo);
-
- assertEquals(options, mInfo.getAnimationOptions());
- }
-
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testOverrideAnimationOptionsToInfoIfNecessary_fromStyleAnimOptions() {
ActivityRecord r = initializeOverrideAnimationOptionsTest();
@@ -2049,7 +2034,6 @@
options, activityChange.getAnimationOptions());
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testOverrideAnimationOptionsToInfoIfNecessary_sceneAnimOptions() {
ActivityRecord r = initializeOverrideAnimationOptionsTest();
@@ -2075,7 +2059,6 @@
options, activityChange.getAnimationOptions());
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testOverrideAnimationOptionsToInfoIfNecessary_crossProfileAnimOptions() {
ActivityRecord r = initializeOverrideAnimationOptionsTest();
@@ -2103,7 +2086,6 @@
assertTrue(activityChange.hasFlags(FLAG_CROSS_PROFILE_OWNER_THUMBNAIL));
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testOverrideAnimationOptionsToInfoIfNecessary_customAnimOptions() {
ActivityRecord r = initializeOverrideAnimationOptionsTest();
@@ -2136,7 +2118,6 @@
options.getBackgroundColor(), activityChange.getBackgroundColor());
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testOverrideAnimationOptionsToInfoIfNecessary_haveTaskFragmentAnimParams() {
ActivityRecord r = initializeOverrideAnimationOptionsTest();
@@ -2185,7 +2166,6 @@
options.getBackgroundColor(), activityChange.getBackgroundColor());
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testOverrideAnimationOptionsToInfoIfNecessary_customAnimOptionsWithTaskOverride() {
ActivityRecord r = initializeOverrideAnimationOptionsTest();
@@ -2415,7 +2395,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS)
public void testMoveDisplayToTop() {
// Set up two displays, each of which has a task.
DisplayContent otherDisplay = createNewDisplay();
@@ -3071,8 +3050,11 @@
TestTransitionPlayer player, SurfaceControl.Transaction startTransaction) {
final ArgumentCaptor<SurfaceControl.TransactionCommittedListener> listenerCaptor =
ArgumentCaptor.forClass(SurfaceControl.TransactionCommittedListener.class);
- player.onTransactionReady(startTransaction);
- verify(startTransaction).addTransactionCommittedListener(any(), listenerCaptor.capture());
+ player.onTransactionReady();
+ // The startTransaction is from mWm.mTransactionFactory.get() in SyncGroup#finishNow.
+ // 2 times are from SyncGroup#finishNow and AsyncRotationController#setupStartTransaction.
+ verify(startTransaction, times(2)).addTransactionCommittedListener(
+ any(), listenerCaptor.capture());
return listenerCaptor.getValue();
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index cee98fb..4f310de 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -30,8 +30,6 @@
import static android.view.WindowInsets.Type.systemOverlays;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.TRANSIT_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -1121,7 +1119,6 @@
final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
final WindowState win = newWindowBuilder("win", TYPE_BASE_APPLICATION).setWindowToken(
activity).build();
- task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE);
spyOn(win);
doReturn(true).when(task).okToAnimate();
ArrayList<WindowContainer> sources = new ArrayList<>();
@@ -1130,8 +1127,6 @@
// Simulate the task applying the exit transition, verify the main window of the task
// will be set the frozen insets state before the animation starts
activity.setVisibility(false);
- task.applyAnimation(null, TRANSIT_OLD_TASK_CLOSE, false /* enter */,
- false /* isVoiceInteraction */, sources);
verify(win).freezeInsetsState();
// Simulate the task transition finished.
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 97c6ac6..c641685 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -2159,13 +2159,14 @@
mOrganizer.startTransition(mLastTransit.getToken(), null);
}
- void onTransactionReady(SurfaceControl.Transaction t) {
- mLastTransit.onTransactionReady(mLastTransit.getSyncId(), t);
+ void onTransactionReady() {
+ // SyncGroup#finishNow -> Transition#onTransactionReady.
+ mController.mSyncEngine.abort(mLastTransit.getSyncId());
}
void start() {
startTransition();
- onTransactionReady(mock(SurfaceControl.Transaction.class));
+ onTransactionReady();
}
public void finish() {
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java b/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java
index 49ad461..df43ed9 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java
@@ -24,7 +24,6 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.telephony.flags.Flags;
import java.util.ArrayList;
import java.util.List;
@@ -119,28 +118,18 @@
private boolean checkCallStatus() {
List<SubscriptionInfo> infoList = mSubscriptionManager.getActiveSubscriptionInfoList();
if (infoList == null) return false;
- if (!Flags.enforceTelephonyFeatureMapping()) {
- return infoList.stream()
- .filter(s -> (s.getSubscriptionId()
- != SubscriptionManager.INVALID_SUBSCRIPTION_ID))
- .anyMatch(s -> isCallOngoingFromState(
- mTelephonyManager
- .createForSubscriptionId(s.getSubscriptionId())
- .getCallStateForSubscription()));
- } else {
- return infoList.stream()
- .filter(s -> (s.getSubscriptionId()
- != SubscriptionManager.INVALID_SUBSCRIPTION_ID))
- .anyMatch(s -> {
- try {
- return isCallOngoingFromState(mTelephonyManager
- .createForSubscriptionId(s.getSubscriptionId())
- .getCallStateForSubscription());
- } catch (UnsupportedOperationException e) {
- return false;
- }
- });
- }
+ return infoList.stream()
+ .filter(s -> (s.getSubscriptionId()
+ != SubscriptionManager.INVALID_SUBSCRIPTION_ID))
+ .anyMatch(s -> {
+ try {
+ return isCallOngoingFromState(mTelephonyManager
+ .createForSubscriptionId(s.getSubscriptionId())
+ .getCallStateForSubscription());
+ } catch (UnsupportedOperationException e) {
+ return false;
+ }
+ });
}
private void updateTelephonyListeners() {
diff --git a/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/transitions/flicker/LaunchTaskPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/transitions/flicker/LaunchTaskPortrait.kt
new file mode 100644
index 0000000..c82ce8a
--- /dev/null
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/transitions/flicker/LaunchTaskPortrait.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.service.transitions.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.server.wm.flicker.service.transitions.scenarios.LaunchTask
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class LaunchTaskPortrait : LaunchTask(Rotation.ROTATION_0) {
+ @ExpectedScenarios(["TASK_TRANSITION_SCENARIO", "OPEN_NEW_TASK_APP_SCENARIO"])
+ @Test
+ override fun openNewTask() = super.openNewTask()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig()
+ .use(FlickerServiceConfig.DEFAULT)
+ .use(TASK_TRANSITION_SCENARIO)
+ .use(OPEN_NEW_TASK_APP_SCENARIO)
+ }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/transitions/scenarios/LaunchTask.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/transitions/scenarios/LaunchTask.kt
new file mode 100644
index 0000000..147477d
--- /dev/null
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/transitions/scenarios/LaunchTask.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.service.transitions.scenarios
+
+import android.app.Instrumentation
+import android.tools.Rotation
+import android.tools.flicker.AssertionInvocationGroup
+import android.tools.flicker.assertors.assertions.AppWindowCoversFullScreenAtStart
+import android.tools.flicker.assertors.assertions.AppWindowOnTopAtEnd
+import android.tools.flicker.assertors.assertions.AppWindowOnTopAtStart
+import android.tools.flicker.assertors.assertions.BackgroundShowsInTransition
+import android.tools.flicker.assertors.assertions.LayerBecomesInvisible
+import android.tools.flicker.assertors.assertions.LayerBecomesVisible
+import android.tools.flicker.assertors.assertions.LayerIsNeverVisible
+import android.tools.flicker.assertors.assertions.AppWindowIsNeverVisible
+import android.tools.flicker.config.AssertionTemplates
+import android.tools.flicker.config.FlickerConfigEntry
+import android.tools.flicker.config.ScenarioId
+import android.tools.flicker.config.appclose.Components.CLOSING_APPS
+import android.tools.flicker.config.appclose.Components.CLOSING_CHANGES
+import android.tools.flicker.config.applaunch.Components.OPENING_CHANGES
+import android.tools.flicker.config.common.Components.LAUNCHER
+import android.tools.flicker.config.common.Components.WALLPAPER
+import android.tools.flicker.extractors.TaggedCujTransitionMatcher
+import android.tools.flicker.extractors.TaggedScenarioExtractorBuilder
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
+import android.tools.traces.events.CujType
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.NewTasksAppHelper
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+
+
+/**
+ * This tests performs a transition between tasks
+ */
+abstract class LaunchTask(val rotation: Rotation = Rotation.ROTATION_0) {
+ protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val tapl = LauncherInstrumentation()
+
+ private val launchNewTaskApp = NewTasksAppHelper(instrumentation)
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ ChangeDisplayOrientationRule.setRotation(rotation)
+ tapl.setExpectedRotation(rotation.value)
+ launchNewTaskApp.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun openNewTask() {
+ launchNewTaskApp.openNewTask(device, wmHelper)
+ }
+
+ @After
+ fun tearDown() {
+ launchNewTaskApp.exit(wmHelper)
+ }
+
+ companion object {
+ /**
+ * General task transition scenario that can be reused for any trace
+ */
+ val TASK_TRANSITION_SCENARIO =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("TASK_TRANSITION_SCENARIO"),
+ extractor = TaggedScenarioExtractorBuilder()
+ .setTargetTag(CujType.CUJ_DEFAULT_TASK_TO_TASK_ANIMATION)
+ .setTransitionMatcher(
+ TaggedCujTransitionMatcher(associatedTransitionRequired = true)
+ )
+ .build(),
+ assertions = listOf(
+ // Opening changes replace the closing ones
+ LayerBecomesInvisible(CLOSING_CHANGES),
+ AppWindowOnTopAtStart(CLOSING_CHANGES),
+ LayerBecomesVisible(OPENING_CHANGES),
+ AppWindowOnTopAtEnd(OPENING_CHANGES),
+
+ // There is a background color and it's covering the transition area
+ BackgroundShowsInTransition(CLOSING_CHANGES)
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING })
+ )
+
+ /**
+ * Scenario that is making assertions that are valid for the new task app but that do not
+ * apply to other task transitions in general
+ */
+ val OPEN_NEW_TASK_APP_SCENARIO =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("OPEN_NEW_TASK_APP_SCENARIO"),
+ extractor = TaggedScenarioExtractorBuilder()
+ .setTargetTag(CujType.CUJ_DEFAULT_TASK_TO_TASK_ANIMATION)
+ .setTransitionMatcher(
+ TaggedCujTransitionMatcher(associatedTransitionRequired = true)
+ )
+ .build(),
+ assertions = AssertionTemplates.COMMON_ASSERTIONS +
+ listOf(
+ // Wallpaper and launcher never visible
+ LayerIsNeverVisible(WALLPAPER, mustExist = true),
+ LayerIsNeverVisible(LAUNCHER, mustExist = true),
+ AppWindowIsNeverVisible(LAUNCHER, mustExist = true),
+ // App window covers the display at start
+ AppWindowCoversFullScreenAtStart(CLOSING_APPS)
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING })
+ )
+ }
+}
\ No newline at end of file
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index 1f0bd61..168141b 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -19,6 +19,9 @@
"src/**/*.kt",
],
asset_dirs: ["assets"],
+ kotlincflags: [
+ "-Werror",
+ ],
platform_apis: true,
certificate: "platform",
static_libs: [
diff --git a/tests/PackageWatchdog/Android.bp b/tests/PackageWatchdog/Android.bp
index 44e545b..16c6e3b 100644
--- a/tests/PackageWatchdog/Android.bp
+++ b/tests/PackageWatchdog/Android.bp
@@ -28,6 +28,7 @@
static_libs: [
"PlatformProperties",
"androidx.test.rules",
+ "androidx.test.runner",
"flag-junit",
"frameworks-base-testutils",
"junit",
diff --git a/tests/PackageWatchdog/AndroidManifest.xml b/tests/PackageWatchdog/AndroidManifest.xml
index 540edb4..334d50f 100644
--- a/tests/PackageWatchdog/AndroidManifest.xml
+++ b/tests/PackageWatchdog/AndroidManifest.xml
@@ -15,7 +15,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.tests.packagewatchdog" >
+ package="com.android.server" >
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
@@ -23,6 +23,6 @@
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.tests.packagewatchdog"
- android:label="PackageWatchdog Test"/>
+ android:targetPackage="com.android.server"
+ android:label="PackageWatchdog Test"/>
</manifest>
diff --git a/tests/PackageWatchdog/AndroidTest.xml b/tests/PackageWatchdog/AndroidTest.xml
new file mode 100644
index 0000000..45a88cd
--- /dev/null
+++ b/tests/PackageWatchdog/AndroidTest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<configuration description="Runs PackageWatchdog Tests.">
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="PackageWatchdogTest.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-tag" value="PackageWatchdogTest" />
+
+ <!-- Only run this tests in MTS if the Crashrecovery Mainline module is installed. -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <option name="mainline-module-package-name" value="com.google.android.crashrecovery" />
+ </object>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.server" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
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/link/FlaggedResources_test.cpp b/tools/aapt2/link/FlaggedResources_test.cpp
index adf711e..7bea96c 100644
--- a/tools/aapt2/link/FlaggedResources_test.cpp
+++ b/tools/aapt2/link/FlaggedResources_test.cpp
@@ -169,4 +169,18 @@
ASSERT_TRUE(output.contains("test.package.readWriteFlag"));
}
+TEST_F(FlaggedResourcesTest, ReadWriteFlagInPathFails) {
+ test::TestDiagnosticsImpl diag;
+ const std::string compiled_files_dir = GetTestPath("compiled");
+ ASSERT_FALSE(CompileFile(GetTestPath("res/values/flag(!test.package.rwFlag)/bools.xml"),
+ R"(<resources>
+ <bool name="bool1">false</bool>
+ </resources>)",
+ compiled_files_dir, &diag,
+ {"--feature-flags", "test.package.rwFlag=false"}));
+
+ ASSERT_TRUE(diag.GetLog().contains(
+ "Only read only flags may be used with resources: test.package.rwFlag"));
+}
+
} // namespace aapt