summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp1
-rw-r--r--OWNERS1
-rw-r--r--core/java/android/os/Parcel.java16
-rw-r--r--core/java/android/os/PerfettoTrace.java170
-rw-r--r--core/java/android/os/PerfettoTrackEventExtra.java481
-rw-r--r--core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java10
-rw-r--r--core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java18
-rw-r--r--core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java55
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java2
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/Operations.java12
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java144
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java96
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java67
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java6
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java9
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java11
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java21
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java11
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java17
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java6
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java9
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java10
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java5
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java5
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java20
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java14
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java5
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java5
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java175
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java175
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java95
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java32
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java100
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightInModifierOperation.java124
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java19
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthInModifierOperation.java124
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java19
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java126
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java38
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java39
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java42
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicSpline.java14
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/SpringStopEngine.java65
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java22
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java15
-rw-r--r--core/jni/android_os_PerfettoTrace.cpp49
-rw-r--r--core/jni/android_os_PerfettoTrackEventExtra.cpp54
-rw-r--r--core/tests/coretests/src/android/os/PerfettoTraceTest.java226
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt35
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt728
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt41
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt36
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt40
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt27
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt126
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt3
-rw-r--r--packages/SystemUI/Android.bp7
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt3
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt9
-rw-r--r--packages/SystemUI/log/src/com/android/systemui/log/LogBuffer.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModelTest.kt43
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModelTest.kt20
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt6
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/dagger/NotificationsLogModule.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java7
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AutoclickController.java18
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java9
-rw-r--r--services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java35
-rw-r--r--services/core/java/com/android/server/display/BrightnessTracker.java11
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java48
-rw-r--r--services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java46
-rw-r--r--services/core/java/com/android/server/wm/Task.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AutoclickControllerTest.java242
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskTests.java38
107 files changed, 3770 insertions, 912 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 458d1dfeadb8..2ce3221e56cf 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -102,6 +102,7 @@ aconfig_declarations_group {
"com.android.media.flags.projection-aconfig-java",
"com.android.net.http.flags-aconfig-exported-java",
"com.android.net.thread.platform.flags-aconfig-java",
+ "com.android.permission.flags-aconfig-java-export",
"com.android.ranging.flags.ranging-aconfig-java-export",
"com.android.server.contextualsearch.flags-java",
"com.android.server.flags.services-aconfig-java",
diff --git a/OWNERS b/OWNERS
index 058ea3619a58..aa93a275100a 100644
--- a/OWNERS
+++ b/OWNERS
@@ -16,6 +16,7 @@ omakoto@google.com #{LAST_RESORT_SUGGESTION}
roosa@google.com #{LAST_RESORT_SUGGESTION}
smoreland@google.com #{LAST_RESORT_SUGGESTION}
yamasani@google.com #{LAST_RESORT_SUGGESTION}
+timmurray@google.com #{LAST_RESORT_SUGGESTION}
# API changes are already covered by API-Review+1 (http://mdb/android-api-council)
# via https://android.git.corp.google.com/All-Projects/+/refs/meta/config/rules.pl.
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 4aa74621bd62..875b9098843f 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -2831,9 +2831,11 @@ public final class Parcel {
}
}
- private void resetSqaushingState() {
+ private void resetSquashingState() {
if (mAllowSquashing) {
- Slog.wtf(TAG, "allowSquashing wasn't restored.");
+ String error = "allowSquashing wasn't restored.";
+ Slog.wtf(TAG, error);
+ throw new BadParcelableException(error);
}
mWrittenSquashableParcelables = null;
mReadSquashableParcelables = null;
@@ -2950,9 +2952,11 @@ public final class Parcel {
for (int i = 0; i < mReadSquashableParcelables.size(); i++) {
sb.append(mReadSquashableParcelables.keyAt(i)).append(' ');
}
- Slog.wtfStack(TAG, "Map doesn't contain offset "
+ String error = "Map doesn't contain offset "
+ firstAbsolutePos
- + " : contains=" + sb.toString());
+ + " : contains=" + sb.toString();
+ Slog.wtfStack(TAG, error);
+ throw new BadParcelableException(error);
}
return (T) p;
}
@@ -5505,7 +5509,7 @@ public final class Parcel {
private void freeBuffer() {
mFlags = 0;
- resetSqaushingState();
+ resetSquashingState();
if (mOwnsNativeParcelObject) {
nativeFreeBuffer(mNativePtr);
}
@@ -5513,7 +5517,7 @@ public final class Parcel {
}
private void destroy() {
- resetSqaushingState();
+ resetSquashingState();
if (mNativePtr != 0) {
if (mOwnsNativeParcelObject) {
nativeDestroy(mNativePtr);
diff --git a/core/java/android/os/PerfettoTrace.java b/core/java/android/os/PerfettoTrace.java
index 164561acac32..e3f251e34b45 100644
--- a/core/java/android/os/PerfettoTrace.java
+++ b/core/java/android/os/PerfettoTrace.java
@@ -22,7 +22,6 @@ import dalvik.annotation.optimization.FastNative;
import libcore.util.NativeAllocationRegistry;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Consumer;
/**
* Writes trace events to the perfetto trace buffer. These trace events can be
@@ -72,7 +71,7 @@ public final class PerfettoTrace {
* @param name The category name.
*/
public Category(String name) {
- this(name, null, null);
+ this(name, "", "");
}
/**
@@ -82,7 +81,7 @@ public final class PerfettoTrace {
* @param tag An atrace tag name that this category maps to.
*/
public Category(String name, String tag) {
- this(name, tag, null);
+ this(name, tag, "");
}
/**
@@ -155,9 +154,6 @@ public final class PerfettoTrace {
}
}
- @FastNative
- private static native void native_event(int type, long tag, String name, long ptr);
-
@CriticalNative
private static native long native_get_process_track_uuid();
@@ -170,176 +166,98 @@ public final class PerfettoTrace {
/**
* Writes a trace message to indicate a given section of code was invoked.
*
- * @param category The perfetto category pointer.
+ * @param category The perfetto category.
* @param eventName The event name to appear in the trace.
- * @param extra The extra arguments.
*/
- public static void instant(Category category, String eventName, PerfettoTrackEventExtra extra) {
+ public static PerfettoTrackEventExtra.Builder instant(Category category, String eventName) {
if (!category.isEnabled()) {
- return;
+ return PerfettoTrackEventExtra.noOpBuilder();
}
- native_event(PERFETTO_TE_TYPE_INSTANT, category.getPtr(), eventName, extra.getPtr());
- extra.reset();
- }
-
- /**
- * Writes a trace message to indicate a given section of code was invoked.
- *
- * @param category The perfetto category.
- * @param eventName The event name to appear in the trace.
- * @param extraConfig Consumer for the extra arguments.
- */
- public static void instant(Category category, String eventName,
- Consumer<PerfettoTrackEventExtra.Builder> extraConfig) {
- PerfettoTrackEventExtra.Builder extra = PerfettoTrackEventExtra.builder();
- extraConfig.accept(extra);
- instant(category, eventName, extra.build());
- }
-
- /**
- * Writes a trace message to indicate a given section of code was invoked.
- *
- * @param category The perfetto category.
- * @param eventName The event name to appear in the trace.
- */
- public static void instant(Category category, String eventName) {
- instant(category, eventName, PerfettoTrackEventExtra.builder().build());
+ return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_INSTANT, category)
+ .setEventName(eventName);
}
/**
* Writes a trace message to indicate the start of a given section of code.
*
- * @param category The perfetto category pointer.
+ * @param category The perfetto category.
* @param eventName The event name to appear in the trace.
- * @param extra The extra arguments.
*/
- public static void begin(Category category, String eventName, PerfettoTrackEventExtra extra) {
+ public static PerfettoTrackEventExtra.Builder begin(Category category, String eventName) {
if (!category.isEnabled()) {
- return;
+ return PerfettoTrackEventExtra.noOpBuilder();
}
- native_event(PERFETTO_TE_TYPE_SLICE_BEGIN, category.getPtr(), eventName, extra.getPtr());
- extra.reset();
- }
-
- /**
- * Writes a trace message to indicate the start of a given section of code.
- *
- * @param category The perfetto category pointer.
- * @param eventName The event name to appear in the trace.
- * @param extraConfig Consumer for the extra arguments.
- */
- public static void begin(Category category, String eventName,
- Consumer<PerfettoTrackEventExtra.Builder> extraConfig) {
- PerfettoTrackEventExtra.Builder extra = PerfettoTrackEventExtra.builder();
- extraConfig.accept(extra);
- begin(category, eventName, extra.build());
- }
-
- /**
- * Writes a trace message to indicate the start of a given section of code.
- *
- * @param category The perfetto category pointer.
- * @param eventName The event name to appear in the trace.
- */
- public static void begin(Category category, String eventName) {
- begin(category, eventName, PerfettoTrackEventExtra.builder().build());
+ return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_SLICE_BEGIN, category)
+ .setEventName(eventName);
}
/**
* Writes a trace message to indicate the end of a given section of code.
*
- * @param category The perfetto category pointer.
- * @param extra The extra arguments.
+ * @param category The perfetto category.
*/
- public static void end(Category category, PerfettoTrackEventExtra extra) {
+ public static PerfettoTrackEventExtra.Builder end(Category category) {
if (!category.isEnabled()) {
- return;
+ return PerfettoTrackEventExtra.noOpBuilder();
}
- native_event(PERFETTO_TE_TYPE_SLICE_END, category.getPtr(), "", extra.getPtr());
- extra.reset();
- }
-
- /**
- * Writes a trace message to indicate the end of a given section of code.
- *
- * @param category The perfetto category pointer.
- * @param extraConfig Consumer for the extra arguments.
- */
- public static void end(Category category,
- Consumer<PerfettoTrackEventExtra.Builder> extraConfig) {
- PerfettoTrackEventExtra.Builder extra = PerfettoTrackEventExtra.builder();
- extraConfig.accept(extra);
- end(category, extra.build());
- }
-
- /**
- * Writes a trace message to indicate the end of a given section of code.
- *
- * @param category The perfetto category pointer.
- */
- public static void end(Category category) {
- end(category, PerfettoTrackEventExtra.builder().build());
+ return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_SLICE_END, category);
}
/**
* Writes a trace message to indicate the value of a given section of code.
*
- * @param category The perfetto category pointer.
- * @param extra The extra arguments.
+ * @param category The perfetto category.
+ * @param value The value of the counter.
*/
- public static void counter(Category category, PerfettoTrackEventExtra extra) {
+ public static PerfettoTrackEventExtra.Builder counter(Category category, long value) {
if (!category.isEnabled()) {
- return;
+ return PerfettoTrackEventExtra.noOpBuilder();
}
- native_event(PERFETTO_TE_TYPE_COUNTER, category.getPtr(), "", extra.getPtr());
- extra.reset();
+ return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_COUNTER, category)
+ .setCounter(value);
}
/**
* Writes a trace message to indicate the value of a given section of code.
*
- * @param category The perfetto category pointer.
- * @param extraConfig Consumer for the extra arguments.
+ * @param category The perfetto category.
+ * @param value The value of the counter.
+ * @param trackName The trackName for the event.
*/
- public static void counter(Category category,
- Consumer<PerfettoTrackEventExtra.Builder> extraConfig) {
- PerfettoTrackEventExtra.Builder extra = PerfettoTrackEventExtra.builder();
- extraConfig.accept(extra);
- counter(category, extra.build());
+ public static PerfettoTrackEventExtra.Builder counter(
+ Category category, long value, String trackName) {
+ return counter(category, value).usingProcessCounterTrack(trackName);
}
/**
* Writes a trace message to indicate the value of a given section of code.
*
- * @param category The perfetto category pointer.
- * @param trackName The trackName for the event.
+ * @param category The perfetto category.
* @param value The value of the counter.
*/
- public static void counter(Category category, String trackName, long value) {
- PerfettoTrackEventExtra extra = PerfettoTrackEventExtra.builder()
- .usingCounterTrack(trackName, PerfettoTrace.getProcessTrackUuid())
- .setCounter(value)
- .build();
- counter(category, extra);
+ 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);
}
/**
* Writes a trace message to indicate the value of a given section of code.
*
- * @param category The perfetto category pointer.
- * @param trackName The trackName for the event.
+ * @param category The perfetto category.
* @param value The value of the counter.
+ * @param trackName The trackName for the event.
*/
- public static void counter(Category category, String trackName, double value) {
- PerfettoTrackEventExtra extra = PerfettoTrackEventExtra.builder()
- .usingCounterTrack(trackName, PerfettoTrace.getProcessTrackUuid())
- .setCounter(value)
- .build();
- counter(category, extra);
+ public static PerfettoTrackEventExtra.Builder counter(
+ Category category, double value, String trackName) {
+ return counter(category, value).usingProcessCounterTrack(trackName);
}
/**
@@ -360,7 +278,7 @@ public final class PerfettoTrace {
* Returns the process track uuid that can be used as a parent track uuid.
*/
public static long getProcessTrackUuid() {
- if (IS_FLAG_ENABLED) {
+ if (!IS_FLAG_ENABLED) {
return 0;
}
return native_get_process_track_uuid();
@@ -370,7 +288,7 @@ public final class PerfettoTrace {
* Given a thread tid, returns the thread track uuid that can be used as a parent track uuid.
*/
public static long getThreadTrackUuid(long tid) {
- if (IS_FLAG_ENABLED) {
+ if (!IS_FLAG_ENABLED) {
return 0;
}
return native_get_thread_track_uuid(tid);
@@ -380,7 +298,7 @@ public final class PerfettoTrace {
* Activates a trigger by name {@code triggerName} with expiry in {@code ttlMs}.
*/
public static void activateTrigger(String triggerName, int ttlMs) {
- if (IS_FLAG_ENABLED) {
+ if (!IS_FLAG_ENABLED) {
return;
}
native_activate_trigger(triggerName, ttlMs);
diff --git a/core/java/android/os/PerfettoTrackEventExtra.java b/core/java/android/os/PerfettoTrackEventExtra.java
index a219b3b5678b..e034fb3726e3 100644
--- a/core/java/android/os/PerfettoTrackEventExtra.java
+++ b/core/java/android/os/PerfettoTrackEventExtra.java
@@ -31,6 +31,7 @@ import java.util.function.Supplier;
*/
public final class PerfettoTrackEventExtra {
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
@@ -40,7 +41,6 @@ public final class PerfettoTrackEventExtra {
};
private static final AtomicLong sNamedTrackId = new AtomicLong();
- private boolean mIsInUse;
private CounterInt64 mCounterInt64;
private CounterDouble mCounterDouble;
private Proto mProto;
@@ -123,15 +123,299 @@ public final class PerfettoTrackEventExtra {
}
}
+ 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.
+ *
+ * @param eventName can contain a format string specifier, in which case, the
+ * {@code args} are the format arguments. If no {@code args} are provided,
+ * the {@code eventName} should be the string itself.
+ * @param args format arguments if {@code eventName} is specified.
+ */
+ Builder setEventName(String eventName, Object... args);
+
+ /**
+ * 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}.
+ *
+ * @param val can contain a format string specifier, in which case, the
+ * {@code args} are the format arguments. If no {@code args} are provided,
+ * the {@code val} should be the string itself.
+ * @param args format arguments if {@code val} is specified.
+ */
+ Builder addArg(String name, String val, Object... args);
+
+ /**
+ * 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.
+ *
+ * @param name can contain a format string specifier, in which case, the
+ * {@code args} are the format arguments. If no {@code args} are provided,
+ * the {@code name} should be the string itself.
+ * @param args format arguments if {@code name} is specified.
+ */
+ Builder usingNamedTrack(long parentUuid, String name, Object... args);
+
+ /**
+ * Adds the events to a process scoped named track instead of the thread track where the
+ * event occurred.
+ *
+ * @param name can contain a format string specifier, in which case, the
+ * {@code args} are the format arguments. If no {@code args} are provided,
+ * the {@code name} should be the string itself.
+ * @param args format arguments if {@code name} is specified.
+ */
+ Builder usingProcessNamedTrack(String name, Object... args);
+
+ /**
+ * Adds the events to a thread scoped named track instead of the thread track where the
+ * event occurred.
+ *
+ * @param name can contain a format string specifier, in which case, the
+ * {@code args} are the format arguments. If no {@code args} are provided,
+ * the {@code name} should be the string itself.
+ * @param args format arguments if {@code name} is specified.
+ */
+ Builder usingThreadNamedTrack(long tid, String name, Object... args);
+
+ /**
+ * Adds the events to a counter track instead. This is required for
+ * setting counter values.
+ *
+ * @param name can contain a format string specifier, in which case, the
+ * {@code args} are the format arguments. If no {@code args} are provided,
+ * the {@code name} should be the string itself.
+ * @param args format arguments if {@code name} is specified.
+ */
+ Builder usingCounterTrack(long parentUuid, String name, Object... args);
+
+ /**
+ * Adds the events to a process scoped counter track instead. This is required for
+ * setting counter values.
+ *
+ * @param name can contain a format string specifier, in which case, the
+ * {@code args} are the format arguments. If no {@code args} are provided,
+ * the {@code name} should be the string itself.
+ * @param args format arguments if {@code eventName} is specified.
+ */
+ Builder usingProcessCounterTrack(String name, Object... args);
+
+ /**
+ * Adds the events to a thread scoped counter track instead. This is required for
+ * setting counter values.
+ *
+ * @param name can contain a format string specifier, in which case, the
+ * {@code args} are the format arguments. If no {@code args} are provided,
+ * the {@code name} should be the string itself.
+ * @param args format arguments if {@code name} is specified.
+ */
+ Builder usingThreadCounterTrack(long tid, String name, Object... args);
+
+ /**
+ * 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}.
+ *
+ * @param val can contain a format string specifier, in which case, the
+ * {@code args} are the format arguments. If no {@code args} are provided,
+ * the {@code val} should be the string itself.
+ * @param args format arguments if {@code val} is specified.
+ */
+ Builder addField(long id, String val, Object... args);
+
+ /**
+ * 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();
+ }
+
+ 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, Object... args) {
+ 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, Object... args) {
+ 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, Object... args) {
+ return this;
+ }
+ @Override
+ public Builder usingProcessNamedTrack(String name, Object... args) {
+ return this;
+ }
+ @Override
+ public Builder usingThreadNamedTrack(long tid, String name, Object... args) {
+ return this;
+ }
+ @Override
+ public Builder usingCounterTrack(long parentUuid, String name, Object... args) {
+ return this;
+ }
+ @Override
+ public Builder usingProcessCounterTrack(String name, Object... args) {
+ return this;
+ }
+ @Override
+ public Builder usingThreadCounterTrack(long tid, String name, Object... args) {
+ 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, Object... args) {
+ 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 Builder {
+ public static final class BuilderImpl implements 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.
private final PerfettoTrackEventExtra mExtra;
+
+ private int mTraceType;
+ private PerfettoTrace.Category mCategory;
+ private String mEventName;
private boolean mIsBuilt;
+
private Builder mParent;
private FieldContainer mCurrentContainer;
@@ -151,16 +435,10 @@ public final class PerfettoTrackEventExtra {
private final Pool<FieldString> mFieldStringCache;
private final Pool<FieldNested> mFieldNestedCache;
private final Pool<Flow> mFlowCache;
- private final Pool<Builder> mBuilderCache;
+ private final Pool<BuilderImpl> mBuilderCache;
- private Builder() {
- this(sTrackEventExtra.get(), null, null);
- }
-
- private Builder(PerfettoTrackEventExtra extra, Builder parent, FieldContainer container) {
- mExtra = extra;
- mParent = parent;
- mCurrentContainer = container;
+ private BuilderImpl() {
+ mExtra = sTrackEventExtra.get();
mNamedTrackCache = mExtra.mNamedTrackCache;
mCounterTrackCache = mExtra.mCounterTrackCache;
@@ -180,25 +458,39 @@ public final class PerfettoTrackEventExtra {
mProto = mExtra.getProto();
}
- /**
- * Builds the track event extra.
- */
- public PerfettoTrackEventExtra build() {
+ @Override
+ public void emit() {
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
+ public Builder init(int traceType, PerfettoTrace.Category category) {
+ mTraceType = traceType;
+ mCategory = category;
+ mEventName = "";
mFieldInt64Cache.reset();
mFieldDoubleCache.reset();
mFieldStringCache.reset();
mFieldNestedCache.reset();
mBuilderCache.reset();
- return mExtra;
+ mExtra.reset();
+ // Reset after on init in case the thread created builders without calling emit
+ return initInternal(this, null);
}
- /**
- * Adds a debug arg with key {@code name} and value {@code val}.
- */
+ @Override
+ public Builder setEventName(String eventName, Object... args) {
+ mEventName = toString(eventName, args);
+ return this;
+ }
+
+ @Override
public Builder addArg(String name, long val) {
checkParent();
ArgInt64 arg = mArgInt64Cache.get(name.hashCode());
@@ -211,9 +503,7 @@ public final class PerfettoTrackEventExtra {
return this;
}
- /**
- * Adds a debug arg with key {@code name} and value {@code val}.
- */
+ @Override
public Builder addArg(String name, boolean val) {
checkParent();
ArgBool arg = mArgBoolCache.get(name.hashCode());
@@ -226,9 +516,7 @@ public final class PerfettoTrackEventExtra {
return this;
}
- /**
- * Adds a debug arg with key {@code name} and value {@code val}.
- */
+ @Override
public Builder addArg(String name, double val) {
checkParent();
ArgDouble arg = mArgDoubleCache.get(name.hashCode());
@@ -241,24 +529,20 @@ public final class PerfettoTrackEventExtra {
return this;
}
- /**
- * Adds a debug arg with key {@code name} and value {@code val}.
- */
- public Builder addArg(String name, String val) {
+ @Override
+ public Builder addArg(String name, String val, Object... args) {
checkParent();
ArgString arg = mArgStringCache.get(name.hashCode());
if (arg == null || !arg.getName().equals(name)) {
arg = new ArgString(name);
mArgStringCache.put(name.hashCode(), arg);
}
- arg.setValue(val);
+ arg.setValue(toString(val, args));
mExtra.addPerfettoPointer(arg);
return this;
}
- /**
- * Adds a flow with {@code id}.
- */
+ @Override
public Builder addFlow(int id) {
checkParent();
Flow flow = mFlowCache.get(Flow::new);
@@ -267,9 +551,7 @@ public final class PerfettoTrackEventExtra {
return this;
}
- /**
- * Adds a terminating flow with {@code id}.
- */
+ @Override
public Builder addTerminatingFlow(int id) {
checkParent();
Flow flow = mFlowCache.get(Flow::new);
@@ -278,12 +560,11 @@ public final class PerfettoTrackEventExtra {
return this;
}
- /**
- * Adds the events to a named track instead of the thread track where the
- * event occurred.
- */
- public Builder usingNamedTrack(String name, long parentUuid) {
+ @Override
+ public Builder usingNamedTrack(long parentUuid, String name, Object... args) {
checkParent();
+ name = toString(name, args);
+
NamedTrack track = mNamedTrackCache.get(name.hashCode());
if (track == null || !track.getName().equals(name)) {
track = new NamedTrack(name, parentUuid);
@@ -293,13 +574,21 @@ public final class PerfettoTrackEventExtra {
return this;
}
- /**
- * Adds the events to a counter track instead. This is required for
- * setting counter values.
- *
- */
- public Builder usingCounterTrack(String name, long parentUuid) {
+ @Override
+ public Builder usingProcessNamedTrack(String name, Object... args) {
+ return usingNamedTrack(PerfettoTrace.getProcessTrackUuid(), name, args);
+ }
+
+ @Override
+ public Builder usingThreadNamedTrack(long tid, String name, Object... args) {
+ return usingNamedTrack(PerfettoTrace.getThreadTrackUuid(tid), name, args);
+ }
+
+ @Override
+ public Builder usingCounterTrack(long parentUuid, String name, Object... args) {
checkParent();
+ name = toString(name, args);
+
CounterTrack track = mCounterTrackCache.get(name.hashCode());
if (track == null || !track.getName().equals(name)) {
track = new CounterTrack(name, parentUuid);
@@ -309,10 +598,17 @@ public final class PerfettoTrackEventExtra {
return this;
}
- /**
- * Sets a long counter value on the event.
- *
- */
+ @Override
+ public Builder usingProcessCounterTrack(String name, Object... args) {
+ return usingCounterTrack(PerfettoTrace.getProcessTrackUuid(), name, args);
+ }
+
+ @Override
+ public Builder usingThreadCounterTrack(long tid, String name, Object... args) {
+ return usingCounterTrack(PerfettoTrace.getThreadTrackUuid(tid), name, args);
+ }
+
+ @Override
public Builder setCounter(long val) {
checkParent();
mCounterInt64.setValue(val);
@@ -320,10 +616,7 @@ public final class PerfettoTrackEventExtra {
return this;
}
- /**
- * Sets a double counter value on the event.
- *
- */
+ @Override
public Builder setCounter(double val) {
checkParent();
mCounterDouble.setValue(val);
@@ -331,9 +624,7 @@ public final class PerfettoTrackEventExtra {
return this;
}
- /**
- * Adds a proto field with field id {@code id} and value {@code val}.
- */
+ @Override
public Builder addField(long id, long val) {
checkContainer();
FieldInt64 field = mFieldInt64Cache.get(FieldInt64::new);
@@ -342,9 +633,7 @@ public final class PerfettoTrackEventExtra {
return this;
}
- /**
- * Adds a proto field with field id {@code id} and value {@code val}.
- */
+ @Override
public Builder addField(long id, double val) {
checkContainer();
FieldDouble field = mFieldDoubleCache.get(FieldDouble::new);
@@ -353,35 +642,24 @@ public final class PerfettoTrackEventExtra {
return this;
}
- /**
- * Adds a proto field with field id {@code id} and value {@code val}.
- */
- public Builder addField(long id, String val) {
+ @Override
+ public Builder addField(long id, String val, Object... args) {
checkContainer();
FieldString field = mFieldStringCache.get(FieldString::new);
- field.setValue(id, val);
+ field.setValue(id, toString(val, args));
mCurrentContainer.addField(field);
return this;
}
- /**
- * 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}.
- */
+ @Override
public Builder beginProto() {
checkParent();
mProto.clearFields();
mExtra.addPerfettoPointer(mProto);
- return mBuilderCache.get(Builder::new).init(this, mProto);
+ return mBuilderCache.get(BuilderImpl::new).initInternal(this, mProto);
}
- /**
- * Ends a proto field.
- */
+ @Override
public Builder endProto() {
if (mParent == null || mCurrentContainer == null) {
throw new IllegalStateException("No proto to end");
@@ -389,22 +667,16 @@ public final class PerfettoTrackEventExtra {
return mParent;
}
- /**
- * 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}.
- */
+ @Override
public Builder beginNested(long id) {
checkContainer();
FieldNested field = mFieldNestedCache.get(FieldNested::new);
field.setId(id);
mCurrentContainer.addField(field);
- return mBuilderCache.get(Builder::new).init(this, field);
+ return mBuilderCache.get(BuilderImpl::new).initInternal(this, field);
}
- /**
- * Ends a nested proto field.
- */
+ @Override
public Builder endNested() {
if (mParent == null || mCurrentContainer == null) {
throw new IllegalStateException("No nested field to end");
@@ -412,21 +684,15 @@ public final class PerfettoTrackEventExtra {
return mParent;
}
- /**
- * Initializes a {@link Builder}.
- */
- public Builder init(Builder parent, FieldContainer container) {
+ private static String toString(String val, Object... args) {
+ return args == null || args.length == 0 ? val : String.format(val, args);
+ }
+
+ private Builder initInternal(Builder parent, FieldContainer container) {
mParent = parent;
mCurrentContainer = container;
mIsBuilt = false;
- if (mParent == null) {
- if (mExtra.mIsInUse) {
- throw new IllegalStateException("Cannot create a new builder when another"
- + " extra is in use");
- }
- mExtra.mIsInUse = true;
- }
return this;
}
@@ -439,9 +705,8 @@ public final class PerfettoTrackEventExtra {
private void checkParent() {
checkState();
- if (mParent != null) {
- throw new IllegalStateException(
- "This builder has already been used. Create a new builder for another event.");
+ if (!this.equals(mParent)) {
+ throw new IllegalStateException("Operation not supported for proto");
}
}
@@ -458,7 +723,14 @@ public final class PerfettoTrackEventExtra {
* Start a {@link Builder} to build a {@link PerfettoTrackEventExtra}.
*/
public static Builder builder() {
- return sTrackEventExtra.get().mBuilderCache.get(Builder::new).init(null, null);
+ return sTrackEventExtra.get().mBuilderCache.get(BuilderImpl::new).initInternal(null, null);
+ }
+
+ /**
+ * Returns a no-op {@link Builder}. Useful if a category is disabled.
+ */
+ public static Builder noOpBuilder() {
+ return NO_OP_BUILDER;
}
private final RingBuffer<NamedTrack> mNamedTrackCache =
@@ -476,7 +748,7 @@ public final class PerfettoTrackEventExtra {
private final Pool<FieldString> mFieldStringCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
private final Pool<FieldNested> mFieldNestedCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
private final Pool<Flow> mFlowCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
- private final Pool<Builder> mBuilderCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
+ private final Pool<BuilderImpl> mBuilderCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
private static final NativeAllocationRegistry sRegistry =
NativeAllocationRegistry.createMalloced(
@@ -509,7 +781,6 @@ public final class PerfettoTrackEventExtra {
*/
public void reset() {
native_clear_args(mPtr);
- mIsInUse = false;
}
private CounterInt64 getCounterInt64() {
@@ -1078,4 +1349,6 @@ public final class PerfettoTrackEventExtra {
private static native void native_add_arg(long ptr, long extraPtr);
@CriticalNative
private static native void native_clear_args(long ptr);
+ @FastNative
+ private static native void native_emit(int type, long tag, String name, long ptr);
}
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
index b5251db4e539..051885e10132 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
@@ -25,6 +25,7 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
import java.io.Closeable;
import java.util.concurrent.Executor;
@@ -232,6 +233,15 @@ public interface QuickAccessWalletClient extends Closeable {
Drawable getTileIcon();
/**
+ * Returns the user that should receive the wallet intents
+ *
+ * @return UserHandle
+ * @hide
+ */
+ @Nullable
+ UserHandle getUser();
+
+ /**
* Returns the service label specified by {@code android:label} in the service manifest entry.
*
* @hide
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
index 97a4beff633f..177164296eea 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
@@ -243,8 +243,9 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
return null;
}
String packageName = mServiceInfo.getComponentName().getPackageName();
+ int userId = mServiceInfo.getUserId();
String walletActivity = mServiceInfo.getWalletActivity();
- return createIntent(walletActivity, packageName, ACTION_VIEW_WALLET);
+ return createIntent(walletActivity, packageName, userId, ACTION_VIEW_WALLET);
}
@Override
@@ -302,12 +303,15 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
}
String packageName = mServiceInfo.getComponentName().getPackageName();
String settingsActivity = mServiceInfo.getSettingsActivity();
- return createIntent(settingsActivity, packageName, ACTION_VIEW_WALLET_SETTINGS);
+ return createIntent(settingsActivity, packageName, UserHandle.myUserId(),
+ ACTION_VIEW_WALLET_SETTINGS);
}
@Nullable
- private Intent createIntent(@Nullable String activityName, String packageName, String action) {
- PackageManager pm = mContext.getPackageManager();
+ private Intent createIntent(@Nullable String activityName, String packageName,
+ int userId, String action) {
+ Context userContext = mContext.createContextAsUser(UserHandle.of(userId), 0);
+ PackageManager pm = userContext.getPackageManager();
if (TextUtils.isEmpty(activityName)) {
activityName = queryActivityForAction(pm, packageName, action);
}
@@ -361,6 +365,12 @@ public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Ser
return mServiceInfo == null ? null : mServiceInfo.getTileIcon();
}
+ @Nullable
+ @Override
+ public UserHandle getUser() {
+ return mServiceInfo == null ? null : UserHandle.of(mServiceInfo.getUserId());
+ }
+
@Override
@Nullable
public CharSequence getServiceLabel() {
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
index 8a3f6ceb852b..01de54354a04 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
@@ -16,6 +16,10 @@
package android.service.quickaccesswallet;
+import static android.permission.flags.Flags.walletRoleCrossUserEnabled;
+
+import static com.android.permission.flags.Flags.crossUserRoleEnabled;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -32,10 +36,12 @@ import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.os.Binder;
+import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.Pair;
import android.util.Xml;
import com.android.internal.R;
@@ -59,22 +65,29 @@ class QuickAccessWalletServiceInfo {
private final ServiceInfo mServiceInfo;
private final ServiceMetadata mServiceMetadata;
private final TileServiceMetadata mTileServiceMetadata;
+ private final int mUserId;
private QuickAccessWalletServiceInfo(
@NonNull ServiceInfo serviceInfo,
@NonNull ServiceMetadata metadata,
- @NonNull TileServiceMetadata tileServiceMetadata) {
+ @NonNull TileServiceMetadata tileServiceMetadata,
+ int userId) {
mServiceInfo = serviceInfo;
mServiceMetadata = metadata;
mTileServiceMetadata = tileServiceMetadata;
+ mUserId = userId;
}
@Nullable
static QuickAccessWalletServiceInfo tryCreate(@NonNull Context context) {
String defaultAppPackageName = null;
+ int defaultAppUser = UserHandle.myUserId();
+
if (isWalletRoleAvailable(context)) {
- defaultAppPackageName = getDefaultWalletApp(context);
+ Pair<String, Integer> roleAndUser = getDefaultWalletApp(context);
+ defaultAppPackageName = roleAndUser.first;
+ defaultAppUser = roleAndUser.second;
} else {
ComponentName defaultPaymentApp = getDefaultPaymentApp(context);
if (defaultPaymentApp == null) {
@@ -83,7 +96,8 @@ class QuickAccessWalletServiceInfo {
defaultAppPackageName = defaultPaymentApp.getPackageName();
}
- ServiceInfo serviceInfo = getWalletServiceInfo(context, defaultAppPackageName);
+ ServiceInfo serviceInfo = getWalletServiceInfo(context, defaultAppPackageName,
+ defaultAppUser);
if (serviceInfo == null) {
return null;
}
@@ -98,15 +112,32 @@ class QuickAccessWalletServiceInfo {
ServiceMetadata metadata = parseServiceMetadata(context, serviceInfo);
TileServiceMetadata tileServiceMetadata =
new TileServiceMetadata(parseTileServiceMetadata(context, serviceInfo));
- return new QuickAccessWalletServiceInfo(serviceInfo, metadata, tileServiceMetadata);
+ return new QuickAccessWalletServiceInfo(serviceInfo, metadata, tileServiceMetadata,
+ defaultAppUser);
}
- private static String getDefaultWalletApp(Context context) {
+ @NonNull
+ private static Pair<String, Integer> getDefaultWalletApp(Context context) {
+ UserHandle user = UserHandle.of(UserHandle.myUserId());
+
final long token = Binder.clearCallingIdentity();
try {
RoleManager roleManager = context.getSystemService(RoleManager.class);
- List<String> roleHolders = roleManager.getRoleHolders(RoleManager.ROLE_WALLET);
- return roleHolders.isEmpty() ? null : roleHolders.get(0);
+
+ if (walletRoleCrossUserEnabled()
+ && crossUserRoleEnabled()
+ && context.checkCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ == PackageManager.PERMISSION_GRANTED) {
+ user = roleManager.getActiveUserForRole(RoleManager.ROLE_WALLET);
+ if (user == null) {
+ return new Pair<>(null, user.getIdentifier());
+ }
+ }
+ List<String> roleHolders = roleManager.getRoleHoldersAsUser(RoleManager.ROLE_WALLET,
+ user);
+ return new Pair<>(roleHolders.isEmpty() ? null : roleHolders.get(0),
+ user.getIdentifier());
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -128,15 +159,16 @@ class QuickAccessWalletServiceInfo {
return comp == null ? null : ComponentName.unflattenFromString(comp);
}
- private static ServiceInfo getWalletServiceInfo(Context context, String packageName) {
+ private static ServiceInfo getWalletServiceInfo(Context context, String packageName,
+ int userId) {
Intent intent = new Intent(QuickAccessWalletService.SERVICE_INTERFACE);
intent.setPackage(packageName);
List<ResolveInfo> resolveInfos =
- context.getPackageManager().queryIntentServices(intent,
+ context.getPackageManager().queryIntentServicesAsUser(intent,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
| PackageManager.MATCH_DEFAULT_ONLY
- | PackageManager.GET_META_DATA);
+ | PackageManager.GET_META_DATA, userId);
return resolveInfos.isEmpty() ? null : resolveInfos.get(0).serviceInfo;
}
@@ -247,6 +279,9 @@ class QuickAccessWalletServiceInfo {
return mServiceInfo.getComponentName();
}
+ int getUserId() {
+ return mUserId;
+ }
/**
* @return the fully qualified name of the activity that hosts the full wallet. If available,
* this intent should be started with the action
diff --git a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
index 26b0d11955d2..f5f4e4332d28 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
@@ -62,7 +62,7 @@ public class CoreDocument {
// We also keep a more fine-grained BUILD number, exposed as
// ID_API_LEVEL = DOCUMENT_API_LEVEL + BUILD
- static final float BUILD = 0.2f;
+ static final float BUILD = 0.3f;
@NonNull ArrayList<Operation> mOperations = new ArrayList<>();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operations.java b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
index 9a37a22390a2..0b6a3c415e4a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
@@ -88,6 +88,8 @@ import com.android.internal.widget.remotecompose.core.operations.layout.TouchUpM
import com.android.internal.widget.remotecompose.core.operations.layout.animation.AnimationSpec;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.BoxLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.CanvasLayout;
+import com.android.internal.widget.remotecompose.core.operations.layout.managers.CollapsibleColumnLayout;
+import com.android.internal.widget.remotecompose.core.operations.layout.managers.CollapsibleRowLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.ColumnLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.RowLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.StateLayout;
@@ -97,6 +99,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.modifier
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ClipRectModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentVisibilityOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.GraphicsLayerModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightInModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HostActionOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HostNamedActionOperation;
@@ -111,6 +114,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.modifier
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ValueIntegerChangeActionOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ValueIntegerExpressionChangeActionOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ValueStringChangeActionOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthInModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ZIndexModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap;
@@ -208,7 +212,9 @@ public class Operations {
public static final int LAYOUT_CONTENT = 201;
public static final int LAYOUT_BOX = 202;
public static final int LAYOUT_ROW = 203;
+ public static final int LAYOUT_COLLAPSIBLE_ROW = 230;
public static final int LAYOUT_COLUMN = 204;
+ public static final int LAYOUT_COLLAPSIBLE_COLUMN = 233;
public static final int LAYOUT_CANVAS = 205;
public static final int LAYOUT_CANVAS_CONTENT = 207;
public static final int LAYOUT_TEXT = 208;
@@ -218,6 +224,8 @@ public class Operations {
public static final int MODIFIER_WIDTH = 16;
public static final int MODIFIER_HEIGHT = 67;
+ public static final int MODIFIER_WIDTH_IN = 231;
+ public static final int MODIFIER_HEIGHT_IN = 232;
public static final int MODIFIER_BACKGROUND = 55;
public static final int MODIFIER_BORDER = 107;
public static final int MODIFIER_PADDING = 58;
@@ -324,6 +332,8 @@ public class Operations {
map.put(MODIFIER_WIDTH, WidthModifierOperation::read);
map.put(MODIFIER_HEIGHT, HeightModifierOperation::read);
+ map.put(MODIFIER_WIDTH_IN, WidthInModifierOperation::read);
+ map.put(MODIFIER_HEIGHT_IN, HeightInModifierOperation::read);
map.put(MODIFIER_PADDING, PaddingModifierOperation::read);
map.put(MODIFIER_BACKGROUND, BackgroundModifierOperation::read);
map.put(MODIFIER_BORDER, BorderModifierOperation::read);
@@ -359,7 +369,9 @@ public class Operations {
map.put(LAYOUT_CONTENT, LayoutComponentContent::read);
map.put(LAYOUT_BOX, BoxLayout::read);
map.put(LAYOUT_COLUMN, ColumnLayout::read);
+ map.put(LAYOUT_COLLAPSIBLE_COLUMN, CollapsibleColumnLayout::read);
map.put(LAYOUT_ROW, RowLayout::read);
+ map.put(LAYOUT_COLLAPSIBLE_ROW, CollapsibleRowLayout::read);
map.put(LAYOUT_CANVAS, CanvasLayout::read);
map.put(LAYOUT_CANVAS_CONTENT, CanvasContent::read);
map.put(LAYOUT_TEXT, TextLayout::read);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
index 39cc997fadc2..1cb8fefde80c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
@@ -86,6 +86,8 @@ import com.android.internal.widget.remotecompose.core.operations.layout.LoopOper
import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.BoxLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.CanvasLayout;
+import com.android.internal.widget.remotecompose.core.operations.layout.managers.CollapsibleColumnLayout;
+import com.android.internal.widget.remotecompose.core.operations.layout.managers.CollapsibleRowLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.ColumnLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.RowLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.StateLayout;
@@ -691,6 +693,12 @@ public class RemoteComposeBuffer {
return out;
}
+ /**
+ * Append a path to an existing path
+ *
+ * @param id id of the path to append to
+ * @param path the path to append
+ */
public void pathAppend(int id, float... path) {
PathAppend.apply(mBuffer, id, path);
}
@@ -772,8 +780,8 @@ public class RemoteComposeBuffer {
* @param text The text to be drawn
* @param start The index of the first character in text to draw
* @param end (end - 1) is the index of the last character in text to draw
- * @param contextStart
- * @param contextEnd
+ * @param contextStart the context start
+ * @param contextEnd the context end
* @param x The x-coordinate of the origin of the text being drawn
* @param y The y-coordinate of the baseline of the text being drawn
* @param rtl Draw RTTL
@@ -798,8 +806,8 @@ public class RemoteComposeBuffer {
* @param textId The text to be drawn
* @param start The index of the first character in text to draw
* @param end (end - 1) is the index of the last character in text to draw
- * @param contextStart
- * @param contextEnd
+ * @param contextStart the context start
+ * @param contextEnd the context end
* @param x The x-coordinate of the origin of the text being drawn
* @param y The y-coordinate of the baseline of the text being drawn
* @param rtl Draw RTTL
@@ -986,6 +994,11 @@ public class RemoteComposeBuffer {
///////////////////////////////////////////////////////////////////////////////////////////////
+ /**
+ * inflate the buffer into a list of operations
+ *
+ * @param operations the operations list to add to
+ */
public void inflateFromBuffer(@NonNull ArrayList<Operation> operations) {
mBuffer.setIndex(0);
while (mBuffer.available()) {
@@ -1001,6 +1014,12 @@ public class RemoteComposeBuffer {
}
}
+ /**
+ * Read the next operation from the buffer
+ *
+ * @param buffer The buff to read
+ * @param operations the operations list to add to
+ */
public static void readNextOperation(
@NonNull WireBuffer buffer, @NonNull ArrayList<Operation> operations) {
int opId = buffer.readByte();
@@ -1014,6 +1033,11 @@ public class RemoteComposeBuffer {
operation.read(buffer, operations);
}
+ /**
+ * copy the current buffer to a new one
+ *
+ * @return A new RemoteComposeBuffer
+ */
@NonNull
RemoteComposeBuffer copy() {
ArrayList<Operation> operations = new ArrayList<>();
@@ -1022,6 +1046,11 @@ public class RemoteComposeBuffer {
return copyFromOperations(operations, buffer);
}
+ /**
+ * add a set theme
+ *
+ * @param theme The theme to set
+ */
public void setTheme(int theme) {
Theme.apply(mBuffer, theme);
}
@@ -1040,6 +1069,14 @@ public class RemoteComposeBuffer {
return buffer;
}
+ /**
+ * Create a RemoteComposeBuffer from a file
+ *
+ * @param file A file
+ * @param remoteComposeState The RemoteComposeState
+ * @return A RemoteComposeBuffer
+ * @throws IOException if the file cannot be read
+ */
@NonNull
public RemoteComposeBuffer fromFile(
@NonNull File file, @NonNull RemoteComposeState remoteComposeState) throws IOException {
@@ -1048,6 +1085,13 @@ public class RemoteComposeBuffer {
return buffer;
}
+ /**
+ * Create a RemoteComposeBuffer from an InputStream
+ *
+ * @param inputStream An InputStream
+ * @param remoteComposeState The RemoteComposeState
+ * @return A RemoteComposeBuffer
+ */
@NonNull
public static RemoteComposeBuffer fromInputStream(
@NonNull InputStream inputStream, @NonNull RemoteComposeState remoteComposeState) {
@@ -1056,6 +1100,13 @@ public class RemoteComposeBuffer {
return buffer;
}
+ /**
+ * Create a RemoteComposeBuffer from an array of operations
+ *
+ * @param operations An array of operations
+ * @param buffer A RemoteComposeBuffer
+ * @return A RemoteComposeBuffer
+ */
@NonNull
RemoteComposeBuffer copyFromOperations(
@NonNull ArrayList<Operation> operations, @NonNull RemoteComposeBuffer buffer) {
@@ -1834,12 +1885,12 @@ public class RemoteComposeBuffer {
/**
* Add a marquee modifier
*
- * @param iterations
- * @param animationMode
- * @param repeatDelayMillis
- * @param initialDelayMillis
- * @param spacing
- * @param velocity
+ * @param iterations number of iterations
+ * @param animationMode animation mode
+ * @param repeatDelayMillis repeat delay
+ * @param initialDelayMillis initial delay
+ * @param spacing spacing between items
+ * @param velocity velocity of the marquee
*/
public void addModifierMarquee(
int iterations,
@@ -1861,14 +1912,21 @@ public class RemoteComposeBuffer {
/**
* Add a graphics layer
*
- * @param scaleX
- * @param scaleY
- * @param rotationX
- * @param rotationY
- * @param rotationZ
- * @param shadowElevation
- * @param transformOriginX
- * @param transformOriginY
+ * @param scaleX scale x
+ * @param scaleY scale y
+ * @param rotationX rotation in X
+ * @param rotationY rotation in Y
+ * @param rotationZ rotation in Z
+ * @param shadowElevation shadow elevation
+ * @param transformOriginX transform origin x
+ * @param transformOriginY transform origin y
+ * @param alpha alpha value
+ * @param cameraDistance camera distance
+ * @param blendMode blend mode
+ * @param spotShadowColorId spot shadow color
+ * @param ambientShadowColorId ambient shadow color
+ * @param colorFilterId id of color filter
+ * @param renderEffectId id of render effect
*/
public void addModifierGraphicsLayer(
float scaleX,
@@ -1923,14 +1981,32 @@ public class RemoteComposeBuffer {
ClipRectModifierOperation.apply(mBuffer);
}
+ /**
+ * add start of loop
+ *
+ * @param indexId id of the variable
+ * @param from start value
+ * @param step step value
+ * @param until stop value
+ */
public void addLoopStart(int indexId, float from, float step, float until) {
LoopOperation.apply(mBuffer, indexId, from, step, until);
}
+ /** Add a loop end */
public void addLoopEnd() {
ContainerEnd.apply(mBuffer);
}
+ /**
+ * add a state layout
+ *
+ * @param componentId id of the state
+ * @param animationId animation id
+ * @param horizontal horizontal alignment
+ * @param vertical vertical alignment
+ * @param indexId index of the state
+ */
public void addStateLayout(
int componentId, int animationId, int horizontal, int vertical, int indexId) {
mLastComponentId = getComponentId(componentId);
@@ -1966,6 +2042,22 @@ public class RemoteComposeBuffer {
}
/**
+ * Add a row start tag
+ *
+ * @param componentId component id
+ * @param animationId animation id
+ * @param horizontal horizontal alignment
+ * @param vertical vertical alignment
+ * @param spacedBy spacing between items
+ */
+ public void addCollapsibleRowStart(
+ int componentId, int animationId, int horizontal, int vertical, float spacedBy) {
+ mLastComponentId = getComponentId(componentId);
+ CollapsibleRowLayout.apply(
+ mBuffer, mLastComponentId, animationId, horizontal, vertical, spacedBy);
+ }
+
+ /**
* Add a column start tag
*
* @param componentId component id
@@ -1981,6 +2073,22 @@ public class RemoteComposeBuffer {
}
/**
+ * Add a column start tag
+ *
+ * @param componentId component id
+ * @param animationId animation id
+ * @param horizontal horizontal alignment
+ * @param vertical vertical alignment
+ * @param spacedBy spacing between items
+ */
+ public void addCollapsibleColumnStart(
+ int componentId, int animationId, int horizontal, int vertical, float spacedBy) {
+ mLastComponentId = getComponentId(componentId);
+ CollapsibleColumnLayout.apply(
+ mBuffer, mLastComponentId, animationId, horizontal, vertical, spacedBy);
+ }
+
+ /**
* Add a canvas start tag
*
* @param componentId component id
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
index 43f8ea7dc78f..363b82bdf70c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
@@ -67,8 +67,8 @@ public class RemoteComposeState implements CollectionsAccess {
* Get Object based on id. The system will cache things like bitmaps Paths etc. They can be
* accessed with this command
*
- * @param id
- * @return
+ * @param id the id of the object
+ * @return the object
*/
@Nullable
public Object getFromId(int id) {
@@ -78,8 +78,8 @@ public class RemoteComposeState implements CollectionsAccess {
/**
* true if the cache contain this id
*
- * @param id
- * @return
+ * @param id the id of the object
+ * @return true if the cache contain this id
*/
public boolean containsId(int id) {
return mIntDataMap.get(id) != null;
@@ -138,8 +138,8 @@ public class RemoteComposeState implements CollectionsAccess {
/**
* Get the path asociated with the Data
*
- * @param id
- * @return
+ * @param id of path
+ * @return path object
*/
public Object getPath(int id) {
return mPathMap.get(id);
@@ -180,7 +180,7 @@ public class RemoteComposeState implements CollectionsAccess {
/**
* Adds a data Override.
*
- * @param id
+ * @param id the id of the data
* @param item the new value
*/
public void overrideData(int id, @NonNull Object item) {
@@ -222,8 +222,8 @@ public class RemoteComposeState implements CollectionsAccess {
/**
* Adds a float Override.
*
- * @param id
- * @param value the new value
+ * @param id The id of the float
+ * @param value the override value
*/
public void overrideFloat(int id, float value) {
float previous = mFloatMap.get(id);
@@ -235,7 +235,12 @@ public class RemoteComposeState implements CollectionsAccess {
}
}
- /** Insert an item in the cache */
+ /**
+ * Insert an item in the cache
+ *
+ * @param item integer item to cache
+ * @return the id of the integer
+ */
public int cacheInteger(int item) {
int id = nextId();
mIntegerMap.put(id, item);
@@ -243,7 +248,12 @@ public class RemoteComposeState implements CollectionsAccess {
return id;
}
- /** Insert an integer item in the cache */
+ /**
+ * Insert an integer item in the cache
+ *
+ * @param id the id of the integer
+ * @param value the value of the integer
+ */
public void updateInteger(int id, int value) {
if (!mIntegerOverride[id]) {
int previous = mIntegerMap.get(id);
@@ -292,10 +302,10 @@ public class RemoteComposeState implements CollectionsAccess {
}
/**
- * Get the float value
+ * Get the color from the cache
*
- * @param id
- * @return
+ * @param id The id of the color
+ * @return The color
*/
public int getColor(int id) {
return mColorMap.get(id);
@@ -377,6 +387,9 @@ public class RemoteComposeState implements CollectionsAccess {
/**
* Method to determine if a cached value has been written to the documents WireBuffer based on
* its id.
+ *
+ * @param id id to check
+ * @return true if the value has not been written to the WireBuffer
*/
public boolean wasNotWritten(int id) {
return !mIntWrittenMap.get(id);
@@ -406,7 +419,7 @@ public class RemoteComposeState implements CollectionsAccess {
* Get the next available id 0 is normal (float,int,String,color) 1 is VARIABLES 2 is
* collections
*
- * @return
+ * @return return a unique id in the set
*/
public int nextId(int type) {
if (0 == type) {
@@ -418,7 +431,7 @@ public class RemoteComposeState implements CollectionsAccess {
/**
* Set the next id
*
- * @param id
+ * @param id set the id to increment off of
*/
public void setNextId(int id) {
mNextId = id;
@@ -440,8 +453,8 @@ public class RemoteComposeState implements CollectionsAccess {
/**
* Commands that listen to variables add themselves.
*
- * @param id
- * @param variableSupport
+ * @param id id of variable to listen to
+ * @param variableSupport command that listens to variable
*/
public void listenToVar(int id, @NonNull VariableSupport variableSupport) {
add(id, variableSupport);
@@ -450,8 +463,8 @@ public class RemoteComposeState implements CollectionsAccess {
/**
* Is any command listening to this variable
*
- * @param id
- * @return
+ * @param id The Variable id
+ * @return true if any command is listening to this variable
*/
public boolean hasListener(int id) {
return mVarListeners.get(id) != null;
@@ -460,8 +473,8 @@ public class RemoteComposeState implements CollectionsAccess {
/**
* List of Commands that need to be updated
*
- * @param context
- * @return
+ * @param context The context
+ * @return The number of ops to update
*/
public int getOpsToUpdate(@NonNull RemoteContext context) {
if (mVarListeners.get(RemoteContext.ID_CONTINUOUS_SEC) != null) {
@@ -479,7 +492,7 @@ public class RemoteComposeState implements CollectionsAccess {
/**
* Set the width of the overall document on screen.
*
- * @param width
+ * @param width the width of the document in pixels
*/
public void setWindowWidth(float width) {
updateFloat(RemoteContext.ID_WINDOW_WIDTH, width);
@@ -488,12 +501,18 @@ public class RemoteComposeState implements CollectionsAccess {
/**
* Set the width of the overall document on screen.
*
- * @param height
+ * @param height the height of the document in pixels
*/
public void setWindowHeight(float height) {
updateFloat(RemoteContext.ID_WINDOW_HEIGHT, height);
}
+ /**
+ * Add an array access
+ *
+ * @param id The id of the array Access
+ * @param collection The array access
+ */
public void addCollection(int id, @NonNull ArrayAccess collection) {
mCollectionMap.put(id & 0xFFFFF, collection);
}
@@ -513,10 +532,22 @@ public class RemoteComposeState implements CollectionsAccess {
return mCollectionMap.get(id & 0xFFFFF).getId(index);
}
+ /**
+ * adds a DataMap to the cache
+ *
+ * @param id The id of the data map
+ * @param map The data map
+ */
public void putDataMap(int id, @NonNull DataMap map) {
mDataMapMap.put(id, map);
}
+ /**
+ * Get the DataMap asociated with the id
+ *
+ * @param id the id of the DataMap
+ * @return the DataMap
+ */
public @Nullable DataMap getDataMap(int id) {
return mDataMapMap.get(id);
}
@@ -526,15 +557,32 @@ public class RemoteComposeState implements CollectionsAccess {
return mCollectionMap.get(id & 0xFFFFF).getLength();
}
+ /**
+ * sets the RemoteContext
+ *
+ * @param context the context
+ */
public void setContext(@NonNull RemoteContext context) {
mRemoteContext = context;
mRemoteContext.clearLastOpCount();
}
+ /**
+ * Add an object to the cache. Uses the id for the item and adds it to the cache based
+ *
+ * @param id the id of the object
+ * @param value the object
+ */
public void updateObject(int id, @NonNull Object value) {
mObjectMap.put(id, value);
}
+ /**
+ * Get an object from the cache
+ *
+ * @param id The id of the object
+ * @return The object
+ */
public @Nullable Object getObject(int id) {
return mObjectMap.get(id);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
index 23c362830713..36e4ec1ff303 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
@@ -47,7 +47,7 @@ public abstract class RemoteContext {
new RemoteComposeState(); // todo, is this a valid use of RemoteComposeState -- bbade@
@Nullable protected PaintContext mPaintContext = null;
- protected float mDensity = 2.75f;
+ protected float mDensity = Float.NaN;
@NonNull ContextMode mMode = ContextMode.UNSET;
@@ -77,7 +77,7 @@ public abstract class RemoteContext {
* @param density
*/
public void setDensity(float density) {
- if (density > 0) {
+ if (!Float.isNaN(density) && density > 0) {
mDensity = density;
}
}
@@ -234,23 +234,60 @@ public abstract class RemoteContext {
*/
public abstract void addCollection(int id, @NonNull ArrayAccess collection);
+ /**
+ * put DataMap under an id
+ *
+ * @param id the id of the DataMap
+ * @param map the DataMap
+ */
public abstract void putDataMap(int id, @NonNull DataMap map);
+ /**
+ * Get a DataMap given an id
+ *
+ * @param id the id of the DataMap
+ * @return the DataMap
+ */
public abstract @Nullable DataMap getDataMap(int id);
+ /**
+ * Run an action
+ *
+ * @param id the id of the action
+ * @param metadata the metadata of the action
+ */
public abstract void runAction(int id, @NonNull String metadata);
// TODO: we might add an interface to group all valid parameter types
+
+ /**
+ * Run an action with a named parameter
+ *
+ * @param textId the text id of the action
+ * @param value the value of the parameter
+ */
public abstract void runNamedAction(int textId, Object value);
+ /**
+ * Put an object under an id
+ *
+ * @param mId the id of the object
+ * @param command the object
+ */
public abstract void putObject(int mId, @NonNull Object command);
+ /**
+ * Get an object given an id
+ *
+ * @param mId the id of the object
+ * @return the object
+ */
public abstract @Nullable Object getObject(int mId);
/**
* Add a touch listener to the context
*
- * @param touchExpression
+ * @param touchExpression the touch expression
*/
public void addTouchListener(TouchListener touchExpression) {}
@@ -668,11 +705,24 @@ public abstract class RemoteContext {
///////////////////////////////////////////////////////////////////////////////////////////////
// Click handling
///////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Is this a time id float
+ *
+ * @param fl the floatId to test
+ * @return true if it is a time id
+ */
public static boolean isTime(float fl) {
int value = Utils.idFromNan(fl);
return value >= ID_CONTINUOUS_SEC && value <= ID_DAY_OF_MONTH;
}
+ /**
+ * get the time from a float id that indicates a type of time
+ *
+ * @param fl id of the type of time information requested
+ * @return various time information such as seconds or min
+ */
public static float getTime(float fl) {
LocalDateTime dateTime =
LocalDateTime.now(ZoneId.systemDefault()); // TODO, pass in a timezone explicitly?
@@ -716,6 +766,17 @@ public abstract class RemoteContext {
return fl;
}
+ /**
+ * Add a click area to the doc
+ *
+ * @param id the id of the click area
+ * @param contentDescription the content description of the click area
+ * @param left the left bounds of the click area
+ * @param top the top bounds of the click area
+ * @param right the right bounds of the click area
+ * @param bottom the
+ * @param metadataId the id of the metadata string
+ */
public abstract void addClickArea(
int id,
int contentDescription,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
index b55f25c911fe..06ef9979a267 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
@@ -102,6 +102,12 @@ public class ClipPath extends PaintOperation {
return OP_CODE;
}
+ /**
+ * Apply this operation to the buffer
+ *
+ * @param buffer the buffer to apply the operation to
+ * @param id the id of the path
+ */
public static void apply(@NonNull WireBuffer buffer, int id) {
buffer.start(OP_CODE);
buffer.writeInt(id);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java
index ac6271c6328e..7a72b109b2a8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java
@@ -70,6 +70,13 @@ public class DataListFloat extends Operation implements VariableSupport, ArrayAc
return "DataListFloat[" + Utils.idString(mId) + "] " + Arrays.toString(mValues);
}
+ /**
+ * Write this operation to the buffer
+ *
+ * @param buffer the buffer to apply the operation to
+ * @param id the id of the array
+ * @param values the values of the array
+ */
public static void apply(@NonNull WireBuffer buffer, int id, @NonNull float[] values) {
buffer.start(OP_CODE);
buffer.writeInt(id);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java
index 47cbff36d492..7e29620ec104 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java
@@ -62,6 +62,13 @@ public class DataListIds extends Operation implements VariableSupport, ArrayAcce
return "map[" + Utils.idString(mId) + "] \"" + Arrays.toString(mIds) + "\"";
}
+ /**
+ * Write this operation to the buffer
+ *
+ * @param buffer the buffer to apply the operation to
+ * @param id the id of the array
+ * @param ids the values of the array
+ */
public static void apply(@NonNull WireBuffer buffer, int id, @NonNull int[] ids) {
buffer.start(OP_CODE);
buffer.writeInt(id);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java
index ff85721027f7..33752e0b2134 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java
@@ -89,6 +89,15 @@ public class DataMapIds extends Operation {
return builder.toString();
}
+ /**
+ * Write this operation to the buffer
+ *
+ * @param buffer the buffer to apply the operation to
+ * @param id the id
+ * @param names the names of the variables
+ * @param type the types of the variables
+ * @param ids the ids of the variables
+ */
public static void apply(
@NonNull WireBuffer buffer,
int id,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
index c1e2e662ca80..7f1ba6f94065 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
@@ -31,8 +31,8 @@ import java.util.List;
/** Base class for commands that take 3 float */
public abstract class DrawBase2 extends PaintOperation implements VariableSupport {
@NonNull protected String mName = "DrawRectBase";
- float mV1;
- float mV2;
+ protected float mV1;
+ protected float mV2;
float mValue1;
float mValue2;
@@ -76,6 +76,13 @@ public abstract class DrawBase2 extends PaintOperation implements VariableSuppor
return mName + " " + floatToString(mV1) + " " + floatToString(mV2);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param maker the maker of the operation
+ * @param buffer the buffer to read
+ * @param operations the list of operations to add to
+ */
public static void read(
@NonNull Maker maker, @NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
float v1 = buffer.readFloat();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
index 6fedea3245a2..a6bfda8beccd 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
@@ -92,6 +92,13 @@ public abstract class DrawBase3 extends PaintOperation implements VariableSuppor
+ floatToString(mV3);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param maker the maker of the operation
+ * @param buffer the buffer to read
+ * @param operations the list of operations to add to
+ */
public static void read(
@NonNull Maker maker, @NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
float v1 = buffer.readFloat();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java
index aa9cc68e6552..1e96bcd9cebf 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java
@@ -102,6 +102,13 @@ public abstract class DrawBase4 extends PaintOperation implements VariableSuppor
+ floatToString(mY2Value, mY2);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param maker the maker of the operation
+ * @param buffer the buffer to read
+ * @param operations the list of operations to add to
+ */
public static void read(
@NonNull Maker maker, @NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
float v1 = buffer.readFloat();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java
index 64c2730e5f9a..bc5904584527 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java
@@ -116,6 +116,13 @@ public abstract class DrawBase6 extends PaintOperation implements VariableSuppor
DrawBase6 create(float v1, float v2, float v3, float v4, float v5, float v6);
}
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param build interface to construct the component
+ * @param buffer the buffer to read from
+ * @param operations the list of operations to add to
+ */
public static void read(
@NonNull Maker build, @NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
float sv1 = buffer.readFloat();
@@ -132,13 +139,13 @@ public abstract class DrawBase6 extends PaintOperation implements VariableSuppor
/**
* writes out a the operation to the buffer.
*
- * @param v1
- * @param v2
- * @param v3
- * @param v4
- * @param v5
- * @param v6
- * @return
+ * @param v1 the first parameter
+ * @param v2 the second parameter
+ * @param v3 the third parameter
+ * @param v4 the fourth parameter
+ * @param v5 the fifth parameter
+ * @param v6 the sixth parameter
+ * @return the operation
*/
@Nullable
public Operation construct(float v1, float v2, float v3, float v4, float v5, float v6) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
index cdb527dee460..40d3bede0912 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
@@ -137,6 +137,17 @@ public class DrawBitmap extends PaintOperation implements VariableSupport {
return OP_CODE;
}
+ /**
+ * Writes out the operation to the buffer
+ *
+ * @param buffer the buffer to write to
+ * @param id the id of the Bitmap
+ * @param left left most x coordinate
+ * @param top top most y coordinate
+ * @param right right most x coordinate
+ * @param bottom bottom most y coordinate
+ * @param descriptionId string id of the description
+ */
public static void apply(
@NonNull WireBuffer buffer,
int id,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java
index 638fe148d746..013dd1ae9db8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java
@@ -131,6 +131,21 @@ public class DrawBitmapInt extends PaintOperation implements AccessibleComponent
return OP_CODE;
}
+ /**
+ * Draw a bitmap using integer coordinates
+ *
+ * @param buffer the buffer to write to
+ * @param imageId the id of the bitmap
+ * @param srcLeft the left most pixel in the bitmap
+ * @param srcTop the top most pixel in the bitmap
+ * @param srcRight the right most pixel in the bitmap
+ * @param srcBottom the bottom most pixel in the bitmap
+ * @param dstLeft the left most pixel in the destination
+ * @param dstTop the top most pixel in the destination
+ * @param dstRight the right most pixel in the destination
+ * @param dstBottom the bottom most pixel in the destination
+ * @param cdId the content discription id
+ */
public static void apply(
@NonNull WireBuffer buffer,
int imageId,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java
index d6467c926747..e1070f97d5aa 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java
@@ -217,6 +217,23 @@ public class DrawBitmapScaled extends PaintOperation
return OP_CODE;
}
+ /**
+ * Draw a bitmap using integer coordinates
+ *
+ * @param buffer the buffer to write to
+ * @param imageId the id of the image
+ * @param srcLeft the left most pixel in the image to draw
+ * @param srcTop the right most pixel in the image to draw
+ * @param srcRight the right most pixel in the image to draw
+ * @param srcBottom the bottom most pixel in the image to draw
+ * @param dstLeft the left most pixel in the destination
+ * @param dstTop the top most pixel in the destination
+ * @param dstRight the right most pixel in the destination
+ * @param dstBottom the bottom most pixel in the destination
+ * @param scaleType the type of scale operation
+ * @param scaleFactor the scalefactor to use with fixed scale
+ * @param cdId the content discription id
+ */
public static void apply(
@NonNull WireBuffer buffer,
int imageId,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
index 398cf4892e12..db9c4d3efafa 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
@@ -81,6 +81,12 @@ public class DrawPath extends PaintOperation {
return Operations.DRAW_PATH;
}
+ /**
+ * Draw a path
+ *
+ * @param buffer the buffer to write to
+ * @param id the id of the path
+ */
public static void apply(@NonNull WireBuffer buffer, int id) {
buffer.start(Operations.DRAW_PATH);
buffer.writeInt(id);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
index 86f3c992f2fb..3ab4a87c614c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
@@ -117,6 +117,15 @@ public class DrawTextOnPath extends PaintOperation implements VariableSupport {
return Operations.DRAW_TEXT_ON_PATH;
}
+ /**
+ * add a draw text on path operation to the buffer
+ *
+ * @param buffer the buffer to add to
+ * @param textId the id of the text string
+ * @param pathId the id of the path
+ * @param hOffset the horizontal offset to position the string
+ * @param vOffset the vertical offset to position the string
+ */
public static void apply(
@NonNull WireBuffer buffer, int textId, int pathId, float hOffset, float vOffset) {
buffer.start(OP_CODE);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
index d4d4a5ecf6b9..e2883949022c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
@@ -127,6 +127,16 @@ public class DrawTweenPath extends PaintOperation implements VariableSupport {
return Operations.DRAW_TWEEN_PATH;
}
+ /**
+ * add a draw tween path operation to the buffer
+ *
+ * @param buffer the buffer to add to
+ * @param path1Id the first path
+ * @param path2Id the second path
+ * @param tween the amount of the tween
+ * @param start the start sub range to draw
+ * @param stop the end of the sub range to draw
+ */
public static void apply(
@NonNull WireBuffer buffer,
int path1Id,
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
index 044430d1e3c1..66daa13dd21c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
@@ -74,6 +74,11 @@ public class MatrixRestore extends PaintOperation {
return OP_CODE;
}
+ /**
+ * add a matrix restore operation to the buffer
+ *
+ * @param buffer the buffer to add to
+ */
public static void apply(@NonNull WireBuffer buffer) {
buffer.start(OP_CODE);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java
index aec316aea361..ec918e8260b9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java
@@ -72,6 +72,11 @@ public class MatrixSave extends PaintOperation {
return OP_CODE;
}
+ /**
+ * add a matrix save operation to the buffer
+ *
+ * @param buffer the buffer to add to
+ */
public static void apply(@NonNull WireBuffer buffer) {
buffer.start(Operations.MATRIX_SAVE);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
index daf2c5502c5d..f756b76b86c3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
@@ -32,6 +32,7 @@ import com.android.internal.widget.remotecompose.core.operations.paint.PaintBund
import java.util.List;
+/** Paint data operation */
public class PaintData extends PaintOperation implements VariableSupport {
private static final int OP_CODE = Operations.PAINT_VALUES;
private static final String CLASS_NAME = "PaintData";
@@ -80,6 +81,12 @@ public class PaintData extends PaintOperation implements VariableSupport {
return OP_CODE;
}
+ /**
+ * add a paint data to the buffer
+ *
+ * @param buffer the buffer to add to
+ * @param paintBundle the paint bundle
+ */
public static void apply(@NonNull WireBuffer buffer, @NonNull PaintBundle paintBundle) {
buffer.start(Operations.PAINT_VALUES);
paintBundle.writeBundle(buffer);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java
index 7ff879e41cac..e7cce03f0c4b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathAppend.java
@@ -101,6 +101,7 @@ public class PathAppend extends PaintOperation implements VariableSupport {
public static final int CUBIC = 14;
public static final int CLOSE = 15;
public static final int DONE = 16;
+ public static final int RESET = 17;
public static final float MOVE_NAN = Utils.asNan(MOVE);
public static final float LINE_NAN = Utils.asNan(LINE);
public static final float QUADRATIC_NAN = Utils.asNan(QUADRATIC);
@@ -108,6 +109,7 @@ public class PathAppend extends PaintOperation implements VariableSupport {
public static final float CUBIC_NAN = Utils.asNan(CUBIC);
public static final float CLOSE_NAN = Utils.asNan(CLOSE);
public static final float DONE_NAN = Utils.asNan(DONE);
+ public static final float RESET_NAN = Utils.asNan(RESET);
/**
* The name of the class
@@ -128,6 +130,14 @@ public class PathAppend extends PaintOperation implements VariableSupport {
return OP_CODE;
}
+ /**
+ * add a path append operation to the buffer. With PathCreate allows you create a path
+ * dynamically
+ *
+ * @param buffer add the data to this buffer
+ * @param id id of the path
+ * @param data the path data to append
+ */
public static void apply(@NonNull WireBuffer buffer, int id, @NonNull float[] data) {
buffer.start(OP_CODE);
buffer.writeInt(id);
@@ -175,6 +185,10 @@ public class PathAppend extends PaintOperation implements VariableSupport {
public void apply(@NonNull RemoteContext context) {
float[] data = context.getPathData(mInstanceId);
float[] out = mOutputPath;
+ if (Float.floatToRawIntBits(out[0]) == Float.floatToRawIntBits(RESET_NAN)) {
+ context.loadPathData(mInstanceId, new float[0]);
+ return;
+ }
if (data != null) {
out = new float[data.length + mOutputPath.length];
@@ -190,6 +204,12 @@ public class PathAppend extends PaintOperation implements VariableSupport {
context.loadPathData(mInstanceId, out);
}
+ /**
+ * Convert a path to a string
+ *
+ * @param path the path to convert
+ * @return text representation of path
+ */
@NonNull
public static String pathString(@Nullable float[] path) {
if (path == null) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java
index 75562cd8fb4c..1f76639b1b1f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathCreate.java
@@ -131,6 +131,14 @@ public class PathCreate extends PaintOperation implements VariableSupport {
return OP_CODE;
}
+ /**
+ * add a create path operation
+ *
+ * @param buffer buffer to add to
+ * @param id the id of the path
+ * @param startX the start x of the path (moveTo x,y)
+ * @param startY the start y of the path (moveTo x,y)
+ */
public static void apply(@NonNull WireBuffer buffer, int id, float startX, float startY) {
buffer.start(OP_CODE);
buffer.writeInt(id);
@@ -165,6 +173,12 @@ public class PathCreate extends PaintOperation implements VariableSupport {
.field(FLOAT, "startX", "initial start y");
}
+ /**
+ * convert a path to a string
+ *
+ * @param path path to convert (expressed as an array of floats)
+ * @return the text representing the path
+ */
@NonNull
public static String pathString(@Nullable float[] path) {
if (path == null) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
index 85a01fc7cbc7..45d99a716443 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
@@ -131,6 +131,13 @@ public class PathData extends Operation implements VariableSupport {
return OP_CODE;
}
+ /**
+ * add a create path operation
+ *
+ * @param buffer buffer to add to
+ * @param id the id of the path
+ * @param data the path
+ */
public static void apply(@NonNull WireBuffer buffer, int id, @NonNull float[] data) {
buffer.start(Operations.DATA_PATH);
buffer.writeInt(id);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
index d48de37996ee..5788d8f4da64 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
@@ -73,6 +73,13 @@ public class TextData extends Operation implements SerializableToString {
return OP_CODE;
}
+ /**
+ * add a text data operation
+ *
+ * @param buffer buffer to add to
+ * @param textId the id for the text
+ * @param text the data to encode
+ */
public static void apply(@NonNull WireBuffer buffer, int textId, @NonNull String text) {
buffer.start(OP_CODE);
buffer.writeInt(textId);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java
index 37ea567f5913..a6570a371f15 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java
@@ -50,6 +50,11 @@ public class TextLength extends Operation {
return CLASS_NAME + "[" + mLengthId + "] = " + mTextId;
}
+ /**
+ * The name of the class
+ *
+ * @return the name
+ */
public static @NonNull String name() {
return CLASS_NAME;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java
index d51b38924126..58cd68e2a5db 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java
@@ -66,6 +66,11 @@ public class TextMeasure extends PaintOperation {
return "FloatConstant[" + mId + "] = " + mTextId + " " + mType;
}
+ /**
+ * The name of the class
+ *
+ * @return the name
+ */
public static @NonNull String name() {
return CLASS_NAME;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
index e71cb9a51830..dcd334822010 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
@@ -36,10 +36,12 @@ import com.android.internal.widget.remotecompose.core.operations.layout.modifier
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentVisibilityOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.DimensionModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.GraphicsLayerModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightInModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.PaddingModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ScrollModifierOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthInModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ZIndexModifierOperation;
@@ -204,6 +206,9 @@ public class LayoutComponent extends Component {
mPaddingRight = 0f;
mPaddingBottom = 0f;
+ WidthInModifierOperation widthInConstraints = null;
+ HeightInModifierOperation heightInConstraints = null;
+
for (OperationInterface op : mComponentModifiers.getList()) {
if (op instanceof PaddingModifierOperation) {
// We are accumulating padding modifiers to compute the margin
@@ -221,6 +226,10 @@ public class LayoutComponent extends Component {
mWidthModifier = (WidthModifierOperation) op;
} else if (op instanceof HeightModifierOperation && mHeightModifier == null) {
mHeightModifier = (HeightModifierOperation) op;
+ } else if (op instanceof WidthInModifierOperation) {
+ widthInConstraints = (WidthInModifierOperation) op;
+ } else if (op instanceof HeightInModifierOperation) {
+ heightInConstraints = (HeightInModifierOperation) op;
} else if (op instanceof ZIndexModifierOperation) {
mZIndexModifier = (ZIndexModifierOperation) op;
} else if (op instanceof GraphicsLayerModifierOperation) {
@@ -241,6 +250,12 @@ public class LayoutComponent extends Component {
if (mHeightModifier == null) {
mHeightModifier = new HeightModifierOperation(DimensionModifierOperation.Type.WRAP);
}
+ if (widthInConstraints != null) {
+ mWidthModifier.setWidthIn(widthInConstraints);
+ }
+ if (heightInConstraints != null) {
+ mHeightModifier.setHeightIn(heightInConstraints);
+ }
setWidth(computeModifierDefinedWidth(null));
setHeight(computeModifierDefinedHeight(null));
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java
new file mode 100644
index 000000000000..afc41b1873ef
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleColumnLayout.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout.managers;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+
+import java.util.List;
+
+public class CollapsibleColumnLayout extends ColumnLayout {
+
+ public CollapsibleColumnLayout(
+ @Nullable Component parent,
+ int componentId,
+ int animationId,
+ float x,
+ float y,
+ float width,
+ float height,
+ int horizontalPositioning,
+ int verticalPositioning,
+ float spacedBy) {
+ super(
+ parent,
+ componentId,
+ animationId,
+ x,
+ y,
+ width,
+ height,
+ horizontalPositioning,
+ verticalPositioning,
+ spacedBy);
+ }
+
+ public CollapsibleColumnLayout(
+ @Nullable Component parent,
+ int componentId,
+ int animationId,
+ int horizontalPositioning,
+ int verticalPositioning,
+ float spacedBy) {
+ super(
+ parent,
+ componentId,
+ animationId,
+ horizontalPositioning,
+ verticalPositioning,
+ spacedBy);
+ }
+
+ @NonNull
+ @Override
+ protected String getSerializedName() {
+ return "COLLAPSIBLE_COLUMN";
+ }
+
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
+ public static int id() {
+ return Operations.LAYOUT_COLLAPSIBLE_COLUMN;
+ }
+
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer wire buffer
+ * @param componentId component id
+ * @param animationId animation id (-1 if not set)
+ * @param horizontalPositioning horizontal positioning rules
+ * @param verticalPositioning vertical positioning rules
+ * @param spacedBy spaced by value
+ */
+ public static void apply(
+ @NonNull WireBuffer buffer,
+ int componentId,
+ int animationId,
+ int horizontalPositioning,
+ int verticalPositioning,
+ float spacedBy) {
+ buffer.start(id());
+ buffer.writeInt(componentId);
+ buffer.writeInt(animationId);
+ buffer.writeInt(horizontalPositioning);
+ buffer.writeInt(verticalPositioning);
+ buffer.writeFloat(spacedBy);
+ }
+
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+ int componentId = buffer.readInt();
+ int animationId = buffer.readInt();
+ int horizontalPositioning = buffer.readInt();
+ int verticalPositioning = buffer.readInt();
+ float spacedBy = buffer.readFloat();
+ operations.add(
+ new CollapsibleColumnLayout(
+ null,
+ componentId,
+ animationId,
+ horizontalPositioning,
+ verticalPositioning,
+ spacedBy));
+ }
+
+ @Override
+ protected boolean hasVerticalIntrinsicDimension() {
+ return true;
+ }
+
+ @Override
+ public void computeWrapSize(
+ @NonNull PaintContext context,
+ float maxWidth,
+ float maxHeight,
+ boolean horizontalWrap,
+ boolean verticalWrap,
+ @NonNull MeasurePass measure,
+ @NonNull Size size) {
+ super.computeWrapSize(
+ context, maxWidth, Float.MAX_VALUE, horizontalWrap, verticalWrap, measure, size);
+ }
+
+ @Override
+ public boolean applyVisibility(
+ float selfWidth, float selfHeight, @NonNull MeasurePass measure) {
+ float childrenWidth = 0f;
+ float childrenHeight = 0f;
+ boolean changedVisibility = false;
+ for (Component child : mChildrenComponents) {
+ ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
+ if (childrenHeight + childMeasure.getH() > selfHeight) {
+ childMeasure.setVisibility(Visibility.GONE);
+ changedVisibility = true;
+ } else {
+ childrenHeight += childMeasure.getH();
+ childrenWidth = Math.max(childrenWidth, childMeasure.getW());
+ }
+ }
+ return changedVisibility;
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java
new file mode 100644
index 000000000000..0e7eb8676f46
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CollapsibleRowLayout.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout.managers;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+
+import java.util.List;
+
+public class CollapsibleRowLayout extends RowLayout {
+
+ public CollapsibleRowLayout(
+ @Nullable Component parent,
+ int componentId,
+ int animationId,
+ float x,
+ float y,
+ float width,
+ float height,
+ int horizontalPositioning,
+ int verticalPositioning,
+ float spacedBy) {
+ super(
+ parent,
+ componentId,
+ animationId,
+ x,
+ y,
+ width,
+ height,
+ horizontalPositioning,
+ verticalPositioning,
+ spacedBy);
+ }
+
+ public CollapsibleRowLayout(
+ @Nullable Component parent,
+ int componentId,
+ int animationId,
+ int horizontalPositioning,
+ int verticalPositioning,
+ float spacedBy) {
+ super(
+ parent,
+ componentId,
+ animationId,
+ horizontalPositioning,
+ verticalPositioning,
+ spacedBy);
+ }
+
+ @NonNull
+ @Override
+ protected String getSerializedName() {
+ return "COLLAPSIBLE_ROW";
+ }
+
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
+ public static int id() {
+ return Operations.LAYOUT_COLLAPSIBLE_ROW;
+ }
+
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer wire buffer
+ * @param componentId component id
+ * @param animationId animation id (-1 if not set)
+ * @param horizontalPositioning horizontal positioning rules
+ * @param verticalPositioning vertical positioning rules
+ * @param spacedBy spaced by value
+ */
+ public static void apply(
+ @NonNull WireBuffer buffer,
+ int componentId,
+ int animationId,
+ int horizontalPositioning,
+ int verticalPositioning,
+ float spacedBy) {
+ buffer.start(id());
+ buffer.writeInt(componentId);
+ buffer.writeInt(animationId);
+ buffer.writeInt(horizontalPositioning);
+ buffer.writeInt(verticalPositioning);
+ buffer.writeFloat(spacedBy);
+ }
+
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+ int componentId = buffer.readInt();
+ int animationId = buffer.readInt();
+ int horizontalPositioning = buffer.readInt();
+ int verticalPositioning = buffer.readInt();
+ float spacedBy = buffer.readFloat();
+ operations.add(
+ new CollapsibleRowLayout(
+ null,
+ componentId,
+ animationId,
+ horizontalPositioning,
+ verticalPositioning,
+ spacedBy));
+ }
+
+ @Override
+ protected boolean hasHorizontalIntrinsicDimension() {
+ return true;
+ }
+
+ @Override
+ public void computeWrapSize(
+ @NonNull PaintContext context,
+ float maxWidth,
+ float maxHeight,
+ boolean horizontalWrap,
+ boolean verticalWrap,
+ @NonNull MeasurePass measure,
+ @NonNull Size size) {
+ super.computeWrapSize(
+ context, Float.MAX_VALUE, maxHeight, horizontalWrap, verticalWrap, measure, size);
+ }
+
+ @Override
+ public boolean applyVisibility(
+ float selfWidth, float selfHeight, @NonNull MeasurePass measure) {
+ float childrenWidth = 0f;
+ float childrenHeight = 0f;
+ boolean changedVisibility = false;
+ for (Component child : mChildrenComponents) {
+ ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
+ if (childrenWidth + childMeasure.getW() > selfWidth) {
+ childMeasure.setVisibility(Visibility.GONE);
+ changedVisibility = true;
+ } else {
+ childrenWidth += childMeasure.getW();
+ childrenHeight = Math.max(childrenHeight, childMeasure.getH());
+ }
+ }
+ return changedVisibility;
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
index f68d7b439578..4d0cbefb0c92 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
@@ -32,6 +32,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.LayoutCo
import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.HeightInModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.utils.DebugLog;
import java.util.List;
@@ -93,7 +94,8 @@ public class ColumnLayout extends LayoutManager {
@NonNull
@Override
public String toString() {
- return "COLUMN ["
+ return getSerializedName()
+ + " ["
+ mComponentId
+ ":"
+ mAnimationId
@@ -213,41 +215,62 @@ public class ColumnLayout extends LayoutManager {
selfHeight =
mComponentModifiers.getVerticalScrollDimension() - mPaddingTop - mPaddingBottom;
}
- boolean hasWeights = false;
- float totalWeights = 0f;
- for (Component child : mChildrenComponents) {
- ComponentMeasure childMeasure = measure.get(child);
- if (childMeasure.getVisibility() == Visibility.GONE) {
- continue;
- }
- if (child instanceof LayoutComponent
- && ((LayoutComponent) child).getHeightModifier().hasWeight()) {
- hasWeights = true;
- totalWeights += ((LayoutComponent) child).getHeightModifier().getValue();
- } else {
- childrenHeight += childMeasure.getH();
- }
- }
- if (hasWeights) {
- float availableSpace = selfHeight - childrenHeight;
+ boolean checkWeights = true;
+ while (checkWeights) {
+ checkWeights = false;
+ boolean hasWeights = false;
+ float totalWeights = 0f;
for (Component child : mChildrenComponents) {
+ ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
if (child instanceof LayoutComponent
&& ((LayoutComponent) child).getHeightModifier().hasWeight()) {
- ComponentMeasure childMeasure = measure.get(child);
- if (childMeasure.getVisibility() == Visibility.GONE) {
- continue;
+ hasWeights = true;
+ totalWeights += ((LayoutComponent) child).getHeightModifier().getValue();
+ } else {
+ childrenHeight += childMeasure.getH();
+ }
+ }
+ if (hasWeights) {
+ float availableSpace = selfHeight - childrenHeight;
+ for (Component child : mChildrenComponents) {
+ if (child instanceof LayoutComponent
+ && ((LayoutComponent) child).getHeightModifier().hasWeight()) {
+ ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
+ float weight = ((LayoutComponent) child).getHeightModifier().getValue();
+ float childHeight = (weight * availableSpace) / totalWeights;
+ HeightInModifierOperation heightInConstraints =
+ ((LayoutComponent) child).getHeightModifier().getHeightIn();
+ if (heightInConstraints != null) {
+ float min = heightInConstraints.getMin();
+ float max = heightInConstraints.getMax();
+ if (min != -1) {
+ childHeight = Math.max(min, childHeight);
+ }
+ if (max != -1) {
+ childHeight = Math.min(max, childHeight);
+ }
+ }
+ childMeasure.setH(childHeight);
+ child.measure(
+ context,
+ childMeasure.getW(),
+ childMeasure.getW(),
+ childMeasure.getH(),
+ childMeasure.getH(),
+ measure);
}
- float weight = ((LayoutComponent) child).getHeightModifier().getValue();
- childMeasure.setH((weight * availableSpace) / totalWeights);
- child.measure(
- context,
- childMeasure.getW(),
- childMeasure.getW(),
- childMeasure.getH(),
- childMeasure.getH(),
- measure);
}
}
+
+ if (applyVisibility(selfWidth, selfHeight, measure) && hasWeights) {
+ checkWeights = true;
+ }
}
childrenHeight = 0f;
@@ -360,6 +383,16 @@ public class ColumnLayout extends LayoutManager {
return Operations.LAYOUT_COLUMN;
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer wire buffer
+ * @param componentId component id
+ * @param animationId animation id (-1 if not set)
+ * @param horizontalPositioning horizontal positioning rules
+ * @param verticalPositioning vertical positioning rules
+ * @param spacedBy spaced by value
+ */
public static void apply(
@NonNull WireBuffer buffer,
int componentId,
@@ -367,7 +400,7 @@ public class ColumnLayout extends LayoutManager {
int horizontalPositioning,
int verticalPositioning,
float spacedBy) {
- buffer.start(Operations.LAYOUT_COLUMN);
+ buffer.start(id());
buffer.writeInt(componentId);
buffer.writeInt(animationId);
buffer.writeInt(horizontalPositioning);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
index edfd69cbfa96..8b52bbe5cdf8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
@@ -43,6 +43,18 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl
super(parent, componentId, animationId, x, y, width, height);
}
+ /**
+ * Allows layout managers to override elements visibility
+ *
+ * @param selfWidth intrinsic width of the layout manager content
+ * @param selfHeight intrinsic height of the layout manager content
+ * @param measure measure pass
+ */
+ public boolean applyVisibility(
+ float selfWidth, float selfHeight, @NonNull MeasurePass measure) {
+ return false;
+ }
+
/** Implemented by subclasses to provide a layout/measure pass */
public void internalLayoutMeasure(@NonNull PaintContext context, @NonNull MeasurePass measure) {
// nothing here
@@ -197,7 +209,7 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl
}
if (!hasWrap) {
- if (hasHorizontalScroll()) {
+ if (hasHorizontalIntrinsicDimension()) {
mCachedWrapSize.setWidth(0f);
mCachedWrapSize.setHeight(0f);
computeWrapSize(
@@ -210,15 +222,19 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl
mCachedWrapSize);
float w = mCachedWrapSize.getWidth();
computeSize(context, 0f, w, 0, measuredHeight, measure);
- mComponentModifiers.setHorizontalScrollDimension(measuredWidth, w);
- } else if (hasVerticalScroll()) {
+ if (hasHorizontalScroll()) {
+ mComponentModifiers.setHorizontalScrollDimension(measuredWidth, w);
+ }
+ } else if (hasVerticalIntrinsicDimension()) {
mCachedWrapSize.setWidth(0f);
mCachedWrapSize.setHeight(0f);
computeWrapSize(
context, maxWidth, Float.MAX_VALUE, false, false, measure, mCachedWrapSize);
float h = mCachedWrapSize.getHeight();
computeSize(context, 0f, measuredWidth, 0, h, measure);
- mComponentModifiers.setVerticalScrollDimension(measuredHeight, h);
+ if (hasVerticalScroll()) {
+ mComponentModifiers.setVerticalScrollDimension(measuredHeight, h);
+ }
} else {
float maxChildWidth = measuredWidth - mPaddingLeft - mPaddingRight;
float maxChildHeight = measuredHeight - mPaddingTop - mPaddingBottom;
@@ -246,6 +262,14 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl
return mComponentModifiers.hasHorizontalScroll();
}
+ protected boolean hasHorizontalIntrinsicDimension() {
+ return hasHorizontalScroll();
+ }
+
+ protected boolean hasVerticalIntrinsicDimension() {
+ return hasVerticalScroll();
+ }
+
private boolean hasVerticalScroll() {
return mComponentModifiers.hasVerticalScroll();
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
index b688f6e4175a..5b35c4c70702 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
@@ -32,6 +32,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.LayoutCo
import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthInModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.utils.DebugLog;
import java.util.List;
@@ -91,7 +92,8 @@ public class RowLayout extends LayoutManager {
@NonNull
@Override
public String toString() {
- return "ROW ["
+ return getSerializedName()
+ + " ["
+ mComponentId
+ ":"
+ mAnimationId
@@ -212,44 +214,66 @@ public class RowLayout extends LayoutManager {
mComponentModifiers.getVerticalScrollDimension() - mPaddingTop - mPaddingBottom;
}
- boolean hasWeights = false;
- float totalWeights = 0f;
- for (Component child : mChildrenComponents) {
- ComponentMeasure childMeasure = measure.get(child);
- if (childMeasure.getVisibility() == Visibility.GONE) {
- continue;
- }
- if (child instanceof LayoutComponent
- && ((LayoutComponent) child).getWidthModifier().hasWeight()) {
- hasWeights = true;
- totalWeights += ((LayoutComponent) child).getWidthModifier().getValue();
- } else {
- childrenWidth += childMeasure.getW();
- }
- }
+ boolean checkWeights = true;
- // TODO: need to move the weight measuring in the measure function,
- // currently we'll measure unnecessarily
- if (hasWeights) {
- float availableSpace = selfWidth - childrenWidth;
+ while (checkWeights) {
+ checkWeights = false;
+ boolean hasWeights = false;
+ float totalWeights = 0f;
for (Component child : mChildrenComponents) {
+ ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
if (child instanceof LayoutComponent
&& ((LayoutComponent) child).getWidthModifier().hasWeight()) {
- ComponentMeasure childMeasure = measure.get(child);
- if (childMeasure.getVisibility() == Visibility.GONE) {
- continue;
+ hasWeights = true;
+ totalWeights += ((LayoutComponent) child).getWidthModifier().getValue();
+ } else {
+ childrenWidth += childMeasure.getW();
+ }
+ }
+
+ // TODO: need to move the weight measuring in the measure function,
+ // currently we'll measure unnecessarily
+ if (hasWeights) {
+ float availableSpace = selfWidth - childrenWidth;
+ for (Component child : mChildrenComponents) {
+ if (child instanceof LayoutComponent
+ && ((LayoutComponent) child).getWidthModifier().hasWeight()) {
+ ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
+ float weight = ((LayoutComponent) child).getWidthModifier().getValue();
+ float childWidth = (weight * availableSpace) / totalWeights;
+ WidthInModifierOperation widthInConstraints =
+ ((LayoutComponent) child).getWidthModifier().getWidthIn();
+ if (widthInConstraints != null) {
+ float min = widthInConstraints.getMin();
+ float max = widthInConstraints.getMax();
+ if (min != -1) {
+ childWidth = Math.max(min, childWidth);
+ }
+ if (max != -1) {
+ childWidth = Math.min(max, childWidth);
+ }
+ }
+ childMeasure.setW(childWidth);
+ child.measure(
+ context,
+ childMeasure.getW(),
+ childMeasure.getW(),
+ childMeasure.getH(),
+ childMeasure.getH(),
+ measure);
}
- float weight = ((LayoutComponent) child).getWidthModifier().getValue();
- childMeasure.setW((weight * availableSpace) / totalWeights);
- child.measure(
- context,
- childMeasure.getW(),
- childMeasure.getW(),
- childMeasure.getH(),
- childMeasure.getH(),
- measure);
}
}
+
+ if (applyVisibility(selfWidth, selfHeight, measure) && hasWeights) {
+ checkWeights = true;
+ }
}
childrenWidth = 0f;
@@ -363,6 +387,16 @@ public class RowLayout extends LayoutManager {
return Operations.LAYOUT_ROW;
}
+ /**
+ * Write the operation to the buffer
+ *
+ * @param buffer wire buffer
+ * @param componentId component id
+ * @param animationId animation id (-1 if not set)
+ * @param horizontalPositioning horizontal positioning rules
+ * @param verticalPositioning vertical positioning rules
+ * @param spacedBy spaced by value
+ */
public static void apply(
@NonNull WireBuffer buffer,
int componentId,
@@ -370,7 +404,7 @@ public class RowLayout extends LayoutManager {
int horizontalPositioning,
int verticalPositioning,
float spacedBy) {
- buffer.start(Operations.LAYOUT_ROW);
+ buffer.start(id());
buffer.writeInt(componentId);
buffer.writeInt(animationId);
buffer.writeInt(horizontalPositioning);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightInModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightInModifierOperation.java
new file mode 100644
index 000000000000..c19bd2f6b7c0
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightInModifierOperation.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
+
+import android.annotation.NonNull;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+import com.android.internal.widget.remotecompose.core.operations.DrawBase2;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+
+import java.util.List;
+
+/** Set the min / max height dimension on a component */
+public class HeightInModifierOperation extends DrawBase2 implements ModifierOperation {
+ private static final int OP_CODE = Operations.MODIFIER_HEIGHT_IN;
+ public static final String CLASS_NAME = "HeightInModifierOperation";
+
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+ Maker m = HeightInModifierOperation::new;
+ read(m, buffer, operations);
+ }
+
+ /**
+ * Returns the min value
+ *
+ * @return minimum value
+ */
+ public float getMin() {
+ return mV1;
+ }
+
+ /**
+ * Returns the max value
+ *
+ * @return maximum value
+ */
+ public float getMax() {
+ return mV2;
+ }
+
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
+ public static int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * The name of the class
+ *
+ * @return the name
+ */
+ @NonNull
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ @Override
+ protected void write(@NonNull WireBuffer buffer, float v1, float v2) {
+ apply(buffer, v1, v2);
+ }
+
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
+ public static void documentation(@NonNull DocumentationBuilder doc) {
+ doc.operation("Layout Operations", OP_CODE, "HeightInModifierOperation")
+ .description("Add additional constraints to the height")
+ .field(DocumentedOperation.FLOAT, "min", "The minimum height, -1 if not applied")
+ .field(DocumentedOperation.FLOAT, "max", "The maximum height, -1 if not applied");
+ }
+
+ public HeightInModifierOperation(float min, float max) {
+ super(min, max);
+ mName = CLASS_NAME;
+ }
+
+ @Override
+ public void paint(@NonNull PaintContext context) {}
+
+ /**
+ * Writes out the HeightInModifier to the buffer
+ *
+ * @param buffer buffer to write to
+ * @param x1 start x of DrawOval
+ * @param y1 start y of the DrawOval
+ */
+ public static void apply(@NonNull WireBuffer buffer, float x1, float y1) {
+ write(buffer, OP_CODE, x1, y1);
+ }
+
+ @Override
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
+ serializer.append(indent, "HEIGHT_IN = [" + getMin() + ", " + getMax() + "]");
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
index ec078a9e73ea..4b50a916b9cd 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
@@ -31,6 +31,7 @@ import java.util.List;
public class HeightModifierOperation extends DimensionModifierOperation {
private static final int OP_CODE = Operations.MODIFIER_HEIGHT;
public static final String CLASS_NAME = "HeightModifierOperation";
+ private HeightInModifierOperation mHeightIn = null;
/**
* The name of the class
@@ -110,4 +111,22 @@ public class HeightModifierOperation extends DimensionModifierOperation {
.field(INT, "type", "")
.field(FLOAT, "value", "");
}
+
+ /**
+ * Set height in constraints
+ *
+ * @param heightInConstraints height constraints
+ */
+ public void setHeightIn(HeightInModifierOperation heightInConstraints) {
+ mHeightIn = heightInConstraints;
+ }
+
+ /**
+ * Returns height in constraints
+ *
+ * @return height in constraints
+ */
+ public HeightInModifierOperation getHeightIn() {
+ return mHeightIn;
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthInModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthInModifierOperation.java
new file mode 100644
index 000000000000..c3624e5b3d88
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthInModifierOperation.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
+
+import android.annotation.NonNull;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+import com.android.internal.widget.remotecompose.core.operations.DrawBase2;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+
+import java.util.List;
+
+/** Set the min / max width dimension on a component */
+public class WidthInModifierOperation extends DrawBase2 implements ModifierOperation {
+ private static final int OP_CODE = Operations.MODIFIER_WIDTH_IN;
+ public static final String CLASS_NAME = "WidthInModifierOperation";
+
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+ Maker m = WidthInModifierOperation::new;
+ read(m, buffer, operations);
+ }
+
+ /**
+ * Returns the min value
+ *
+ * @return minimum value
+ */
+ public float getMin() {
+ return mV1;
+ }
+
+ /**
+ * Returns the max value
+ *
+ * @return maximum value
+ */
+ public float getMax() {
+ return mV2;
+ }
+
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
+ public static int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * The name of the class
+ *
+ * @return the name
+ */
+ @NonNull
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ @Override
+ protected void write(@NonNull WireBuffer buffer, float v1, float v2) {
+ apply(buffer, v1, v2);
+ }
+
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
+ public static void documentation(@NonNull DocumentationBuilder doc) {
+ doc.operation("Layout Operations", OP_CODE, "WidthInModifierOperation")
+ .description("Add additional constraints to the width")
+ .field(DocumentedOperation.FLOAT, "min", "The minimum width, -1 if not applied")
+ .field(DocumentedOperation.FLOAT, "max", "The maximum width, -1 if not applied");
+ }
+
+ public WidthInModifierOperation(float min, float max) {
+ super(min, max);
+ mName = CLASS_NAME;
+ }
+
+ @Override
+ public void paint(@NonNull PaintContext context) {}
+
+ /**
+ * Writes out the WidthInModifier to the buffer
+ *
+ * @param buffer buffer to write to
+ * @param x1 start x of DrawOval
+ * @param y1 start y of the DrawOval
+ */
+ public static void apply(@NonNull WireBuffer buffer, float x1, float y1) {
+ write(buffer, OP_CODE, x1, y1);
+ }
+
+ @Override
+ public void serializeToString(int indent, @NonNull StringSerializer serializer) {
+ serializer.append(indent, "WIDTH_IN = [" + getMin() + ", " + getMax() + "]");
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
index 05305988a49f..532027ab2087 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
@@ -31,6 +31,7 @@ import java.util.List;
public class WidthModifierOperation extends DimensionModifierOperation {
private static final int OP_CODE = Operations.MODIFIER_WIDTH;
public static final String CLASS_NAME = "WidthModifierOperation";
+ private WidthInModifierOperation mWidthIn = null;
/**
* The name of the class
@@ -110,4 +111,22 @@ public class WidthModifierOperation extends DimensionModifierOperation {
.field(INT, "type", "")
.field(FLOAT, "value", "");
}
+
+ /**
+ * Set width in constraints
+ *
+ * @param widthInConstraints width constraints
+ */
+ public void setWidthIn(WidthInModifierOperation widthInConstraints) {
+ mWidthIn = widthInConstraints;
+ }
+
+ /**
+ * Returns width in constraints
+ *
+ * @return width in constraints
+ */
+ public WidthInModifierOperation getWidthIn() {
+ return mWidthIn;
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
index b2ea0afd8fab..eb834a97c723 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
@@ -150,17 +150,47 @@ public class AnimatedFloatExpression {
/** RAND_SEED operator */
public static final float RAND_SEED = asNan(OFFSET + 40);
+ /** NOISE_FROM operator calculate a random 0..1 number based on a seed */
+ public static final float NOISE_FROM = asNan(OFFSET + 41);
+
+ /** RANDOM_IN_RANGE random number in range */
+ public static final float RAND_IN_RANGE = asNan(OFFSET + 42);
+
+ /** SQUARE_SUM the sum of the square of two numbers */
+ public static final float SQUARE_SUM = asNan(OFFSET + 43);
+
+ /** STEP x > edge ? 1 : 0; */
+ public static final float STEP = asNan(OFFSET + 44);
+
+ /** SQUARE x*x; */
+ public static final float SQUARE = asNan(OFFSET + 45);
+
+ /** DUP x,x; */
+ public static final float DUP = asNan(OFFSET + 46);
+
+ /** HYPOT sqrt(x*x+y*y); */
+ public static final float HYPOT = asNan(OFFSET + 47);
+
+ /** SWAP y,x; */
+ public static final float SWAP = asNan(OFFSET + 48);
+
+ /** LERP (1-t)*x+t*y; */
+ public static final float LERP = asNan(OFFSET + 49);
+
+ /** SMOOTH_STEP (1-smoothstep(edge0,edge1,x)); */
+ public static final float SMOOTH_STEP = asNan(OFFSET + 50);
+
/** LAST valid operator */
- public static final int LAST_OP = OFFSET + 40;
+ public static final int LAST_OP = OFFSET + 50;
/** VAR1 operator */
- public static final float VAR1 = asNan(OFFSET + 41);
+ public static final float VAR1 = asNan(OFFSET + 51);
/** VAR2 operator */
- public static final float VAR2 = asNan(OFFSET + 42);
+ public static final float VAR2 = asNan(OFFSET + 52);
/** VAR2 operator */
- public static final float VAR3 = asNan(OFFSET + 43);
+ public static final float VAR3 = asNan(OFFSET + 53);
// TODO SQUARE, DUP, HYPOT, SWAP
// private static final float FP_PI = (float) Math.PI;
@@ -399,6 +429,17 @@ public class AnimatedFloatExpression {
sNames.put(k++, "RAND");
sNames.put(k++, "RAND_SEED");
+ sNames.put(k++, "noise_from");
+ sNames.put(k++, "rand_in_range");
+ sNames.put(k++, "square_sum");
+ sNames.put(k++, "step");
+ sNames.put(k++, "square");
+ sNames.put(k++, "dup");
+ sNames.put(k++, "hypot");
+ sNames.put(k++, "swap");
+ sNames.put(k++, "lerp");
+ sNames.put(k++, "smooth_step");
+
sNames.put(k++, "a[0]");
sNames.put(k++, "a[1]");
sNames.put(k++, "a[2]");
@@ -615,9 +656,20 @@ public class AnimatedFloatExpression {
private static final int OP_RAND = OFFSET + 39;
private static final int OP_RAND_SEED = OFFSET + 40;
- private static final int OP_FIRST_VAR = OFFSET + 41;
- private static final int OP_SECOND_VAR = OFFSET + 42;
- private static final int OP_THIRD_VAR = OFFSET + 43;
+ private static final int OP_NOISE_FROM = OFFSET + 41;
+ private static final int OP_RAND_IN_RANGE = OFFSET + 42;
+ private static final int OP_SQUARE_SUM = OFFSET + 43;
+ private static final int OP_STEP = OFFSET + 44;
+ private static final int OP_SQUARE = OFFSET + 45;
+ private static final int OP_DUP = OFFSET + 46;
+ private static final int OP_HYPOT = OFFSET + 47;
+ private static final int OP_SWAP = OFFSET + 48;
+ private static final int OP_LERP = OFFSET + 49;
+ private static final int OP_SMOOTH_STEP = OFFSET + 50;
+
+ private static final int OP_FIRST_VAR = OFFSET + 51;
+ private static final int OP_SECOND_VAR = OFFSET + 52;
+ private static final int OP_THIRD_VAR = OFFSET + 53;
int opEval(int sp, int id) {
float[] array;
@@ -824,6 +876,66 @@ public class AnimatedFloatExpression {
}
}
return sp - 1;
+ case OP_NOISE_FROM:
+ int x = Float.floatToRawIntBits(mStack[sp]);
+ x = (x << 13) ^ x; // / Bitwise scrambling return
+ mStack[sp] =
+ (1.0f
+ - ((x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff)
+ / 1073741824.0f);
+ return sp;
+
+ case OP_RAND_IN_RANGE:
+ if (sRandom == null) {
+ sRandom = new Random();
+ }
+ mStack[sp] = sRandom.nextFloat() * (mStack[sp] - mStack[sp - 1]) + mStack[sp - 1];
+ return sp;
+ case OP_SQUARE_SUM:
+ mStack[sp - 1] = mStack[sp - 1] * mStack[sp - 1] + mStack[sp] * mStack[sp];
+ return sp - 1;
+ case OP_STEP:
+ System.out.println(mStack[sp] + " > " + mStack[sp - 1]);
+ mStack[sp - 1] = (mStack[sp - 1] > mStack[sp]) ? 1f : 0f;
+ return sp - 1;
+ case OP_SQUARE:
+ mStack[sp] = mStack[sp] * mStack[sp];
+ return sp;
+ case OP_DUP:
+ mStack[sp + 1] = mStack[sp];
+ return sp + 1;
+ case OP_HYPOT:
+ mStack[sp - 1] = (float) Math.hypot(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+ case OP_SWAP:
+ float swap = mStack[sp - 1];
+ mStack[sp - 1] = mStack[sp];
+ mStack[sp] = swap;
+ return sp;
+ case OP_LERP:
+ float tmp1 = mStack[sp - 2];
+ float tmp2 = mStack[sp - 1];
+ float tmp3 = mStack[sp];
+ mStack[sp - 2] = tmp1 + (tmp2 - tmp1) * tmp3;
+ return sp - 2;
+ case OP_SMOOTH_STEP:
+ float val3 = mStack[sp - 2];
+ float max2 = mStack[sp - 1];
+ float min1 = mStack[sp];
+ System.out.println("val3 = " + val3 + " min1 = " + min1 + " max2 = " + max2);
+ if (val3 < min1) {
+ mStack[sp - 2] = 0f;
+ System.out.println("below min ");
+ } else if (val3 > max2) {
+ mStack[sp - 2] = 1f;
+ System.out.println("above max ");
+
+ } else {
+ float v = (val3 - min1) / (max2 - min1);
+ System.out.println("v = " + v);
+ mStack[sp - 2] = v * v * (3 - 2 * v);
+ }
+ return sp - 2;
case OP_FIRST_VAR:
mStack[sp] = mVar[0];
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java
index d8bc83eb8a2e..2b5368297dae 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java
@@ -19,24 +19,58 @@ package com.android.internal.widget.remotecompose.core.operations.utilities.easi
public abstract class Easing {
int mType;
- /** get the value at point x */
+ /**
+ * get the value at point x
+ *
+ * @param x the position at which to get the slope
+ * @return the value at the point
+ */
public abstract float get(float x);
- /** get the slope of the easing function at at x */
+ /**
+ * get the slope of the easing function at at x
+ *
+ * @param x the position at which to get the slope
+ * @return the slope
+ */
public abstract float getDiff(float x);
+ /**
+ * get the type of easing function
+ *
+ * @return the type of easing function
+ */
public int getType() {
return mType;
}
+ /** cubic Easing function that accelerates and decelerates */
public static final int CUBIC_STANDARD = 1;
+
+ /** cubic Easing function that accelerates */
public static final int CUBIC_ACCELERATE = 2;
+
+ /** cubic Easing function that decelerates */
public static final int CUBIC_DECELERATE = 3;
+
+ /** cubic Easing function that just linearly interpolates */
public static final int CUBIC_LINEAR = 4;
+
+ /** cubic Easing function that goes bacwards and then accelerates */
public static final int CUBIC_ANTICIPATE = 5;
+
+ /** cubic Easing function that overshoots and then goes back */
public static final int CUBIC_OVERSHOOT = 6;
+
+ /** cubic Easing function that you customize */
public static final int CUBIC_CUSTOM = 11;
+
+ /** a monotonic spline Easing function that you customize */
public static final int SPLINE_CUSTOM = 12;
+
+ /** a bouncing Easing function */
public static final int EASE_OUT_BOUNCE = 13;
+
+ /** a elastic Easing function */
public static final int EASE_OUT_ELASTIC = 14;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java
index 465c95d06726..65472c262206 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java
@@ -52,16 +52,25 @@ public class FloatAnimation extends Easing {
return str;
}
- public FloatAnimation() {
- mType = CUBIC_STANDARD;
- mEasingCurve = new CubicEasing(mType);
- }
-
+ /**
+ * Create an animation based on a float encoding of the animation
+ *
+ * @param description the float encoding of the animation
+ */
public FloatAnimation(@NonNull float... description) {
mType = CUBIC_STANDARD;
setAnimationDescription(description);
}
+ /**
+ * Create an animation based on the parameters
+ *
+ * @param type The type of animation
+ * @param duration The duration of the animation
+ * @param description The float parameters describing the animation
+ * @param initialValue The initial value of the float (NaN if none)
+ * @param wrap The wrap value of the animation NaN if it does not wrap
+ */
public FloatAnimation(
int type,
float duration,
@@ -139,8 +148,8 @@ public class FloatAnimation extends Easing {
/**
* Useful to debug the packed form of an animation string
*
- * @param description
- * @return
+ * @param description the float encoding of the animation
+ * @return a string describing the animation
*/
public static String unpackAnimationToString(float[] description) {
float[] mSpec = description;
@@ -223,7 +232,7 @@ public class FloatAnimation extends Easing {
/**
* Create an animation based on a float encoding of the animation
*
- * @param description
+ * @param description the float encoding of the animation
*/
public void setAnimationDescription(@NonNull float[] description) {
mSpec = description;
@@ -288,7 +297,7 @@ public class FloatAnimation extends Easing {
/**
* Set the initial Value
*
- * @param value
+ * @param value the value to set
*/
public void setInitialValue(float value) {
@@ -321,7 +330,7 @@ public class FloatAnimation extends Easing {
/**
* Set the target value to interpolate to
*
- * @param value
+ * @param value the value to set
*/
public void setTargetValue(float value) {
mTargetValue = value;
@@ -342,6 +351,11 @@ public class FloatAnimation extends Easing {
setScaleOffset();
}
+ /**
+ * Get the target value
+ *
+ * @return the target value
+ */
public float getTargetValue() {
return mTargetValue;
}
@@ -369,6 +383,11 @@ public class FloatAnimation extends Easing {
return mEasingCurve.getDiff(t / mDuration) * (mTargetValue - mInitialValue);
}
+ /**
+ * Get the initial value
+ *
+ * @return the initial value
+ */
public float getInitialValue() {
return mInitialValue;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java
index 06969ccd1b10..960eff2e7242 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java
@@ -25,13 +25,18 @@ public class GeneralEasing extends Easing {
/**
* Set the curve based on the float encoding of it
*
- * @param data
+ * @param data the float encoding of the curve
*/
public void setCurveSpecification(@NonNull float[] data) {
mEasingData = data;
createEngine();
}
+ /**
+ * Get the float encoding of the curve
+ *
+ * @return the float encoding of the curve
+ */
public @NonNull float[] getCurveSpecification() {
return mEasingData;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java
index f4579a24fd44..01d64dff10f9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java
@@ -76,10 +76,10 @@ public class MonotonicCurveFit {
}
/**
- * Get the position of all curves at time t
+ * Get the position of all curves at position t
*
- * @param t
- * @param v
+ * @param t the point on the spline
+ * @param v the array to fill (for multiple curves)
*/
public void getPos(double t, @NonNull double[] v) {
final int n = mT.length;
@@ -136,10 +136,10 @@ public class MonotonicCurveFit {
}
/**
- * Get the position of all curves at time t
+ * Get the position of all curves at position t
*
- * @param t
- * @param v
+ * @param t the point on the spline
+ * @param v the array to fill
*/
public void getPos(double t, @NonNull float[] v) {
final int n = mT.length;
@@ -196,11 +196,11 @@ public class MonotonicCurveFit {
}
/**
- * Get the position of the jth curve at time t
+ * Get the position of the jth curve at position t
*
- * @param t
- * @param j
- * @return
+ * @param t the position
+ * @param j the curve to get
+ * @return the position
*/
public double getPos(double t, int j) {
final int n = mT.length;
@@ -240,8 +240,8 @@ public class MonotonicCurveFit {
/**
* Get the slope of all the curves at position t
*
- * @param t
- * @param v
+ * @param t the position
+ * @param v the array to fill
*/
public void getSlope(double t, @NonNull double[] v) {
final int n = mT.length;
@@ -271,9 +271,9 @@ public class MonotonicCurveFit {
/**
* Get the slope of the j curve at position t
*
- * @param t
- * @param j
- * @return
+ * @param t the position
+ * @param j the curve to get the value at
+ * @return the slope
*/
public double getSlope(double t, int j) {
final int n = mT.length;
@@ -297,6 +297,11 @@ public class MonotonicCurveFit {
return 0; // should never reach here
}
+ /**
+ * Get the time point used to create the curve
+ *
+ * @return the time points used to create the curve
+ */
public @NonNull double[] getTimePoints() {
return mT;
}
@@ -332,7 +337,12 @@ public class MonotonicCurveFit {
+ h * t1;
}
- /** This builds a monotonic spline to be used as a wave function */
+ /**
+ * This builds a monotonic spline to be used as a wave function
+ *
+ * @param configString the configuration string
+ * @return the curve
+ */
@NonNull
public static MonotonicCurveFit buildWave(@NonNull String configString) {
// done this way for efficiency
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicSpline.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicSpline.java
index 23a664336c5f..8bb7dae2fd6a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicSpline.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicSpline.java
@@ -76,6 +76,11 @@ public class MonotonicSpline {
mTangent = tangent;
}
+ /**
+ * Get the value point used in the interpolator.
+ *
+ * @return the value points
+ */
public float[] getArray() {
return mY;
}
@@ -83,7 +88,7 @@ public class MonotonicSpline {
/**
* Get the position of all curves at time t
*
- * @param t
+ * @param t the position along spline
* @return position at t
*/
public float getPos(float t) {
@@ -139,7 +144,7 @@ public class MonotonicSpline {
/**
* Get the slope of the curve at position t
*
- * @param t
+ * @param t the position along spline
* @return slope at t
*/
public float getSlope(float t) {
@@ -167,6 +172,11 @@ public class MonotonicSpline {
return v;
}
+ /**
+ * Get the time points used in the interpolator.
+ *
+ * @return the time points
+ */
public float[] getTimePoints() {
return mT;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/SpringStopEngine.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/SpringStopEngine.java
index 03e45031e515..2f1379b3e9fc 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/SpringStopEngine.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/SpringStopEngine.java
@@ -42,9 +42,9 @@ public class SpringStopEngine {
private float mStopThreshold;
private int mBoundaryMode = 0;
- public String debug(String desc, float time) {
- return null;
- }
+ // public String debug(String desc, float time) {
+ // return null;
+ // }
void log(String str) {
StackTraceElement s = new Throwable().getStackTrace()[1];
@@ -53,20 +53,41 @@ public class SpringStopEngine {
System.out.println(line + str);
}
+ /** */
public SpringStopEngine() {}
+ /**
+ * get the value the sping is pulling towards
+ *
+ * @return the value the sping is pulling towards
+ */
public float getTargetValue() {
return (float) mTargetPos;
}
+ /**
+ * get the value the sping is starting from
+ *
+ * @param v the value the sping is starting from
+ */
public void setInitialValue(float v) {
mPos = v;
}
+ /**
+ * set the value the sping is pulling towards
+ *
+ * @param v the value the sping is pulling towards
+ */
public void setTargetValue(float v) {
mTargetPos = v;
}
+ /**
+ * Create a sping engine with the parameters encoded as an array of floats
+ *
+ * @param parameters the parameters to use
+ */
public SpringStopEngine(float[] parameters) {
if (parameters[0] != 0) {
throw new RuntimeException(" parameter[0] should be 0");
@@ -83,9 +104,9 @@ public class SpringStopEngine {
/**
* Config the spring starting conditions
*
- * @param currentPos
- * @param target
- * @param currentVelocity
+ * @param currentPos the current position of the spring
+ * @param target the target position of the spring
+ * @param currentVelocity the current velocity of the spring
*/
public void springStart(float currentPos, float target, float currentVelocity) {
mTargetPos = target;
@@ -115,10 +136,22 @@ public class SpringStopEngine {
mLastTime = 0;
}
+ /**
+ * get the velocity of the spring at a time
+ *
+ * @param time the time to get the velocity at
+ * @return the velocity of the spring at a time
+ */
public float getVelocity(float time) {
return (float) mV;
}
+ /**
+ * get the position of the spring at a time
+ *
+ * @param time the time to get the position at
+ * @return the position of the spring at a time
+ */
public float get(float time) {
compute(time - mLastTime);
mLastTime = time;
@@ -128,6 +161,11 @@ public class SpringStopEngine {
return (float) mPos;
}
+ /**
+ * get the acceleration of the spring
+ *
+ * @return the acceleration of the spring
+ */
public float getAcceleration() {
double k = mStiffness;
double c = mDamping;
@@ -135,10 +173,20 @@ public class SpringStopEngine {
return (float) (-k * x - c * mV) / mMass;
}
+ /**
+ * get the velocity of the spring
+ *
+ * @return the velocity of the spring
+ */
public float getVelocity() {
return 0;
}
+ /**
+ * is the spring stopped
+ *
+ * @return true if the spring is stopped
+ */
public boolean isStopped() {
double x = (mPos - mTargetPos);
double k = mStiffness;
@@ -149,6 +197,11 @@ public class SpringStopEngine {
return max_def <= mStopThreshold;
}
+ /**
+ * increment the spring position over time dt
+ *
+ * @param dt the time to increment the spring position over
+ */
private void compute(double dt) {
if (dt <= 0) {
// Nothing to compute if there's no time difference
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java
index b1eb8041b0b3..376e1e9179f4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java
@@ -26,6 +26,13 @@ public class StepCurve extends Easing {
// private static final boolean DEBUG = false;
@NonNull private final MonotonicCurveFit mCurveFit;
+ /**
+ * Create a step curve from a series of values
+ *
+ * @param params the series of values to ease over
+ * @param offset the offset into the array
+ * @param len the length of the array to use
+ */
public StepCurve(@NonNull float[] params, int offset, int len) {
mCurveFit = genSpline(params, offset, len);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
index 5de11a19799d..b17e3dc82d50 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
@@ -66,6 +66,28 @@ public class RemoteComposePlayer extends FrameLayout {
}
/**
+ * @inheritDoc
+ */
+ public void requestLayout() {
+ super.requestLayout();
+
+ if (mInner != null) {
+ mInner.requestLayout();
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public void invalidate() {
+ super.invalidate();
+
+ if (mInner != null) {
+ mInner.invalidate();
+ }
+ }
+
+ /**
* Returns true if the document supports drag touch events
*
* @return true if draggable content, false otherwise
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
index 970cc4a44672..334ba62636ff 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
@@ -48,7 +48,7 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
boolean mHasClickAreas = false;
Point mActionDownPoint = new Point(0, 0);
AndroidRemoteContext mARContext = new AndroidRemoteContext();
- float mDensity = 1f;
+ float mDensity = Float.NaN;
long mStart = System.nanoTime();
long mLastFrameDelay = 1;
@@ -68,24 +68,18 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
public RemoteComposeCanvas(Context context) {
super(context);
- if (USE_VIEW_AREA_CLICK) {
- addOnAttachStateChangeListener(this);
- }
+ addOnAttachStateChangeListener(this);
}
public RemoteComposeCanvas(Context context, AttributeSet attrs) {
super(context, attrs);
- if (USE_VIEW_AREA_CLICK) {
- addOnAttachStateChangeListener(this);
- }
+ addOnAttachStateChangeListener(this);
}
public RemoteComposeCanvas(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setBackgroundColor(Color.WHITE);
- if (USE_VIEW_AREA_CLICK) {
- addOnAttachStateChangeListener(this);
- }
+ addOnAttachStateChangeListener(this);
}
public void setDebug(int value) {
@@ -124,6 +118,7 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
mChoreographer.postFrameCallback(mFrameCallback);
}
mDensity = getContext().getResources().getDisplayMetrics().density;
+ mARContext.setDensity(mDensity);
if (mDocument == null) {
return;
}
diff --git a/core/jni/android_os_PerfettoTrace.cpp b/core/jni/android_os_PerfettoTrace.cpp
index 988aea722be3..962aefc482e4 100644
--- a/core/jni/android_os_PerfettoTrace.cpp
+++ b/core/jni/android_os_PerfettoTrace.cpp
@@ -23,6 +23,7 @@
#include <nativehelper/scoped_local_ref.h>
#include <nativehelper/scoped_primitive_array.h>
#include <nativehelper/scoped_utf_chars.h>
+#include <nativehelper/utils.h>
#include <tracing_sdk.h>
namespace android {
@@ -36,30 +37,6 @@ inline static jlong toJLong(T* ptr) {
return static_cast<jlong>(reinterpret_cast<uintptr_t>(ptr));
}
-static const char* fromJavaString(JNIEnv* env, jstring jstr) {
- if (!jstr) return "";
- ScopedUtfChars chars(env, jstr);
-
- if (!chars.c_str()) {
- ALOGE("Failed extracting string");
- return "";
- }
-
- return chars.c_str();
-}
-
-static void android_os_PerfettoTrace_event(JNIEnv* env, jclass, jint type, jlong cat_ptr,
- jstring name, jlong extra_ptr) {
- ScopedUtfChars name_utf(env, name);
- if (!name_utf.c_str()) {
- ALOGE("Failed extracting string");
- }
-
- tracing_perfetto::Category* category = toPointer<tracing_perfetto::Category>(cat_ptr);
- tracing_perfetto::trace_event(type, category->get(), name_utf.c_str(),
- toPointer<tracing_perfetto::Extra>(extra_ptr));
-}
-
static jlong android_os_PerfettoTrace_get_process_track_uuid() {
return tracing_perfetto::get_process_track_uuid();
}
@@ -70,20 +47,18 @@ static jlong android_os_PerfettoTrace_get_thread_track_uuid(jlong tid) {
static void android_os_PerfettoTrace_activate_trigger(JNIEnv* env, jclass, jstring name,
jint ttl_ms) {
- ScopedUtfChars name_utf(env, name);
- if (!name_utf.c_str()) {
- ALOGE("Failed extracting string");
- return;
- }
-
- tracing_perfetto::activate_trigger(name_utf.c_str(), static_cast<uint32_t>(ttl_ms));
+ ScopedUtfChars name_chars = GET_UTF_OR_RETURN_VOID(env, name);
+ tracing_perfetto::activate_trigger(name_chars.c_str(), static_cast<uint32_t>(ttl_ms));
}
static jlong android_os_PerfettoTraceCategory_init(JNIEnv* env, jclass, jstring name, jstring tag,
jstring severity) {
- return toJLong(new tracing_perfetto::Category(fromJavaString(env, name),
- fromJavaString(env, tag),
- fromJavaString(env, severity)));
+ ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
+ ScopedUtfChars tag_chars = GET_UTF_OR_RETURN(env, tag);
+ ScopedUtfChars severity_chars = GET_UTF_OR_RETURN(env, severity);
+
+ return toJLong(new tracing_perfetto::Category(name_chars.c_str(), tag_chars.c_str(),
+ severity_chars.c_str()));
}
static jlong android_os_PerfettoTraceCategory_delete() {
@@ -121,8 +96,7 @@ static const JNINativeMethod gCategoryMethods[] = {
};
static const JNINativeMethod gTraceMethods[] =
- {{"native_event", "(IJLjava/lang/String;J)V", (void*)android_os_PerfettoTrace_event},
- {"native_get_process_track_uuid", "()J",
+ {{"native_get_process_track_uuid", "()J",
(void*)android_os_PerfettoTrace_get_process_track_uuid},
{"native_get_thread_track_uuid", "(J)J",
(void*)android_os_PerfettoTrace_get_thread_track_uuid},
@@ -132,10 +106,11 @@ static const JNINativeMethod gTraceMethods[] =
int register_android_os_PerfettoTrace(JNIEnv* env) {
int res = jniRegisterNativeMethods(env, "android/os/PerfettoTrace", gTraceMethods,
NELEM(gTraceMethods));
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register perfetto native methods.");
res = jniRegisterNativeMethods(env, "android/os/PerfettoTrace$Category", gCategoryMethods,
NELEM(gCategoryMethods));
- LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
+ LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register category native methods.");
return 0;
}
diff --git a/core/jni/android_os_PerfettoTrackEventExtra.cpp b/core/jni/android_os_PerfettoTrackEventExtra.cpp
index 9adad7bca940..b8bdc8c29199 100644
--- a/core/jni/android_os_PerfettoTrackEventExtra.cpp
+++ b/core/jni/android_os_PerfettoTrackEventExtra.cpp
@@ -20,6 +20,7 @@
#include <log/log.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/scoped_utf_chars.h>
+#include <nativehelper/utils.h>
#include <tracing_sdk.h>
static constexpr ssize_t kMaxStrLen = 4096;
@@ -34,32 +35,24 @@ inline static jlong toJLong(T* ptr) {
return static_cast<jlong>(reinterpret_cast<uintptr_t>(ptr));
}
-static const char* fromJavaString(JNIEnv* env, jstring jstr) {
- if (!jstr) return "";
- ScopedUtfChars chars(env, jstr);
-
- if (!chars.c_str()) {
- ALOGE("Failed extracting string");
- return "";
- }
-
- return chars.c_str();
-}
-
static jlong android_os_PerfettoTrackEventExtraArgInt64_init(JNIEnv* env, jclass, jstring name) {
- return toJLong(new tracing_perfetto::DebugArg<int64_t>(fromJavaString(env, name)));
+ ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
+ return toJLong(new tracing_perfetto::DebugArg<int64_t>(name_chars.c_str()));
}
static jlong android_os_PerfettoTrackEventExtraArgBool_init(JNIEnv* env, jclass, jstring name) {
- return toJLong(new tracing_perfetto::DebugArg<bool>(fromJavaString(env, name)));
+ ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
+ return toJLong(new tracing_perfetto::DebugArg<bool>(name_chars.c_str()));
}
static jlong android_os_PerfettoTrackEventExtraArgDouble_init(JNIEnv* env, jclass, jstring name) {
- return toJLong(new tracing_perfetto::DebugArg<double>(fromJavaString(env, name)));
+ ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
+ return toJLong(new tracing_perfetto::DebugArg<double>(name_chars.c_str()));
}
static jlong android_os_PerfettoTrackEventExtraArgString_init(JNIEnv* env, jclass, jstring name) {
- return toJLong(new tracing_perfetto::DebugArg<const char*>(fromJavaString(env, name)));
+ ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
+ return toJLong(new tracing_perfetto::DebugArg<const char*>(name_chars.c_str()));
}
static jlong android_os_PerfettoTrackEventExtraArgInt64_delete() {
@@ -116,9 +109,11 @@ static void android_os_PerfettoTrackEventExtraArgDouble_set_value(jlong ptr, jdo
static void android_os_PerfettoTrackEventExtraArgString_set_value(JNIEnv* env, jclass, jlong ptr,
jstring val) {
+ ScopedUtfChars val_chars = GET_UTF_OR_RETURN_VOID(env, val);
+
tracing_perfetto::DebugArg<const char*>* arg =
toPointer<tracing_perfetto::DebugArg<const char*>>(ptr);
- arg->set_value(strdup(fromJavaString(env, val)));
+ arg->set_value(strdup(val_chars.c_str()));
}
static jlong android_os_PerfettoTrackEventExtraFieldInt64_init() {
@@ -191,9 +186,11 @@ static void android_os_PerfettoTrackEventExtraFieldDouble_set_value(jlong ptr, j
static void android_os_PerfettoTrackEventExtraFieldString_set_value(JNIEnv* env, jclass, jlong ptr,
jlong id, jstring val) {
+ ScopedUtfChars val_chars = GET_UTF_OR_RETURN_VOID(env, val);
+
tracing_perfetto::ProtoField<const char*>* field =
toPointer<tracing_perfetto::ProtoField<const char*>>(ptr);
- field->set_value(id, strdup(fromJavaString(env, val)));
+ field->set_value(id, strdup(val_chars.c_str()));
}
static void android_os_PerfettoTrackEventExtraFieldNested_add_field(jlong field_ptr,
@@ -234,7 +231,8 @@ static jlong android_os_PerfettoTrackEventExtraFlow_get_extra_ptr(jlong ptr) {
static jlong android_os_PerfettoTrackEventExtraNamedTrack_init(JNIEnv* env, jclass, jlong id,
jstring name, jlong parent_uuid) {
- return toJLong(new tracing_perfetto::NamedTrack(id, parent_uuid, fromJavaString(env, name)));
+ ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
+ return toJLong(new tracing_perfetto::NamedTrack(id, parent_uuid, name_chars.c_str()));
}
static jlong android_os_PerfettoTrackEventExtraNamedTrack_delete() {
@@ -248,8 +246,9 @@ static jlong android_os_PerfettoTrackEventExtraNamedTrack_get_extra_ptr(jlong pt
static jlong android_os_PerfettoTrackEventExtraCounterTrack_init(JNIEnv* env, jclass, jstring name,
jlong parent_uuid) {
- return toJLong(
- new tracing_perfetto::RegisteredTrack(1, parent_uuid, fromJavaString(env, name), true));
+ ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
+
+ return toJLong(new tracing_perfetto::RegisteredTrack(1, parent_uuid, name_chars.c_str(), true));
}
static jlong android_os_PerfettoTrackEventExtraCounterTrack_delete() {
@@ -317,6 +316,15 @@ static void android_os_PerfettoTrackEventExtra_clear_args(jlong ptr) {
extra->clear_extras();
}
+static void android_os_PerfettoTrackEventExtra_emit(JNIEnv* env, jclass, jint type, jlong cat_ptr,
+ jstring name, jlong extra_ptr) {
+ ScopedUtfChars name_chars = GET_UTF_OR_RETURN_VOID(env, name);
+
+ tracing_perfetto::Category* category = toPointer<tracing_perfetto::Category>(cat_ptr);
+ tracing_perfetto::trace_event(type, category->get(), name_chars.c_str(),
+ toPointer<tracing_perfetto::Extra>(extra_ptr));
+}
+
static jlong android_os_PerfettoTrackEventExtraProto_init() {
return toJLong(new tracing_perfetto::Proto());
}
@@ -344,7 +352,9 @@ static const JNINativeMethod gExtraMethods[] =
{{"native_init", "()J", (void*)android_os_PerfettoTrackEventExtra_init},
{"native_delete", "()J", (void*)android_os_PerfettoTrackEventExtra_delete},
{"native_add_arg", "(JJ)V", (void*)android_os_PerfettoTrackEventExtra_add_arg},
- {"native_clear_args", "(J)V", (void*)android_os_PerfettoTrackEventExtra_clear_args}};
+ {"native_clear_args", "(J)V", (void*)android_os_PerfettoTrackEventExtra_clear_args},
+ {"native_emit", "(IJLjava/lang/String;J)V",
+ (void*)android_os_PerfettoTrackEventExtra_emit}};
static const JNINativeMethod gProtoMethods[] =
{{"native_init", "()J", (void*)android_os_PerfettoTrackEventExtraProto_init},
diff --git a/core/tests/coretests/src/android/os/PerfettoTraceTest.java b/core/tests/coretests/src/android/os/PerfettoTraceTest.java
index 292f7500479b..ad28383689af 100644
--- a/core/tests/coretests/src/android/os/PerfettoTraceTest.java
+++ b/core/tests/coretests/src/android/os/PerfettoTraceTest.java
@@ -112,15 +112,14 @@ public class PerfettoTraceTest {
long ptr = nativeStartTracing(traceConfig.toByteArray());
- PerfettoTrackEventExtra extra = PerfettoTrackEventExtra.builder()
+ PerfettoTrace.instant(FOO_CATEGORY, "event")
.addFlow(2)
.addTerminatingFlow(3)
.addArg("long_val", 10000000000L)
.addArg("bool_val", true)
.addArg("double_val", 3.14)
.addArg("string_val", FOO)
- .build();
- PerfettoTrace.instant(FOO_CATEGORY, "event", extra);
+ .emit();
byte[] traceBytes = nativeStopTracing(ptr);
@@ -163,12 +162,12 @@ public class PerfettoTraceTest {
@Test
@RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
- public void testDebugAnnotationsWithLamda() throws Exception {
+ public void testDebugAnnotationsWithLambda() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
long ptr = nativeStartTracing(traceConfig.toByteArray());
- PerfettoTrace.instant(FOO_CATEGORY, "event", e -> e.addArg("long_val", 123L));
+ PerfettoTrace.instant(FOO_CATEGORY, "event").addArg("long_val", 123L).emit();
byte[] traceBytes = nativeStopTracing(ptr);
@@ -203,15 +202,14 @@ public class PerfettoTraceTest {
long ptr = nativeStartTracing(traceConfig.toByteArray());
- PerfettoTrackEventExtra beginExtra = PerfettoTrackEventExtra.builder()
- .usingNamedTrack(FOO, PerfettoTrace.getProcessTrackUuid())
- .build();
- PerfettoTrace.begin(FOO_CATEGORY, "event", beginExtra);
+ PerfettoTrace.begin(FOO_CATEGORY, "event")
+ .usingNamedTrack(PerfettoTrace.getProcessTrackUuid(), FOO)
+ .emit();
- PerfettoTrackEventExtra endExtra = PerfettoTrackEventExtra.builder()
- .usingNamedTrack("bar", PerfettoTrace.getThreadTrackUuid(Process.myTid()))
- .build();
- PerfettoTrace.end(FOO_CATEGORY, endExtra);
+
+ PerfettoTrace.end(FOO_CATEGORY)
+ .usingNamedTrack(PerfettoTrace.getThreadTrackUuid(Process.myTid()), "bar")
+ .emit();
Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
@@ -242,26 +240,67 @@ public class PerfettoTraceTest {
assertThat(hasTrackUuid).isTrue();
assertThat(mCategoryNames).contains(FOO);
assertThat(mTrackNames).contains(FOO);
+ assertThat(mTrackNames).contains("bar");
}
@Test
@RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
- public void testCounter() throws Exception {
+ public void testProcessThreadNamedTrack() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
long ptr = nativeStartTracing(traceConfig.toByteArray());
- PerfettoTrackEventExtra intExtra = PerfettoTrackEventExtra.builder()
- .usingCounterTrack(FOO, PerfettoTrace.getProcessTrackUuid())
- .setCounter(16)
- .build();
- PerfettoTrace.counter(FOO_CATEGORY, intExtra);
+ PerfettoTrace.begin(FOO_CATEGORY, "event")
+ .usingProcessNamedTrack(FOO)
+ .emit();
- PerfettoTrackEventExtra doubleExtra = PerfettoTrackEventExtra.builder()
- .usingCounterTrack("bar", PerfettoTrace.getProcessTrackUuid())
- .setCounter(3.14)
- .build();
- PerfettoTrace.counter(FOO_CATEGORY, doubleExtra);
+
+ PerfettoTrace.end(FOO_CATEGORY)
+ .usingThreadNamedTrack(Process.myTid(), "%s-%s", "bar", "stool")
+ .emit();
+
+ Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
+
+ boolean hasTrackEvent = false;
+ boolean hasTrackUuid = false;
+ for (TracePacket packet: trace.getPacketList()) {
+ TrackEvent event;
+ if (packet.hasTrackEvent()) {
+ hasTrackEvent = true;
+ event = packet.getTrackEvent();
+
+ if (TrackEvent.Type.TYPE_SLICE_BEGIN.equals(event.getType())
+ && event.hasTrackUuid()) {
+ hasTrackUuid = true;
+ }
+
+ if (TrackEvent.Type.TYPE_SLICE_END.equals(event.getType())
+ && event.hasTrackUuid()) {
+ hasTrackUuid &= true;
+ }
+ }
+
+ collectInternedData(packet);
+ collectTrackNames(packet);
+ }
+
+ assertThat(hasTrackEvent).isTrue();
+ assertThat(hasTrackUuid).isTrue();
+ assertThat(mCategoryNames).contains(FOO);
+ assertThat(mTrackNames).contains(FOO);
+ assertThat(mTrackNames).contains("bar-stool");
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
+ public void testCounterSimple() throws Exception {
+ TraceConfig traceConfig = getTraceConfig(FOO);
+
+ long ptr = nativeStartTracing(traceConfig.toByteArray());
+
+ PerfettoTrace.counter(FOO_CATEGORY, 16, FOO).emit();
+
+ PerfettoTrace.counter(FOO_CATEGORY, 3.14, "bar").emit();
Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
@@ -297,12 +336,102 @@ public class PerfettoTraceTest {
@Test
@RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
+ public void testCounter() throws Exception {
+ TraceConfig traceConfig = getTraceConfig(FOO);
+
+ long ptr = nativeStartTracing(traceConfig.toByteArray());
+
+ PerfettoTrace.counter(FOO_CATEGORY, 16)
+ .usingCounterTrack(PerfettoTrace.getProcessTrackUuid(), FOO).emit();
+
+ PerfettoTrace.counter(FOO_CATEGORY, 3.14)
+ .usingCounterTrack(PerfettoTrace.getThreadTrackUuid(Process.myTid()),
+ "%s-%s", "bar", "stool").emit();
+
+ Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
+
+ boolean hasTrackEvent = false;
+ boolean hasCounterValue = false;
+ boolean hasDoubleCounterValue = false;
+ for (TracePacket packet: trace.getPacketList()) {
+ TrackEvent event;
+ if (packet.hasTrackEvent()) {
+ hasTrackEvent = true;
+ event = packet.getTrackEvent();
+
+ if (TrackEvent.Type.TYPE_COUNTER.equals(event.getType())
+ && event.getCounterValue() == 16) {
+ hasCounterValue = true;
+ }
+
+ if (TrackEvent.Type.TYPE_COUNTER.equals(event.getType())
+ && event.getDoubleCounterValue() == 3.14) {
+ hasDoubleCounterValue = true;
+ }
+ }
+
+ collectTrackNames(packet);
+ }
+
+ assertThat(hasTrackEvent).isTrue();
+ assertThat(hasCounterValue).isTrue();
+ assertThat(hasDoubleCounterValue).isTrue();
+ assertThat(mTrackNames).contains(FOO);
+ assertThat(mTrackNames).contains("bar-stool");
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
+ public void testProcessThreadCounter() throws Exception {
+ TraceConfig traceConfig = getTraceConfig(FOO);
+
+ long ptr = nativeStartTracing(traceConfig.toByteArray());
+
+ PerfettoTrace.counter(FOO_CATEGORY, 16).usingProcessCounterTrack(FOO).emit();
+
+ PerfettoTrace.counter(FOO_CATEGORY, 3.14)
+ .usingThreadCounterTrack(Process.myTid(), "%s-%s", "bar", "stool").emit();
+
+ Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
+
+ boolean hasTrackEvent = false;
+ boolean hasCounterValue = false;
+ boolean hasDoubleCounterValue = false;
+ for (TracePacket packet: trace.getPacketList()) {
+ TrackEvent event;
+ if (packet.hasTrackEvent()) {
+ hasTrackEvent = true;
+ event = packet.getTrackEvent();
+
+ if (TrackEvent.Type.TYPE_COUNTER.equals(event.getType())
+ && event.getCounterValue() == 16) {
+ hasCounterValue = true;
+ }
+
+ if (TrackEvent.Type.TYPE_COUNTER.equals(event.getType())
+ && event.getDoubleCounterValue() == 3.14) {
+ hasDoubleCounterValue = true;
+ }
+ }
+
+ collectTrackNames(packet);
+ }
+
+ assertThat(hasTrackEvent).isTrue();
+ assertThat(hasCounterValue).isTrue();
+ assertThat(hasDoubleCounterValue).isTrue();
+ assertThat(mTrackNames).contains(FOO);
+ assertThat(mTrackNames).contains("bar-stool");
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
public void testProto() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
long ptr = nativeStartTracing(traceConfig.toByteArray());
- PerfettoTrackEventExtra extra5 = PerfettoTrackEventExtra.builder()
+ PerfettoTrace.instant(FOO_CATEGORY, "event_proto")
.beginProto()
.beginNested(33L)
.addField(4L, 2L)
@@ -310,8 +439,7 @@ public class PerfettoTraceTest {
.endNested()
.addField(2001, "AIDL::IActivityManager")
.endProto()
- .build();
- PerfettoTrace.instant(FOO_CATEGORY, "event_proto", extra5);
+ .emit();
byte[] traceBytes = nativeStopTracing(ptr);
@@ -351,7 +479,7 @@ public class PerfettoTraceTest {
long ptr = nativeStartTracing(traceConfig.toByteArray());
- PerfettoTrackEventExtra extra6 = PerfettoTrackEventExtra.builder()
+ PerfettoTrace.instant(FOO_CATEGORY, "event_proto_nested")
.beginProto()
.beginNested(29L)
.beginNested(4L)
@@ -364,8 +492,7 @@ public class PerfettoTraceTest {
.endNested()
.endNested()
.endProto()
- .build();
- PerfettoTrace.instant(FOO_CATEGORY, "event_proto_nested", extra6);
+ .emit();
byte[] traceBytes = nativeStopTracing(ptr);
@@ -413,8 +540,7 @@ public class PerfettoTraceTest {
long ptr = nativeStartTracing(traceConfig.toByteArray());
- PerfettoTrackEventExtra extra = PerfettoTrackEventExtra.builder().build();
- PerfettoTrace.instant(FOO_CATEGORY, "event_trigger", extra);
+ PerfettoTrace.instant(FOO_CATEGORY, "event_trigger").emit();
PerfettoTrace.activateTrigger(FOO, 1000);
@@ -439,49 +565,21 @@ public class PerfettoTraceTest {
@Test
@RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
- public void testMultipleExtras() throws Exception {
- boolean hasException = false;
- try {
- PerfettoTrackEventExtra.builder();
-
- // Unclosed extra will throw an exception here
- PerfettoTrackEventExtra.builder();
- } catch (Exception e) {
- hasException = true;
- }
-
- try {
- PerfettoTrackEventExtra.builder().build();
-
- // Closed extra but unused (reset hasn't been called internally) will throw an exception
- // here.
- PerfettoTrackEventExtra.builder();
- } catch (Exception e) {
- hasException &= true;
- }
-
- assertThat(hasException).isTrue();
- }
-
- @Test
- @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
public void testRegister() throws Exception {
TraceConfig traceConfig = getTraceConfig(BAR);
Category barCategory = new Category(BAR);
long ptr = nativeStartTracing(traceConfig.toByteArray());
- PerfettoTrackEventExtra beforeExtra = PerfettoTrackEventExtra.builder()
+ PerfettoTrace.instant(barCategory, "event")
.addArg("before", 1)
- .build();
- PerfettoTrace.instant(barCategory, "event", beforeExtra);
+ .emit();
barCategory.register();
- PerfettoTrackEventExtra afterExtra = PerfettoTrackEventExtra.builder()
+ PerfettoTrace.instant(barCategory, "event")
.addArg("after", 1)
- .build();
- PerfettoTrace.instant(barCategory, "event", afterExtra);
+ .emit();
byte[] traceBytes = nativeStopTracing(ptr);
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 e8add56619c4..408160d30467 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
@@ -1154,9 +1154,12 @@ public abstract class WMShellModule {
Context context,
ShellInit shellInit,
Transitions transitions,
- DesktopModeEventLogger desktopModeEventLogger) {
+ DesktopModeEventLogger desktopModeEventLogger,
+ Optional<DesktopTasksLimiter> desktopTasksLimiter,
+ ShellTaskOrganizer shellTaskOrganizer) {
return new DesktopModeLoggerTransitionObserver(
- context, shellInit, transitions, desktopModeEventLogger);
+ context, shellInit, transitions, desktopModeEventLogger,
+ desktopTasksLimiter, shellTaskOrganizer);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
index e8f9a789bb98..68bdbd1758b3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
@@ -467,9 +467,13 @@ class DesktopModeEventLogger {
FrameworkStatsLog
.DESKTOP_MODE_SESSION_TASK_UPDATE__MINIMIZE_REASON__MINIMIZE_TASK_LIMIT
),
- MINIMIZE_BUTTON( // TODO(b/356843241): use this enum value
+ MINIMIZE_BUTTON(
FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__MINIMIZE_REASON__MINIMIZE_BUTTON
),
+ KEY_GESTURE(
+ FrameworkStatsLog
+ .DESKTOP_MODE_SESSION_TASK_UPDATE__MINIMIZE_REASON__MINIMIZE_KEY_GESTURE
+ ),
}
// Default value used when the task was not unminimized.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
index 1ddb834399cb..9334898fdb93 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
@@ -30,6 +30,7 @@ import com.android.window.flags.Flags.enableTaskResizingKeyboardShortcuts
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.annotations.ShellMainThread
@@ -125,7 +126,9 @@ class DesktopModeKeyGestureHandler(
KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW -> {
logV("Key gesture MINIMIZE_FREEFORM_WINDOW is handled")
getGloballyFocusedFreeformTask()?.let {
- mainExecutor.execute { desktopTasksController.get().minimizeTask(it) }
+ mainExecutor.execute {
+ desktopTasksController.get().minimizeTask(it, MinimizeReason.KEY_GESTURE)
+ }
}
return true
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index c09504ee3725..2dd89c790b58 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
@@ -36,6 +36,7 @@ import androidx.core.util.isNotEmpty
import androidx.core.util.plus
import androidx.core.util.putAll
import com.android.internal.protolog.ProtoLog
+import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
@@ -52,6 +53,8 @@ import com.android.wm.shell.shared.TransitionUtil
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
+import java.util.Optional
+import kotlin.jvm.optionals.getOrNull
/**
* A [Transitions.TransitionObserver] that observes transitions and the proposed changes to log
@@ -63,6 +66,8 @@ class DesktopModeLoggerTransitionObserver(
shellInit: ShellInit,
private val transitions: Transitions,
private val desktopModeEventLogger: DesktopModeEventLogger,
+ private val desktopTasksLimiter: Optional<DesktopTasksLimiter>,
+ private val shellTaskOrganizer: ShellTaskOrganizer,
) : Transitions.TransitionObserver {
init {
@@ -141,6 +146,7 @@ class DesktopModeLoggerTransitionObserver(
// identify if we need to log any changes and update the state of visible freeform tasks
identifyLogEventAndUpdateState(
+ transition = transition,
transitionInfo = info,
preTransitionVisibleFreeformTasks = visibleFreeformTaskInfos,
postTransitionVisibleFreeformTasks = postTransitionVisibleFreeformTasks,
@@ -227,6 +233,7 @@ class DesktopModeLoggerTransitionObserver(
* state and update it
*/
private fun identifyLogEventAndUpdateState(
+ transition: IBinder,
transitionInfo: TransitionInfo,
preTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
postTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
@@ -238,6 +245,7 @@ class DesktopModeLoggerTransitionObserver(
) {
// Sessions is finishing, log task updates followed by an exit event
identifyAndLogTaskUpdates(
+ transition,
transitionInfo,
preTransitionVisibleFreeformTasks,
postTransitionVisibleFreeformTasks,
@@ -255,6 +263,7 @@ class DesktopModeLoggerTransitionObserver(
desktopModeEventLogger.logSessionEnter(getEnterReason(transitionInfo))
identifyAndLogTaskUpdates(
+ transition,
transitionInfo,
preTransitionVisibleFreeformTasks,
postTransitionVisibleFreeformTasks,
@@ -262,6 +271,7 @@ class DesktopModeLoggerTransitionObserver(
} else if (isSessionActive) {
// Session is neither starting, nor finishing, log task updates if there are any
identifyAndLogTaskUpdates(
+ transition,
transitionInfo,
preTransitionVisibleFreeformTasks,
postTransitionVisibleFreeformTasks,
@@ -275,6 +285,7 @@ class DesktopModeLoggerTransitionObserver(
/** Compare the old and new state of taskInfos and identify and log the changes */
private fun identifyAndLogTaskUpdates(
+ transition: IBinder,
transitionInfo: TransitionInfo,
preTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
postTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
@@ -310,12 +321,9 @@ class DesktopModeLoggerTransitionObserver(
// find old tasks that were removed
preTransitionVisibleFreeformTasks.forEach { taskId, taskInfo ->
if (!postTransitionVisibleFreeformTasks.containsKey(taskId)) {
- val minimizeReason =
- if (transitionInfo.type == Transitions.TRANSIT_MINIMIZE) {
- MinimizeReason.MINIMIZE_BUTTON
- } else {
- null
- }
+ // The task is no longer visible, it might have been minimized, get the minimize
+ // reason (if any)
+ val minimizeReason = getMinimizeReason(transition, transitionInfo, taskInfo)
val taskUpdate =
buildTaskUpdateForTask(
taskInfo,
@@ -336,6 +344,21 @@ class DesktopModeLoggerTransitionObserver(
}
}
+ private fun getMinimizeReason(
+ transition: IBinder,
+ transitionInfo: TransitionInfo,
+ taskInfo: TaskInfo,
+ ): MinimizeReason? {
+ if (transitionInfo.type == Transitions.TRANSIT_MINIMIZE) {
+ return MinimizeReason.MINIMIZE_BUTTON
+ }
+ val minimizingTask = desktopTasksLimiter.getOrNull()?.getMinimizingTask(transition)
+ if (minimizingTask?.taskId == taskInfo.taskId) {
+ return minimizingTask.minimizeReason
+ }
+ return null
+ }
+
private fun buildTaskUpdateForTask(
taskInfo: TaskInfo,
visibleTasks: Int,
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 c975533abf24..fa696682de28 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
@@ -24,8 +24,8 @@ import android.util.SparseArray
import android.view.Display.INVALID_DISPLAY
import android.window.DesktopModeFlags
import androidx.core.util.forEach
-import androidx.core.util.keyIterator
import androidx.core.util.valueIterator
+import com.android.internal.annotations.VisibleForTesting
import com.android.internal.protolog.ProtoLog
import com.android.window.flags.Flags
import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
@@ -43,26 +43,36 @@ class DesktopRepository(
@ShellMainThread private val mainCoroutineScope: CoroutineScope,
val userId: Int,
) {
+ /** A display that supports desktops. */
+ private data class DesktopDisplay(
+ val displayId: Int,
+ val orderedDesks: MutableSet<Desk> = mutableSetOf(),
+ // TODO: b/389960283 - update on desk activation / deactivation.
+ var activeDeskId: Int? = null,
+ )
+
/**
- * Task data tracked per desktop.
+ * Task data tracked per desk.
*
- * @property activeTasks task ids of active tasks currently or previously visible in Desktop
- * mode session. Tasks become inactive when task closes or when desktop mode session ends.
+ * @property activeTasks task ids of active tasks currently or previously visible in the desk.
+ * Tasks become inactive when task closes or when the desk becomes inactive.
* @property visibleTasks task ids for active freeform tasks that are currently visible. There
- * might be other active tasks in desktop mode that are not visible.
+ * might be other active tasks in a desk that are not visible.
* @property minimizedTasks task ids for active freeform tasks that are currently minimized.
* @property closingTasks task ids for tasks that are going to close, but are currently visible.
* @property freeformTasksInZOrder list of current freeform task ids ordered from top to bottom
- * @property fullImmersiveTaskId the task id of the desktop task that is in full-immersive mode.
+ * @property fullImmersiveTaskId the task id of the desk's task that is in full-immersive mode.
* @property topTransparentFullscreenTaskId the task id of any current top transparent
- * fullscreen task launched on top of Desktop Mode. Cleared when the transparent task is
- * closed or sent to back. (top is at index 0).
+ * fullscreen task launched on top of the desk. Cleared when the transparent task is closed or
+ * sent to back. (top is at index 0).
* @property pipTaskId the task id of PiP task entered while in Desktop Mode.
- * @property pipShouldKeepDesktopActive whether an active PiP window should keep the Desktop
- * Mode session active. Only false when we are explicitly exiting Desktop Mode (via user
- * action) while there is an active PiP window.
+ * @property pipShouldKeepDesktopActive whether an active PiP window should keep the desk
+ * active. Only false when we are explicitly exiting Desktop Mode (via user action) while
+ * there is an active PiP window.
*/
- private data class DesktopTaskData(
+ private data class Desk(
+ val deskId: Int,
+ val displayId: Int,
val activeTasks: ArraySet<Int> = ArraySet(),
val visibleTasks: ArraySet<Int> = ArraySet(),
val minimizedTasks: ArraySet<Int> = ArraySet(),
@@ -72,10 +82,13 @@ class DesktopRepository(
var fullImmersiveTaskId: Int? = null,
var topTransparentFullscreenTaskId: Int? = null,
var pipTaskId: Int? = null,
+ // TODO: b/389960283 - consolidate this with [DesktopDisplay#activeDeskId].
var pipShouldKeepDesktopActive: Boolean = true,
) {
- fun deepCopy(): DesktopTaskData =
- DesktopTaskData(
+ fun deepCopy(): Desk =
+ Desk(
+ deskId = deskId,
+ displayId = displayId,
activeTasks = ArraySet(activeTasks),
visibleTasks = ArraySet(visibleTasks),
minimizedTasks = ArraySet(minimizedTasks),
@@ -87,6 +100,8 @@ class DesktopRepository(
pipShouldKeepDesktopActive = pipShouldKeepDesktopActive,
)
+ // TODO: b/362720497 - remove when multi-desktops is enabled where instances aren't
+ // reusable.
fun clear() {
activeTasks.clear()
visibleTasks.clear()
@@ -121,11 +136,11 @@ class DesktopRepository(
private var desktopGestureExclusionListener: Consumer<Region>? = null
private var desktopGestureExclusionExecutor: Executor? = null
- private val desktopTaskDataByDisplayId =
- object : SparseArray<DesktopTaskData>() {
- /** Gets [DesktopTaskData] for existing [displayId] or creates a new one. */
- fun getOrCreate(displayId: Int): DesktopTaskData =
- this[displayId] ?: DesktopTaskData().also { this[displayId] = it }
+ private val desktopData: DesktopData =
+ if (Flags.enableMultipleDesktopsBackend()) {
+ MultiDesktopData()
+ } else {
+ SingleDesktopData()
}
/** Adds [activeTasksListener] to be notified of updates to active tasks. */
@@ -136,10 +151,16 @@ class DesktopRepository(
/** Adds [visibleTasksListener] to be notified of updates to visible tasks. */
fun addVisibleTasksListener(visibleTasksListener: VisibleTasksListener, executor: Executor) {
visibleTasksListeners[visibleTasksListener] = executor
- desktopTaskDataByDisplayId.keyIterator().forEach {
- val visibleTaskCount = getVisibleTaskCount(it)
- executor.execute { visibleTasksListener.onTasksVisibilityChanged(it, visibleTaskCount) }
- }
+ desktopData
+ .desksSequence()
+ .groupBy { it.displayId }
+ .keys
+ .forEach { displayId ->
+ val visibleTaskCount = getVisibleTaskCount(displayId)
+ executor.execute {
+ visibleTasksListener.onTasksVisibilityChanged(displayId, visibleTaskCount)
+ }
+ }
}
/** Updates tasks changes on all the active task listeners for given display id. */
@@ -147,9 +168,8 @@ class DesktopRepository(
activeTasksListeners.onEach { it.onActiveTasksChanged(displayId) }
}
- /** Returns a list of all [DesktopTaskData] in the repository. */
- private fun desktopTaskDataSequence(): Sequence<DesktopTaskData> =
- desktopTaskDataByDisplayId.valueIterator().asSequence()
+ /** Returns a list of all [Desk]s in the repository. */
+ private fun desksSequence(): Sequence<Desk> = desktopData.desksSequence()
/** Adds [regionListener] to inform about changes to exclusion regions for all Desktop tasks. */
fun setExclusionRegionListener(regionListener: Consumer<Region>, executor: Executor) {
@@ -179,99 +199,183 @@ class DesktopRepository(
visibleTasksListeners.remove(visibleTasksListener)
}
- /** Adds task with [taskId] to the list of freeform tasks on [displayId]. */
+ /** Adds the given desk under the given display. */
+ fun addDesk(displayId: Int, deskId: Int) {
+ desktopData.getOrCreateDesk(displayId, deskId)
+ }
+
+ /** Returns the default desk in the given display. */
+ fun getDefaultDesk(displayId: Int): Int? = desktopData.getDefaultDesk(displayId)?.deskId
+
+ /** Sets the given desk as the active one in the given display. */
+ fun setActiveDesk(displayId: Int, deskId: Int) {
+ desktopData.setActiveDesk(displayId = displayId, deskId = deskId)
+ }
+
+ /**
+ * Adds task with [taskId] to the list of freeform tasks on [displayId]'s active desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun addTask(displayId: Int, taskId: Int, isVisible: Boolean) {
addOrMoveFreeformTaskToTop(displayId, taskId)
addActiveTask(displayId, taskId)
updateTask(displayId, taskId, isVisible)
}
- /** Adds task with [taskId] to the list of active tasks on [displayId]. */
+ /**
+ * Adds task with [taskId] to the list of active tasks on [displayId]'s active desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
private fun addActiveTask(displayId: Int, taskId: Int) {
- // Removes task if it is active on another display excluding [displayId].
- removeActiveTask(taskId, excludedDisplayId = displayId)
+ val activeDeskId =
+ desktopData.getActiveDesk(displayId)?.deskId
+ ?: error("Expected active desk in display: $displayId")
+
+ // Removes task if it is active on another desk excluding [activeDesk].
+ removeActiveTask(taskId, excludedDeskId = activeDeskId)
- if (desktopTaskDataByDisplayId.getOrCreate(displayId).activeTasks.add(taskId)) {
- logD("Adds active task=%d displayId=%d", taskId, displayId)
+ if (desktopData.getOrCreateDesk(displayId, activeDeskId).activeTasks.add(taskId)) {
+ logD("Adds active task=%d displayId=%d deskId=%d", taskId, displayId, activeDeskId)
updateActiveTasksListeners(displayId)
}
}
- /** Removes task from active task list of displays excluding the [excludedDisplayId]. */
- fun removeActiveTask(taskId: Int, excludedDisplayId: Int? = null) {
- desktopTaskDataByDisplayId.forEach { displayId, desktopTaskData ->
- if ((displayId != excludedDisplayId) && desktopTaskData.activeTasks.remove(taskId)) {
- logD("Removed active task=%d displayId=%d", taskId, displayId)
- updateActiveTasksListeners(displayId)
+ /** Removes task from active task list of desks excluding the [excludedDeskId]. */
+ @VisibleForTesting
+ fun removeActiveTask(taskId: Int, excludedDeskId: Int? = null) {
+ val affectedDisplays = mutableSetOf<Int>()
+ desktopData.forAllDesks { displayId, desk ->
+ if (desk.deskId != excludedDeskId && desk.activeTasks.remove(taskId)) {
+ logD(
+ "Removed active task=%d displayId=%d deskId=%d",
+ taskId,
+ displayId,
+ desk.deskId,
+ )
+ affectedDisplays.add(displayId)
}
}
+ affectedDisplays.forEach { displayId -> updateActiveTasksListeners(displayId) }
}
- /** Adds given task to the closing task list for [displayId]. */
+ /**
+ * 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) {
- if (desktopTaskDataByDisplayId.getOrCreate(displayId).closingTasks.add(taskId)) {
- logD("Added closing task=%d displayId=%d", taskId, displayId)
+ val activeDeskId =
+ desktopData.getActiveDesk(displayId)?.deskId
+ ?: error("Expected active desk in display: $displayId")
+ if (desktopData.getOrCreateDesk(displayId, activeDeskId).closingTasks.add(taskId)) {
+ logD("Added closing task=%d displayId=%d deskId=%d", taskId, displayId, activeDeskId)
} else {
// If the task hasn't been removed from closing list after it disappeared.
- logW("Task with taskId=%d displayId=%d is already closing", taskId, displayId)
+ logW(
+ "Task with taskId=%d displayId=%d deskId=%d is already closing",
+ taskId,
+ displayId,
+ activeDeskId,
+ )
}
}
- /** Removes task from the list of closing tasks for [displayId]. */
+ /** Removes task from the list of closing tasks for all desks. */
fun removeClosingTask(taskId: Int) {
- desktopTaskDataByDisplayId.forEach { displayId, taskInfo ->
- if (taskInfo.closingTasks.remove(taskId)) {
- logD("Removed closing task=%d displayId=%d", taskId, displayId)
+ desktopData.forAllDesks { desk ->
+ if (desk.closingTasks.remove(taskId)) {
+ logD("Removed closing task=%d deskId=%d", taskId, desk.deskId)
}
}
}
- fun isActiveTask(taskId: Int) = desktopTaskDataSequence().any { taskId in it.activeTasks }
+ fun isActiveTask(taskId: Int) = desksSequence().any { taskId in it.activeTasks }
- fun isClosingTask(taskId: Int) = desktopTaskDataSequence().any { taskId in it.closingTasks }
+ fun isClosingTask(taskId: Int) = desksSequence().any { taskId in it.closingTasks }
- fun isVisibleTask(taskId: Int) = desktopTaskDataSequence().any { taskId in it.visibleTasks }
+ fun isVisibleTask(taskId: Int) = desksSequence().any { taskId in it.visibleTasks }
- fun isMinimizedTask(taskId: Int) = desktopTaskDataSequence().any { taskId in it.minimizedTasks }
+ fun isMinimizedTask(taskId: Int) = desksSequence().any { taskId in it.minimizedTasks }
- /** Checks if a task is the only visible, non-closing, non-minimized task on its display. */
+ /**
+ * 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.
+ */
fun isOnlyVisibleNonClosingTask(taskId: Int, displayId: Int = INVALID_DISPLAY): Boolean {
- val seq =
+ val activeDesks =
if (displayId != INVALID_DISPLAY) {
- sequenceOf(desktopTaskDataByDisplayId[displayId]).filterNotNull()
+ setOfNotNull(desktopData.getActiveDesk(displayId))
} else {
- desktopTaskDataSequence()
+ desktopData.getAllActiveDesks()
}
- return seq.any {
- it.visibleTasks.subtract(it.closingTasks).subtract(it.minimizedTasks).singleOrNull() ==
- taskId
+ return activeDesks.any { desk ->
+ desk.visibleTasks
+ .subtract(desk.closingTasks)
+ .subtract(desk.minimizedTasks)
+ .singleOrNull() == taskId
}
}
+ /**
+ * Returns the active tasks in the given display's active desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
+ @VisibleForTesting
fun getActiveTasks(displayId: Int): ArraySet<Int> =
- ArraySet(desktopTaskDataByDisplayId[displayId]?.activeTasks)
+ ArraySet(desktopData.getActiveDesk(displayId)?.activeTasks)
+ /**
+ * Returns the minimized tasks in the given display's active desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun getMinimizedTasks(displayId: Int): ArraySet<Int> =
- ArraySet(desktopTaskDataByDisplayId[displayId]?.minimizedTasks)
+ ArraySet(desktopData.getActiveDesk(displayId)?.minimizedTasks)
- /** Returns all active non-minimized tasks for [displayId] ordered from top to bottom. */
+ /**
+ * Returns all active non-minimized tasks for [displayId] ordered from top to bottom.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun getExpandedTasksOrdered(displayId: Int): List<Int> =
getFreeformTasksInZOrder(displayId).filter { !isMinimizedTask(it) }
- /** Returns the count of active non-minimized tasks for [displayId]. */
+ /**
+ * Returns the count of active non-minimized tasks for [displayId].
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun getExpandedTaskCount(displayId: Int): Int {
return getActiveTasks(displayId).count { !isMinimizedTask(it) }
}
- /** Returns a list of freeform tasks, ordered from top-bottom (top at index 0). */
+ /**
+ * Returns a list of freeform tasks, ordered from top-bottom (top at index 0).
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
+ @VisibleForTesting
fun getFreeformTasksInZOrder(displayId: Int): ArrayList<Int> =
- ArrayList(desktopTaskDataByDisplayId[displayId]?.freeformTasksInZOrder ?: emptyList())
+ ArrayList(desktopData.getActiveDesk(displayId)?.freeformTasksInZOrder ?: emptyList())
+
+ /** Returns the tasks inside the given desk. */
+ fun getActiveTaskIdsInDesk(deskId: Int): Set<Int> =
+ desktopData.getDesk(deskId)?.activeTasks?.toSet()
+ ?: run {
+ logW("getTasksInDesk: could not find desk: deskId=%d", deskId)
+ emptySet()
+ }
/** Removes task from visible tasks of all displays except [excludedDisplayId]. */
private fun removeVisibleTask(taskId: Int, excludedDisplayId: Int? = null) {
- desktopTaskDataByDisplayId.forEach { displayId, data ->
- if ((displayId != excludedDisplayId) && data.visibleTasks.remove(taskId)) {
- notifyVisibleTaskListeners(displayId, data.visibleTasks.size)
+ desktopData.forAllDesks { displayId, desk ->
+ if (displayId != excludedDisplayId && desk.visibleTasks.remove(taskId)) {
+ notifyVisibleTaskListeners(displayId, desk.visibleTasks.size)
}
}
}
@@ -281,6 +385,8 @@ class DesktopRepository(
*
* If task was visible on a different display with a different [displayId], removes from the set
* of visible tasks on that display and notifies listeners.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
*/
fun updateTask(displayId: Int, taskId: Int, isVisible: Boolean) {
logD("updateTask taskId=%d, displayId=%d, isVisible=%b", taskId, displayId, isVisible)
@@ -295,10 +401,11 @@ class DesktopRepository(
}
val prevCount = getVisibleTaskCount(displayId)
if (isVisible) {
- desktopTaskDataByDisplayId.getOrCreate(displayId).visibleTasks.add(taskId)
+ desktopData.getActiveDesk(displayId)?.visibleTasks?.add(taskId)
+ ?: error("Expected non-null active desk in display $displayId")
unminimizeTask(displayId, taskId)
} else {
- desktopTaskDataByDisplayId[displayId]?.visibleTasks?.remove(taskId)
+ desktopData.getActiveDesk(displayId)?.visibleTasks?.remove(taskId)
}
val newCount = getVisibleTaskCount(displayId)
if (prevCount != newCount) {
@@ -316,57 +423,94 @@ class DesktopRepository(
}
}
- /** Set whether the given task is the Desktop-entered PiP task in this display. */
+ /**
+ * Set whether the given task is the Desktop-entered PiP task in this display's active desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun setTaskInPip(displayId: Int, taskId: Int, enterPip: Boolean) {
- val desktopData = desktopTaskDataByDisplayId.getOrCreate(displayId)
+ val activeDesk =
+ desktopData.getActiveDesk(displayId)
+ ?: error("Expected active desk in display: $displayId")
if (enterPip) {
- desktopData.pipTaskId = taskId
- desktopData.pipShouldKeepDesktopActive = true
+ activeDesk.pipTaskId = taskId
+ activeDesk.pipShouldKeepDesktopActive = true
} else {
- desktopData.pipTaskId =
- if (desktopData.pipTaskId == taskId) null
+ activeDesk.pipTaskId =
+ if (activeDesk.pipTaskId == taskId) null
else {
logW(
- "setTaskInPip: taskId=$taskId did not match saved taskId=${desktopData.pipTaskId}"
+ "setTaskInPip: taskId=%d did not match saved taskId=%d",
+ taskId,
+ activeDesk.pipTaskId,
)
- desktopData.pipTaskId
+ activeDesk.pipTaskId
}
}
notifyVisibleTaskListeners(displayId, getVisibleTaskCount(displayId))
}
- /** Returns whether there is a PiP that was entered/minimized from Desktop in this display. */
+ /**
+ * Returns whether there is a PiP that was entered/minimized from Desktop in this display's
+ * active desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun isMinimizedPipPresentInDisplay(displayId: Int): Boolean =
- desktopTaskDataByDisplayId.getOrCreate(displayId).pipTaskId != null
+ desktopData.getActiveDesk(displayId)?.pipTaskId != null
- /** Returns whether the given task is the Desktop-entered PiP task in this display. */
+ /**
+ * Returns whether the given task is the Desktop-entered PiP task in this display's active desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun isTaskMinimizedPipInDisplay(displayId: Int, taskId: Int): Boolean =
- desktopTaskDataByDisplayId.getOrCreate(displayId).pipTaskId == taskId
+ desktopData.getActiveDesk(displayId)?.pipTaskId == taskId
- /** Returns whether Desktop session should be active in this display due to active PiP. */
+ /**
+ * Returns whether a desk should be active in this display due to active PiP.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun shouldDesktopBeActiveForPip(displayId: Int): Boolean =
Flags.enableDesktopWindowingPip() &&
isMinimizedPipPresentInDisplay(displayId) &&
- desktopTaskDataByDisplayId.getOrCreate(displayId).pipShouldKeepDesktopActive
+ (desktopData.getActiveDesk(displayId)?.pipShouldKeepDesktopActive ?: false)
- /** Saves whether a PiP window should keep Desktop session active in this display. */
+ /**
+ * Saves whether a PiP window should keep Desktop session active in this display.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun setPipShouldKeepDesktopActive(displayId: Int, keepActive: Boolean) {
- desktopTaskDataByDisplayId.getOrCreate(displayId).pipShouldKeepDesktopActive = keepActive
+ desktopData.getActiveDesk(displayId)?.pipShouldKeepDesktopActive = keepActive
}
- /** Saves callback to handle a pending PiP transition being aborted. */
- fun setOnPipAbortedCallback(callbackIfPipAborted: ((Int, Int) -> Unit)?) {
+ /**
+ * Saves callback to handle a pending PiP transition being aborted.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
+ fun setOnPipAbortedCallback(callbackIfPipAborted: ((displayId: Int, pipTaskId: Int) -> Unit)?) {
onPipAbortedCallback = callbackIfPipAborted
}
- /** Invokes callback to handle a pending PiP transition with the given task id being aborted. */
+ /**
+ * Invokes callback to handle a pending PiP transition with the given task id being aborted.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun onPipAborted(displayId: Int, pipTaskId: Int) {
onPipAbortedCallback?.invoke(displayId, pipTaskId)
}
- /** Set whether the given task is the full-immersive task in this display. */
+ /**
+ * Set whether the given task is the full-immersive task in this display's active desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun setTaskInFullImmersiveState(displayId: Int, taskId: Int, immersive: Boolean) {
- val desktopData = desktopTaskDataByDisplayId.getOrCreate(displayId)
+ val desktopData = desktopData.getActiveDesk(displayId) ?: return
if (immersive) {
desktopData.fullImmersiveTaskId = taskId
} else {
@@ -378,25 +522,41 @@ class DesktopRepository(
/* Whether the task is in full-immersive state. */
fun isTaskInFullImmersiveState(taskId: Int): Boolean {
- return desktopTaskDataSequence().any { taskId == it.fullImmersiveTaskId }
+ return desksSequence().any { taskId == it.fullImmersiveTaskId }
}
- /** Returns the task that is currently in immersive mode in this display, or null. */
+ /**
+ * Returns the task that is currently in immersive mode in this display, or null.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun getTaskInFullImmersiveState(displayId: Int): Int? =
- desktopTaskDataByDisplayId.getOrCreate(displayId).fullImmersiveTaskId
+ desktopData.getActiveDesk(displayId)?.fullImmersiveTaskId
- /** Sets the top transparent fullscreen task id for a given display. */
+ /**
+ * Sets the top transparent fullscreen task id for a given display's active desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun setTopTransparentFullscreenTaskId(displayId: Int, taskId: Int) {
- desktopTaskDataByDisplayId.getOrCreate(displayId).topTransparentFullscreenTaskId = taskId
+ desktopData.getActiveDesk(displayId)?.topTransparentFullscreenTaskId = taskId
}
- /** Returns the top transparent fullscreen task id for a given display, or null. */
+ /**
+ * Returns the top transparent fullscreen task id for a given display's active desk, or null.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun getTopTransparentFullscreenTaskId(displayId: Int): Int? =
- desktopTaskDataByDisplayId.getOrCreate(displayId).topTransparentFullscreenTaskId
+ desktopData.getActiveDesk(displayId)?.topTransparentFullscreenTaskId
- /** Clears the top transparent fullscreen task id info for a given display. */
+ /**
+ * Clears the top transparent fullscreen task id info for a given display's active desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun clearTopTransparentFullscreenTaskId(displayId: Int) {
- desktopTaskDataByDisplayId.getOrCreate(displayId).topTransparentFullscreenTaskId = null
+ desktopData.getActiveDesk(displayId)?.topTransparentFullscreenTaskId = null
}
private fun notifyVisibleTaskListeners(displayId: Int, visibleTasksCount: Int) {
@@ -409,22 +569,35 @@ class DesktopRepository(
}
}
- /** Gets number of visible freeform tasks on given [displayId] */
+ /**
+ * Gets number of visible freeform tasks on given [displayId]'s active desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun getVisibleTaskCount(displayId: Int): Int =
- desktopTaskDataByDisplayId[displayId]?.visibleTasks?.size
- ?: 0.also { logD("getVisibleTaskCount=$it") }
+ (desktopData.getActiveDesk(displayId)?.visibleTasks?.size ?: 0).also {
+ logD("getVisibleTaskCount=$it")
+ }
/**
* Adds task (or moves if it already exists) to the top of the ordered list.
*
* Unminimizes the task if it is minimized.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
*/
private fun addOrMoveFreeformTaskToTop(displayId: Int, taskId: Int) {
- logD("Add or move task to top: display=%d taskId=%d", taskId, displayId)
- desktopTaskDataByDisplayId.forEach { _, value ->
- value.freeformTasksInZOrder.remove(taskId)
- }
- desktopTaskDataByDisplayId.getOrCreate(displayId).freeformTasksInZOrder.add(0, taskId)
+ val activeDesk =
+ desktopData.getActiveDesk(displayId)
+ ?: error("Expected a desk to be active in display: $displayId")
+ logD(
+ "Add or move task to top: display=%d taskId=%d deskId=%d",
+ taskId,
+ displayId,
+ activeDesk.deskId,
+ )
+ desktopData.forAllDesks { _, desk -> desk.freeformTasksInZOrder.remove(taskId) }
+ activeDesk.freeformTasksInZOrder.add(0, taskId)
// Unminimize the task if it is minimized.
unminimizeTask(displayId, taskId)
if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
@@ -432,7 +605,11 @@ class DesktopRepository(
}
}
- /** Minimizes the task for [taskId] and [displayId] */
+ /**
+ * Minimizes the task for [taskId] and [displayId]'s active display.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
+ */
fun minimizeTask(displayId: Int, taskId: Int) {
if (displayId == INVALID_DISPLAY) {
// When a task vanishes it doesn't have a displayId. Find the display of the task and
@@ -441,7 +618,8 @@ class DesktopRepository(
?: logW("Minimize task: No display id found for task: taskId=%d", taskId)
} else {
logD("Minimize Task: display=%d, task=%d", displayId, taskId)
- desktopTaskDataByDisplayId.getOrCreate(displayId).minimizedTasks.add(taskId)
+ desktopData.getActiveDesk(displayId)?.minimizedTasks?.add(taskId)
+ ?: logD("Minimize task: No active desk found for task: taskId=%d", taskId)
}
updateTask(displayId, taskId, isVisible = false)
if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
@@ -449,26 +627,42 @@ class DesktopRepository(
}
}
- /** Unminimizes the task for [taskId] and [displayId] */
+ /**
+ * Unminimizes the task for [taskId] and [displayId].
+ *
+ * TODO: b/389960283 - consider adding an explicit [deskId] argument.
+ */
fun unminimizeTask(displayId: Int, taskId: Int) {
logD("Unminimize Task: display=%d, task=%d", displayId, taskId)
- desktopTaskDataByDisplayId[displayId]?.minimizedTasks?.remove(taskId)
- ?: logW("Unminimize Task: display=%d, task=%d, no task data", displayId, taskId)
+ var removed = false
+ desktopData.forAllDesks(displayId) { desk ->
+ if (desk.minimizedTasks.remove(taskId)) {
+ removed = true
+ }
+ }
+ if (!removed) {
+ logW("Unminimize Task: display=%d, task=%d, no task data", displayId, taskId)
+ }
}
private fun getDisplayIdForTask(taskId: Int): Int? {
- desktopTaskDataByDisplayId.forEach { displayId, data ->
- if (taskId in data.freeformTasksInZOrder) {
- return displayId
+ var displayForTask: Int? = null
+ desktopData.forAllDesks { displayId, desk ->
+ if (taskId in desk.freeformTasksInZOrder) {
+ displayForTask = displayId
}
}
- logW("No display id found for task: taskId=%d", taskId)
- return null
+ if (displayForTask == null) {
+ logW("No display id found for task: taskId=%d", taskId)
+ }
+ return displayForTask
}
/**
* Removes [taskId] from the respective display. If [INVALID_DISPLAY], the original display id
* will be looked up from the task id.
+ *
+ * TODO: b/389960283 - consider adding an explicit [deskId] argument.
*/
fun removeTask(displayId: Int, taskId: Int) {
logD("Removes freeform task: taskId=%d", taskId)
@@ -483,13 +677,17 @@ class DesktopRepository(
/** Removes given task from a valid [displayId] and updates the repository state. */
private fun removeTaskFromDisplay(displayId: Int, taskId: Int) {
logD("Removes freeform task: taskId=%d, displayId=%d", taskId, displayId)
- desktopTaskDataByDisplayId[displayId]?.freeformTasksInZOrder?.remove(taskId)
+ desktopData.forAllDesks(displayId) { desk ->
+ if (desk.freeformTasksInZOrder.remove(taskId)) {
+ logD(
+ "Remaining freeform tasks in desk: %d, tasks: %s",
+ desk.deskId,
+ desk.freeformTasksInZOrder.toDumpString(),
+ )
+ }
+ }
boundsBeforeMaximizeByTaskId.remove(taskId)
boundsBeforeFullImmersiveByTaskId.remove(taskId)
- logD(
- "Remaining freeform tasks: %s",
- desktopTaskDataByDisplayId[displayId]?.freeformTasksInZOrder?.toDumpString(),
- )
// Remove task from unminimized task if it is minimized.
unminimizeTask(displayId, taskId)
// Mark task as not in immersive if it was immersive.
@@ -502,15 +700,18 @@ class DesktopRepository(
}
/**
- * Removes the desktop for the given [displayId] and returns the active tasks on that desktop.
+ * Removes the active desk for the given [displayId] and returns the active tasks on that desk.
+ *
+ * TODO: b/389960283 - add explicit [deskId] argument.
*/
- fun removeDesktop(displayId: Int): ArraySet<Int> {
- if (!desktopTaskDataByDisplayId.contains(displayId)) {
- logW("Could not find desktop to remove: displayId=%d", displayId)
+ fun removeDesk(displayId: Int): ArraySet<Int> {
+ val desk = desktopData.getActiveDesk(displayId)
+ if (desk == null) {
+ logW("Could not find desk to remove: displayId=%d", displayId)
return ArraySet()
}
- val activeTasks = ArraySet(desktopTaskDataByDisplayId[displayId].activeTasks)
- desktopTaskDataByDisplayId[displayId].clear()
+ val activeTasks = ArraySet(desk.activeTasks)
+ desktopData.remove(desk.deskId)
return activeTasks
}
@@ -564,19 +765,20 @@ class DesktopRepository(
fun saveBoundsBeforeFullImmersive(taskId: Int, bounds: Rect) =
boundsBeforeFullImmersiveByTaskId.set(taskId, Rect(bounds))
+ /** TODO: b/389960283 - consider updating only the changing desks. */
private fun updatePersistentRepository(displayId: Int) {
- // Create a deep copy of the data
- desktopTaskDataByDisplayId[displayId]?.deepCopy()?.let { desktopTaskDataByDisplayIdCopy ->
- mainCoroutineScope.launch {
+ val desks = desktopData.desksSequence(displayId).map { desk -> desk.deepCopy() }.toList()
+ mainCoroutineScope.launch {
+ desks.forEach { desk ->
try {
persistentRepository.addOrUpdateDesktop(
- // Use display id as desktop id for now since only once desktop per display
+ // Use display id as desk id for now since only once desk per display
// is supported.
userId = userId,
- desktopId = displayId,
- visibleTasks = desktopTaskDataByDisplayIdCopy.visibleTasks,
- minimizedTasks = desktopTaskDataByDisplayIdCopy.minimizedTasks,
- freeformTasksInZOrder = desktopTaskDataByDisplayIdCopy.freeformTasksInZOrder,
+ desktopId = desk.deskId,
+ visibleTasks = desk.visibleTasks,
+ minimizedTasks = desk.minimizedTasks,
+ freeformTasksInZOrder = desk.freeformTasksInZOrder,
)
} catch (exception: Exception) {
logE(
@@ -598,20 +800,27 @@ class DesktopRepository(
private fun dumpDesktopTaskData(pw: PrintWriter, prefix: String) {
val innerPrefix = "$prefix "
- desktopTaskDataByDisplayId.forEach { displayId, data ->
- pw.println("${prefix}Display $displayId:")
- pw.println("${innerPrefix}activeTasks=${data.activeTasks.toDumpString()}")
- pw.println("${innerPrefix}visibleTasks=${data.visibleTasks.toDumpString()}")
- pw.println(
- "${innerPrefix}freeformTasksInZOrder=${data.freeformTasksInZOrder.toDumpString()}"
- )
- pw.println("${innerPrefix}minimizedTasks=${data.minimizedTasks.toDumpString()}")
- pw.println("${innerPrefix}fullImmersiveTaskId=${data.fullImmersiveTaskId}")
- pw.println(
- "${innerPrefix}topTransparentFullscreenTaskId=" +
- "${data.topTransparentFullscreenTaskId}"
- )
- }
+ desktopData
+ .desksSequence()
+ .groupBy { it.displayId }
+ .forEach { (displayId, desks) ->
+ pw.println("${prefix}Display #$displayId:")
+ desks.forEach { desk ->
+ pw.println("${innerPrefix}Desk #${desk.deskId}:")
+ pw.print("$innerPrefix activeTasks=")
+ pw.println(desk.activeTasks.toDumpString())
+ pw.print("$innerPrefix visibleTasks=")
+ pw.println(desk.visibleTasks.toDumpString())
+ pw.print("$innerPrefix freeformTasksInZOrder=")
+ pw.println(desk.freeformTasksInZOrder.toDumpString())
+ pw.print("$innerPrefix minimizedTasks=")
+ pw.println(desk.minimizedTasks.toDumpString())
+ pw.print("$innerPrefix fullImmersiveTaskId=")
+ pw.println(desk.fullImmersiveTaskId)
+ pw.print("$innerPrefix topTransparentFullscreenTaskId=")
+ pw.println(desk.topTransparentFullscreenTaskId)
+ }
+ }
}
/** Listens to changes for active tasks in desktop mode. */
@@ -624,6 +833,227 @@ class DesktopRepository(
fun onTasksVisibilityChanged(displayId: Int, visibleTasksCount: Int) {}
}
+ /** An interface for the desktop hierarchy's data managed by this repository. */
+ private interface DesktopData {
+ /**
+ * Returns the existing desk or creates a new entry if needed.
+ *
+ * TODO: 389787966 - consider removing this as it cannot be assumed a desk can be created in
+ * all devices / form-factors.
+ */
+ fun getOrCreateDesk(displayId: Int, deskId: Int): Desk
+
+ /** Returns the desk with the given id, or null if it does not exist. */
+ fun getDesk(deskId: Int): Desk?
+
+ /** Returns the active desk in this diplay, or null if none are active. */
+ fun getActiveDesk(displayId: Int): Desk?
+
+ /** Sets the given desk as the active desk in the given display. */
+ fun setActiveDesk(displayId: Int, deskId: Int)
+
+ /**
+ * Returns the default desk in the given display. Useful when the system wants to activate a
+ * desk but doesn't care about which one it activates (e.g. when putting a window into a
+ * desk using the App Handle). May return null if the display does not support desks.
+ *
+ * TODO: 389787966 - consider removing or renaming. In practice, this is needed for
+ * soon-to-be deprecated IDesktopMode APIs, adb commands or entry-points into the only
+ * desk (single-desk devices) or the most-recent desk (multi-desk devices).
+ */
+ fun getDefaultDesk(displayId: Int): Desk?
+
+ /** Returns all the active desks of all displays. */
+ fun getAllActiveDesks(): Set<Desk>
+
+ /** Returns the number of desks in the given display. */
+ fun getNumberOfDesks(displayId: Int): Int
+
+ /** Applies a function to all desks. */
+ fun forAllDesks(consumer: (Desk) -> Unit)
+
+ /** Applies a function to all desks. */
+ fun forAllDesks(consumer: (displayId: Int, Desk) -> Unit)
+
+ /** Applies a function to all desks under the given display. */
+ fun forAllDesks(displayId: Int, consumer: (Desk) -> Unit)
+
+ /** Returns a sequence of all desks. */
+ fun desksSequence(): Sequence<Desk>
+
+ /** Returns a sequence of all desks under the given display. */
+ fun desksSequence(displayId: Int): Sequence<Desk>
+
+ /** Remove an existing desk if it exists. */
+ fun remove(deskId: Int)
+
+ /** Returns the id of the display where the given desk is located. */
+ fun getDisplayForDesk(deskId: Int): Int
+ }
+
+ /**
+ * A [DesktopData] implementation that only supports one desk per display.
+ *
+ * Internally, it reuses the displayId as that display's single desk's id.
+ */
+ private class SingleDesktopData : DesktopData {
+ private val deskByDisplayId =
+ object : SparseArray<Desk>() {
+ /** Gets [Desk] for existing [displayId] or creates a new one. */
+ fun getOrCreate(displayId: Int): Desk =
+ this[displayId]
+ ?: Desk(deskId = displayId, displayId = displayId).also {
+ this[displayId] = it
+ }
+ }
+
+ override fun getOrCreateDesk(displayId: Int, deskId: Int): Desk {
+ check(displayId == deskId)
+ return deskByDisplayId.getOrCreate(displayId)
+ }
+
+ override fun getDesk(deskId: Int): Desk = getOrCreateDesk(deskId, deskId)
+
+ override fun getActiveDesk(displayId: Int): Desk {
+ // TODO: 389787966 - consider migrating to an "active" state instead of checking the
+ // number of visible active tasks, PIP in desktop, and empty desktop logic. In
+ // practice, existing single-desktop devices are ok with this function returning the
+ // only desktop, even if it's not active.
+ return deskByDisplayId.getOrCreate(displayId)
+ }
+
+ override fun setActiveDesk(displayId: Int, deskId: Int) {
+ // No-op, in single-desk setups, which desktop is "active" is determined by the
+ // existence of visible desktop windows, among other factors.
+ }
+
+ override fun getDefaultDesk(displayId: Int): Desk = getOrCreateDesk(displayId, displayId)
+
+ override fun getAllActiveDesks(): Set<Desk> =
+ deskByDisplayId.valueIterator().asSequence().toSet()
+
+ override fun getNumberOfDesks(displayId: Int): Int = 1
+
+ override fun forAllDesks(consumer: (Desk) -> Unit) {
+ deskByDisplayId.forEach { _, desk -> consumer(desk) }
+ }
+
+ override fun forAllDesks(consumer: (Int, Desk) -> Unit) {
+ deskByDisplayId.forEach { displayId, desk -> consumer(displayId, desk) }
+ }
+
+ override fun forAllDesks(displayId: Int, consumer: (Desk) -> Unit) {
+ consumer(getOrCreateDesk(displayId, displayId))
+ }
+
+ override fun desksSequence(): Sequence<Desk> = deskByDisplayId.valueIterator().asSequence()
+
+ override fun desksSequence(displayId: Int): Sequence<Desk> =
+ deskByDisplayId[displayId]?.let { sequenceOf(it) } ?: emptySequence()
+
+ override fun remove(deskId: Int) {
+ deskByDisplayId[deskId]?.clear()
+ }
+
+ override fun getDisplayForDesk(deskId: Int): Int = deskId
+ }
+
+ /** A [DesktopData] implementation that supports multiple desks. */
+ private class MultiDesktopData : DesktopData {
+ private val desktopDisplays = SparseArray<DesktopDisplay>()
+
+ override fun getOrCreateDesk(displayId: Int, deskId: Int): Desk {
+ val display =
+ desktopDisplays[displayId]
+ ?: DesktopDisplay(displayId).also { desktopDisplays[displayId] = it }
+ val desk =
+ display.orderedDesks.find { desk -> desk.deskId == deskId }
+ ?: Desk(deskId = deskId, displayId = displayId).also {
+ display.orderedDesks.add(it)
+ }
+ return desk
+ }
+
+ override fun getDesk(deskId: Int): Desk? {
+ desktopDisplays.forEach { _, display ->
+ val desk = display.orderedDesks.find { desk -> desk.deskId == deskId }
+ if (desk != null) {
+ return desk
+ }
+ }
+ return null
+ }
+
+ override fun getActiveDesk(displayId: Int): Desk? {
+ val display = desktopDisplays[displayId] ?: return null
+ if (display.activeDeskId == null) return null
+ return display.orderedDesks.find { it.deskId == display.activeDeskId }
+ }
+
+ override fun setActiveDesk(displayId: Int, deskId: Int) {
+ val display =
+ desktopDisplays[displayId] ?: error("Expected display#$displayId to exist")
+ val desk = display.orderedDesks.single { it.deskId == deskId }
+ display.activeDeskId = desk.deskId
+ }
+
+ override fun getDefaultDesk(displayId: Int): Desk? {
+ val display = desktopDisplays[displayId] ?: return null
+ return display.orderedDesks.firstOrNull()
+ }
+
+ override fun getAllActiveDesks(): Set<Desk> {
+ return desktopDisplays
+ .valueIterator()
+ .asSequence()
+ .filter { display -> display.activeDeskId != null }
+ .map { display ->
+ display.orderedDesks.single { it.deskId == display.activeDeskId }
+ }
+ .toSet()
+ }
+
+ override fun getNumberOfDesks(displayId: Int): Int =
+ desktopDisplays[displayId]?.orderedDesks?.size ?: 0
+
+ override fun forAllDesks(consumer: (Desk) -> Unit) {
+ desktopDisplays.forEach { _, display -> display.orderedDesks.forEach { consumer(it) } }
+ }
+
+ override fun forAllDesks(consumer: (Int, Desk) -> Unit) {
+ desktopDisplays.forEach { _, display ->
+ display.orderedDesks.forEach { consumer(display.displayId, it) }
+ }
+ }
+
+ override fun forAllDesks(displayId: Int, consumer: (Desk) -> Unit) {
+ desktopDisplays
+ .valueIterator()
+ .asSequence()
+ .filter { display -> display.displayId == displayId }
+ .flatMap { display -> display.orderedDesks.asSequence() }
+ .forEach { desk -> consumer(desk) }
+ }
+
+ override fun desksSequence(): Sequence<Desk> =
+ desktopDisplays.valueIterator().asSequence().flatMap { display ->
+ display.orderedDesks.asSequence()
+ }
+
+ override fun desksSequence(displayId: Int): Sequence<Desk> =
+ desktopDisplays[displayId]?.orderedDesks?.asSequence() ?: emptySequence()
+
+ override fun remove(deskId: Int) {
+ desktopDisplays.forEach { _, display ->
+ display.orderedDesks.removeIf { it.deskId == deskId }
+ }
+ }
+
+ override fun getDisplayForDesk(deskId: Int): Int =
+ getAllActiveDesks().find { it.deskId == deskId }?.displayId
+ ?: error("Display for desk=$deskId not found")
+ }
+
private fun logD(msg: String, vararg arguments: Any?) {
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
}
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 91e06300f1b0..172410d0482c 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
@@ -87,6 +87,7 @@ import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.compatui.isTopActivityExemptFromDesktopWindowing
import com.android.wm.shell.compatui.isTransparentTask
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum
import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.DragStartState
@@ -466,7 +467,9 @@ class DesktopTasksController(
desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted(
FREEFORM_ANIMATION_DURATION
)
- taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
+ taskIdToMinimize?.let {
+ addPendingMinimizeTransition(transition, it, MinimizeReason.TASK_LIMIT)
+ }
exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
return true
}
@@ -512,7 +515,9 @@ class DesktopTasksController(
desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted(
FREEFORM_ANIMATION_DURATION
)
- taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
+ taskIdToMinimize?.let {
+ addPendingMinimizeTransition(transition, it, MinimizeReason.TASK_LIMIT)
+ }
exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
}
@@ -573,7 +578,9 @@ class DesktopTasksController(
DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS.toInt()
)
transition?.let {
- taskIdToMinimize?.let { taskId -> addPendingMinimizeTransition(it, taskId) }
+ taskIdToMinimize?.let { taskId ->
+ addPendingMinimizeTransition(it, taskId, MinimizeReason.TASK_LIMIT)
+ }
exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
}
}
@@ -622,7 +629,7 @@ class DesktopTasksController(
?.runOnTransitionStart
}
- fun minimizeTask(taskInfo: RunningTaskInfo) {
+ fun minimizeTask(taskInfo: RunningTaskInfo, minimizeReason: MinimizeReason) {
val wct = WindowContainerTransaction()
val isMinimizingToPip = taskInfo.pictureInPictureParams?.isAutoEnterEnabled() ?: false
@@ -642,16 +649,16 @@ class DesktopTasksController(
freeformTaskTransitionStarter.startPipTransition(wct)
taskRepository.setTaskInPip(taskInfo.displayId, taskInfo.taskId, enterPip = true)
taskRepository.setOnPipAbortedCallback { displayId, taskId ->
- minimizeTaskInner(shellTaskOrganizer.getRunningTaskInfo(taskId)!!)
+ minimizeTaskInner(shellTaskOrganizer.getRunningTaskInfo(taskId)!!, minimizeReason)
taskRepository.setTaskInPip(displayId, taskId, enterPip = false)
}
return
}
- minimizeTaskInner(taskInfo)
+ minimizeTaskInner(taskInfo, minimizeReason)
}
- private fun minimizeTaskInner(taskInfo: RunningTaskInfo) {
+ private fun minimizeTaskInner(taskInfo: RunningTaskInfo, minimizeReason: MinimizeReason) {
val taskId = taskInfo.taskId
val displayId = taskInfo.displayId
val wct = WindowContainerTransaction()
@@ -671,6 +678,7 @@ class DesktopTasksController(
transition = transition,
displayId = displayId,
taskId = taskId,
+ minimizeReason = minimizeReason,
)
}
exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
@@ -826,7 +834,7 @@ class DesktopTasksController(
minimizingTaskId = taskIdToMinimize,
exitingImmersiveTask = exitImmersiveResult.asExit()?.exitingTask,
)
- taskIdToMinimize?.let { addPendingMinimizeTransition(t, it) }
+ taskIdToMinimize?.let { addPendingMinimizeTransition(t, it, MinimizeReason.TASK_LIMIT) }
exitImmersiveResult.asExit()?.runOnTransitionStart?.invoke(t)
return t
}
@@ -846,7 +854,7 @@ class DesktopTasksController(
)
val t = transitions.startTransition(transitionType, wct, remoteTransitionHandler)
remoteTransitionHandler.setTransition(t)
- taskIdToMinimize.let { addPendingMinimizeTransition(t, it) }
+ taskIdToMinimize.let { addPendingMinimizeTransition(t, it, MinimizeReason.TASK_LIMIT) }
exitImmersiveResult.asExit()?.runOnTransitionStart?.invoke(t)
return t
}
@@ -1898,7 +1906,7 @@ class DesktopTasksController(
val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
addPendingAppLaunchTransition(transition, task.taskId, taskIdToMinimize)
if (taskIdToMinimize != null) {
- addPendingMinimizeTransition(transition, taskIdToMinimize)
+ addPendingMinimizeTransition(transition, taskIdToMinimize, MinimizeReason.TASK_LIMIT)
return wct
}
if (!wct.isEmpty) {
@@ -1932,7 +1940,9 @@ class DesktopTasksController(
// Desktop Mode is already showing and we're launching a new Task - we might need to
// minimize another Task.
val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
- taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
+ taskIdToMinimize?.let {
+ addPendingMinimizeTransition(transition, it, MinimizeReason.TASK_LIMIT)
+ }
addPendingAppLaunchTransition(transition, task.taskId, taskIdToMinimize)
desktopImmersiveController.exitImmersiveIfApplicable(
transition,
@@ -2180,13 +2190,18 @@ class DesktopTasksController(
.addAndGetMinimizeTaskChanges(displayId, wct, newTaskId, launchingNewIntent)
}
- private fun addPendingMinimizeTransition(transition: IBinder, taskIdToMinimize: Int) {
+ private fun addPendingMinimizeTransition(
+ transition: IBinder,
+ taskIdToMinimize: Int,
+ minimizeReason: MinimizeReason,
+ ) {
val taskToMinimize = shellTaskOrganizer.getRunningTaskInfo(taskIdToMinimize)
desktopTasksLimiter.ifPresent {
it.addPendingMinimizeChange(
transition = transition,
displayId = taskToMinimize?.displayId ?: DEFAULT_DISPLAY,
taskId = taskIdToMinimize,
+ minimizeReason = minimizeReason,
)
}
}
@@ -2216,7 +2231,7 @@ class DesktopTasksController(
fun removeDesktop(displayId: Int) {
if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) return
- val tasksToRemove = taskRepository.removeDesktop(displayId)
+ val tasksToRemove = taskRepository.removeDesk(displayId)
val wct = WindowContainerTransaction()
tasksToRemove.forEach {
val task = shellTaskOrganizer.getRunningTaskInfo(it)
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 e4a28e9efe60..204b39645248 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
@@ -30,6 +30,7 @@ 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
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.sysui.UserChangeListener
@@ -67,12 +68,21 @@ class DesktopTasksLimiter(
logV("Starting limiter with a maximum of %d tasks", maxTasksLimit)
}
- private data class TaskDetails(
+ data class TaskDetails(
val displayId: Int,
val taskId: Int,
- var transitionInfo: TransitionInfo?,
+ var transitionInfo: TransitionInfo? = null,
+ val minimizeReason: MinimizeReason? = null,
)
+ /**
+ * Returns the task being minimized in the given transition if that transition is a pending or
+ * active minimize transition.
+ */
+ fun getMinimizingTask(transition: IBinder): TaskDetails? {
+ return minimizeTransitionObserver.getMinimizingTask(transition)
+ }
+
// TODO(b/333018485): replace this observer when implementing the minimize-animation
private inner class MinimizeTransitionObserver : TransitionObserver {
private val pendingTransitionTokensAndTasks = mutableMapOf<IBinder, TaskDetails>()
@@ -82,6 +92,11 @@ class DesktopTasksLimiter(
pendingTransitionTokensAndTasks[transition] = taskDetails
}
+ fun getMinimizingTask(transition: IBinder): TaskDetails? {
+ return pendingTransitionTokensAndTasks[transition]
+ ?: activeTransitionTokensAndTasks[transition]
+ }
+
override fun onTransitionReady(
transition: IBinder,
info: TransitionInfo,
@@ -89,6 +104,14 @@ class DesktopTasksLimiter(
finishTransaction: SurfaceControl.Transaction,
) {
val taskRepository = desktopUserRepositories.current
+ handleMinimizeTransition(taskRepository, transition, info)
+ }
+
+ private fun handleMinimizeTransition(
+ taskRepository: DesktopRepository,
+ transition: IBinder,
+ info: TransitionInfo,
+ ) {
val taskToMinimize = pendingTransitionTokensAndTasks.remove(transition) ?: return
if (!taskRepository.isActiveTask(taskToMinimize.taskId)) return
if (!isTaskReadyForMinimize(info, taskToMinimize)) {
@@ -241,10 +264,15 @@ class DesktopTasksLimiter(
* Add a pending minimize transition change to update the list of minimized apps once the
* transition goes through.
*/
- fun addPendingMinimizeChange(transition: IBinder, displayId: Int, taskId: Int) {
+ fun addPendingMinimizeChange(
+ transition: IBinder,
+ displayId: Int,
+ taskId: Int,
+ minimizeReason: MinimizeReason,
+ ) {
minimizeTransitionObserver.addPendingTransitionToken(
transition,
- TaskDetails(displayId, taskId, transitionInfo = null),
+ TaskDetails(displayId, taskId, transitionInfo = null, minimizeReason = minimizeReason),
)
}
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 aeccd86e122c..36eaebdf4fff 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
@@ -1148,9 +1148,12 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
change, layer, info, t, mLeashMap);
appearedTargets[nextTargetIdx++] = target;
// reparent into the original `mInfo` since that's where we are animating.
- final int rootIdx = TransitionUtil.rootIndexFor(change, mInfo);
+ final TransitionInfo.Root root = TransitionUtil.getRootFor(change, mInfo);
final boolean wasClosing = closingIdx >= 0;
- t.reparent(target.leash, mInfo.getRoot(rootIdx).getLeash());
+ t.reparent(target.leash, root.getLeash());
+ t.setPosition(target.leash,
+ change.getStartAbsBounds().left - root.getOffset().x,
+ change.getStartAbsBounds().top - root.getOffset().y);
t.setLayer(target.leash, layer);
if (wasClosing) {
// App was previously visible and is closing
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 d6d393f2500c..eb3a698fb58e 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
@@ -34,6 +34,7 @@ import static com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_MODE_APP_HAND
import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions;
import static com.android.wm.shell.compatui.AppCompatUtils.isTopActivityExemptFromDesktopWindowing;
import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod;
+import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason;
import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger;
import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR;
import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR;
@@ -980,7 +981,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
ToggleTaskSizeInteraction.AmbiguousSource.HEADER_BUTTON, mMotionEvent);
}
} else if (id == R.id.minimize_window) {
- mDesktopTasksController.minimizeTask(decoration.mTaskInfo);
+ mDesktopTasksController.minimizeTask(
+ decoration.mTaskInfo, MinimizeReason.MINIMIZE_BUTTON);
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
index 413e7bc5d1d6..016e04039b12 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
@@ -46,6 +46,7 @@ import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
@@ -294,7 +295,7 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() {
testExecutor.flushAll()
assertThat(result).isTrue()
- verify(desktopTasksController).minimizeTask(task)
+ verify(desktopTasksController).minimizeTask(task, MinimizeReason.KEY_GESTURE)
}
private fun setUpFreeformTask(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
index 4317143aebfe..a9ebcef9bd98 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
@@ -42,6 +42,7 @@ import android.window.WindowContainerToken
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.modules.utils.testing.ExtendedMockitoRule
+import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
@@ -62,6 +63,7 @@ import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.TransitionInfoBuilder
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TRANSIT_MINIMIZE
+import java.util.Optional
import kotlin.test.assertFalse
import kotlin.test.assertTrue
import org.junit.Before
@@ -69,6 +71,7 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
+import org.mockito.Mockito.`when`
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
@@ -102,6 +105,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
private val mockShellInit = mock<ShellInit>()
private val transitions = mock<Transitions>()
private val context = mock<Context>()
+ private val shellTaskOrganizer = mock<ShellTaskOrganizer>()
+ private val desktopTasksLimiter = mock<DesktopTasksLimiter>()
private lateinit var transitionObserver: DesktopModeLoggerTransitionObserver
private lateinit var shellInit: ShellInit
@@ -119,6 +124,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
mockShellInit,
transitions,
desktopModeEventLogger,
+ Optional.of(desktopTasksLimiter),
+ shellTaskOrganizer,
)
val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java)
verify(mockShellInit)
@@ -755,6 +762,39 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
verify(desktopModeEventLogger, never()).logSessionExit(any())
}
+ @Test
+ fun onTransitionReady_taskIsBeingMinimized_logsTaskMinimized() {
+ transitionObserver.isSessionActive = true
+ transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM, id = 1))
+ val taskInfo2 = createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2)
+ transitionObserver.addTaskInfosToCachedMap(taskInfo2)
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_TO_BACK, 0)
+ .addChange(createChange(TRANSIT_TO_BACK, taskInfo2))
+ .build()
+ `when`(desktopTasksLimiter.getMinimizingTask(any()))
+ .thenReturn(
+ DesktopTasksLimiter.TaskDetails(
+ taskInfo2.displayId,
+ taskInfo2.taskId,
+ minimizeReason = MinimizeReason.TASK_LIMIT,
+ )
+ )
+
+ callOnTransitionReady(transitionInfo)
+
+ verify(desktopModeEventLogger, times(1))
+ .logTaskRemoved(
+ eq(
+ DEFAULT_TASK_UPDATE.copy(
+ instanceId = 2,
+ visibleTaskCount = 1,
+ minimizeReason = MinimizeReason.TASK_LIMIT,
+ )
+ )
+ )
+ }
+
/** Simulate calling the onTransitionReady() method */
private fun callOnTransitionReady(transitionInfo: TransitionInfo) {
val transition = mock<IBinder>()
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 6003a219d4db..8d73f3f59afd 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
@@ -1046,14 +1046,14 @@ class DesktopRepositoryTest : ShellTestCase() {
}
@Test
- fun removeDesktop_multipleTasks_removesAll() {
+ fun removeDesk_multipleTasks_removesAll() {
// The front-most task will be the one added last through `addTask`.
repo.addTask(displayId = DEFAULT_DISPLAY, taskId = 3, isVisible = true)
repo.addTask(displayId = DEFAULT_DISPLAY, taskId = 2, isVisible = true)
repo.addTask(displayId = DEFAULT_DISPLAY, taskId = 1, isVisible = true)
repo.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = 2)
- val tasksBeforeRemoval = repo.removeDesktop(displayId = DEFAULT_DISPLAY)
+ val tasksBeforeRemoval = repo.removeDesk(displayId = DEFAULT_DISPLAY)
assertThat(tasksBeforeRemoval).containsExactly(1, 2, 3).inOrder()
assertThat(repo.getActiveTasks(displayId = DEFAULT_DISPLAY)).isEmpty()
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 d13ff79b9518..6c4f043a4f39 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
@@ -102,6 +102,7 @@ import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.desktopmode.DesktopImmersiveController.ExitResult
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
import com.android.wm.shell.desktopmode.DesktopTasksController.DesktopModeEntryExitTransitionListener
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
@@ -2162,7 +2163,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
whenever(transitions.dispatchRequest(any(), any(), anyOrNull()))
.thenReturn(android.util.Pair(handler, WindowContainerTransaction()))
- controller.minimizeTask(pipTask)
+ controller.minimizeTask(pipTask, MinimizeReason.MINIMIZE_BUTTON)
verifyExitDesktopWCTNotExecuted()
taskRepository.setTaskInPip(DEFAULT_DISPLAY, pipTask.taskId, enterPip = false)
@@ -2182,7 +2183,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
.thenReturn(transition)
- controller.minimizeTask(task)
+ controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
@@ -2198,7 +2199,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
whenever(transitions.dispatchRequest(any(), any(), anyOrNull()))
.thenReturn(android.util.Pair(handler, WindowContainerTransaction()))
- controller.minimizeTask(task)
+ controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
verify(freeformTaskTransitionStarter).startPipTransition(any())
verify(freeformTaskTransitionStarter, never()).startMinimizedModeTransition(any())
@@ -2210,7 +2211,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
.thenReturn(Binder())
- controller.minimizeTask(task)
+ controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
verify(freeformTaskTransitionStarter).startMinimizedModeTransition(any())
verify(freeformTaskTransitionStarter, never()).startPipTransition(any())
@@ -2223,7 +2224,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
whenever(transitions.dispatchRequest(any(), any(), anyOrNull()))
.thenReturn(android.util.Pair(handler, WindowContainerTransaction()))
- controller.minimizeTask(task)
+ controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
verify(freeformTaskTransitionStarter).startPipTransition(captor.capture())
@@ -2239,7 +2240,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
.thenReturn(transition)
- controller.minimizeTask(task)
+ controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
@@ -2255,7 +2256,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
.thenReturn(transition)
// The only active task is being minimized.
- controller.minimizeTask(task)
+ controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
@@ -2272,7 +2273,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
taskRepository.minimizeTask(DEFAULT_DISPLAY, task.taskId)
// The only active task is already minimized.
- controller.minimizeTask(task)
+ controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
@@ -2289,7 +2290,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
.thenReturn(transition)
- controller.minimizeTask(task1)
+ controller.minimizeTask(task1, MinimizeReason.MINIMIZE_BUTTON)
val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
@@ -2309,7 +2310,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId)
// task1 is the only visible task as task2 is minimized.
- controller.minimizeTask(task1)
+ controller.minimizeTask(task1, MinimizeReason.MINIMIZE_BUTTON)
// Adds remove wallpaper operation
val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture())
@@ -2324,7 +2325,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any()))
.thenReturn(transition)
- controller.minimizeTask(task)
+ controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
verify(mMockDesktopImmersiveController).exitImmersiveIfApplicable(any(), eq(task), any())
}
@@ -2341,7 +2342,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
ExitResult.Exit(exitingTask = task.taskId, runOnTransitionStart = runOnTransit)
)
- controller.minimizeTask(task)
+ controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
assertThat(runOnTransit.invocations).isEqualTo(1)
assertThat(runOnTransit.lastInvoked).isEqualTo(transition)
@@ -3285,7 +3286,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
whenever(transitions.dispatchRequest(any(), any(), anyOrNull()))
.thenReturn(android.util.Pair(handler, WindowContainerTransaction()))
- controller.minimizeTask(pipTask)
+ controller.minimizeTask(pipTask, MinimizeReason.MINIMIZE_BUTTON)
verifyExitDesktopWCTNotExecuted()
freeformTask.isFocused = true
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 c8214b3838e2..acfe1e9fd5a2 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
@@ -20,6 +20,7 @@ import android.app.ActivityManager.RunningTaskInfo
import android.graphics.Rect
import android.os.Binder
import android.os.Handler
+import android.os.IBinder
import android.os.UserManager
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
@@ -43,6 +44,7 @@ import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGAT
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
@@ -180,7 +182,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val task = setUpFreeformTask()
markTaskHidden(task)
- desktopTasksLimiter.addPendingMinimizeChange(Binder(), displayId = 1, taskId = task.taskId)
+ addPendingMinimizeChange(Binder(), displayId = 1, taskId = task.taskId)
assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isFalse()
}
@@ -208,11 +210,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val taskTransition = Binder()
val task = setUpFreeformTask()
markTaskHidden(task)
- desktopTasksLimiter.addPendingMinimizeChange(
- pendingTransition,
- displayId = DEFAULT_DISPLAY,
- taskId = task.taskId,
- )
+ addPendingMinimizeChange(pendingTransition, taskId = task.taskId)
desktopTasksLimiter
.getTransitionObserver()
@@ -231,11 +229,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val transition = Binder()
val task = setUpFreeformTask()
markTaskVisible(task)
- desktopTasksLimiter.addPendingMinimizeChange(
- transition,
- displayId = DEFAULT_DISPLAY,
- taskId = task.taskId,
- )
+ addPendingMinimizeChange(transition, taskId = task.taskId)
desktopTasksLimiter
.getTransitionObserver()
@@ -254,11 +248,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val transition = Binder()
val task = setUpFreeformTask()
markTaskHidden(task)
- desktopTasksLimiter.addPendingMinimizeChange(
- transition,
- displayId = DEFAULT_DISPLAY,
- taskId = task.taskId,
- )
+ addPendingMinimizeChange(transition, taskId = task.taskId)
desktopTasksLimiter
.getTransitionObserver()
@@ -276,11 +266,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
fun onTransitionReady_pendingTransition_changeTaskToBack_taskIsMinimized() {
val transition = Binder()
val task = setUpFreeformTask()
- desktopTasksLimiter.addPendingMinimizeChange(
- transition,
- displayId = DEFAULT_DISPLAY,
- taskId = task.taskId,
- )
+ addPendingMinimizeChange(transition, taskId = task.taskId)
desktopTasksLimiter
.getTransitionObserver()
@@ -299,11 +285,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val bounds = Rect(0, 0, 200, 200)
val transition = Binder()
val task = setUpFreeformTask()
- desktopTasksLimiter.addPendingMinimizeChange(
- transition,
- displayId = DEFAULT_DISPLAY,
- taskId = task.taskId,
- )
+ addPendingMinimizeChange(transition, taskId = task.taskId)
val change =
TransitionInfo.Change(task.token, mock(SurfaceControl::class.java)).apply {
@@ -330,11 +312,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val mergedTransition = Binder()
val newTransition = Binder()
val task = setUpFreeformTask()
- desktopTasksLimiter.addPendingMinimizeChange(
- mergedTransition,
- displayId = DEFAULT_DISPLAY,
- taskId = task.taskId,
- )
+ addPendingMinimizeChange(mergedTransition, taskId = task.taskId)
desktopTasksLimiter
.getTransitionObserver()
.onTransitionMerged(mergedTransition, newTransition)
@@ -541,11 +519,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
(1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
val transition = Binder()
val task = setUpFreeformTask()
- desktopTasksLimiter.addPendingMinimizeChange(
- transition,
- displayId = DEFAULT_DISPLAY,
- taskId = task.taskId,
- )
+ addPendingMinimizeChange(transition, taskId = task.taskId)
desktopTasksLimiter
.getTransitionObserver()
@@ -573,11 +547,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
(1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
val transition = Binder()
val task = setUpFreeformTask()
- desktopTasksLimiter.addPendingMinimizeChange(
- transition,
- displayId = DEFAULT_DISPLAY,
- taskId = task.taskId,
- )
+ addPendingMinimizeChange(transition, taskId = task.taskId)
desktopTasksLimiter
.getTransitionObserver()
@@ -606,11 +576,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
val mergedTransition = Binder()
val newTransition = Binder()
val task = setUpFreeformTask()
- desktopTasksLimiter.addPendingMinimizeChange(
- mergedTransition,
- displayId = DEFAULT_DISPLAY,
- taskId = task.taskId,
- )
+ addPendingMinimizeChange(mergedTransition, taskId = task.taskId)
desktopTasksLimiter
.getTransitionObserver()
@@ -633,6 +599,60 @@ class DesktopTasksLimiterTest : ShellTestCase() {
verify(interactionJankMonitor).end(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
}
+ @Test
+ fun getMinimizingTask_noPendingTransition_returnsNull() {
+ val transition = Binder()
+
+ assertThat(desktopTasksLimiter.getMinimizingTask(transition)).isNull()
+ }
+
+ @Test
+ fun getMinimizingTask_pendingTaskTransition_returnsTask() {
+ val transition = Binder()
+ val task = setUpFreeformTask()
+ addPendingMinimizeChange(
+ transition,
+ taskId = task.taskId,
+ minimizeReason = MinimizeReason.TASK_LIMIT,
+ )
+
+ assertThat(desktopTasksLimiter.getMinimizingTask(transition))
+ .isEqualTo(
+ createTaskDetails(taskId = task.taskId, minimizeReason = MinimizeReason.TASK_LIMIT)
+ )
+ }
+
+ @Test
+ fun getMinimizingTask_activeTaskTransition_returnsTask() {
+ val transition = Binder()
+ val task = setUpFreeformTask()
+ addPendingMinimizeChange(
+ transition,
+ taskId = task.taskId,
+ minimizeReason = MinimizeReason.TASK_LIMIT,
+ )
+ val transitionInfo =
+ TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build()
+
+ desktopTasksLimiter
+ .getTransitionObserver()
+ .onTransitionReady(
+ transition,
+ transitionInfo,
+ /* startTransaction= */ StubTransaction(),
+ /* finishTransaction= */ StubTransaction(),
+ )
+
+ assertThat(desktopTasksLimiter.getMinimizingTask(transition))
+ .isEqualTo(
+ createTaskDetails(
+ taskId = task.taskId,
+ transitionInfo = transitionInfo,
+ minimizeReason = MinimizeReason.TASK_LIMIT,
+ )
+ )
+ }
+
private fun setUpFreeformTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
val task = createFreeformTask(displayId)
`when`(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
@@ -640,6 +660,20 @@ class DesktopTasksLimiterTest : ShellTestCase() {
return task
}
+ private fun createTaskDetails(
+ displayId: Int = DEFAULT_DISPLAY,
+ taskId: Int,
+ transitionInfo: TransitionInfo? = null,
+ minimizeReason: MinimizeReason? = null,
+ ) = DesktopTasksLimiter.TaskDetails(displayId, taskId, transitionInfo, minimizeReason)
+
+ fun addPendingMinimizeChange(
+ transition: IBinder,
+ displayId: Int = DEFAULT_DISPLAY,
+ taskId: Int,
+ minimizeReason: MinimizeReason = MinimizeReason.TASK_LIMIT,
+ ) = desktopTasksLimiter.addPendingMinimizeChange(transition, displayId, taskId, minimizeReason)
+
private fun markTaskVisible(task: RunningTaskInfo) {
desktopTaskRepo.updateTask(task.displayId, task.taskId, isVisible = true)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 79e9b9c8cd77..b4791642663a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -61,6 +61,7 @@ import com.android.window.flags.Flags
import com.android.wm.shell.R
import com.android.wm.shell.desktopmode.DesktopImmersiveController
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
@@ -272,7 +273,7 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
onClickListenerCaptor.value.onClick(view)
- verify(mockDesktopTasksController).minimizeTask(decor.mTaskInfo)
+ verify(mockDesktopTasksController).minimizeTask(decor.mTaskInfo, MinimizeReason.MINIMIZE_BUTTON)
}
@Test
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index a935aacacf95..848ea0f077ba 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -531,6 +531,7 @@ android_library {
"-Adagger.fastInit=enabled",
"-Adagger.explicitBindingConflictsWithInject=ERROR",
"-Adagger.strictMultibindingValidation=enabled",
+ "-Adagger.useBindingGraphFix=ENABLED",
"-Aroom.schemaLocation=frameworks/base/packages/SystemUI/schemas",
],
kotlincflags: ["-Xjvm-default=all"],
@@ -757,6 +758,10 @@ android_library {
// TODO(b/352363800): Why do we need this?
"-J-Xmx8192M",
],
+ javacflags: [
+ "-Adagger.useBindingGraphFix=ENABLED",
+ ],
+
aaptflags: [
"--extra-packages",
"com.android.systemui",
@@ -847,7 +852,6 @@ android_robolectric_test {
"androidx.test.ext.truth",
],
-
instrumentation_for: "SystemUIRobo-stub",
java_resource_dirs: ["tests/robolectric/config"],
plugins: [
@@ -884,7 +888,6 @@ android_robolectric_test {
"androidx.test.ext.truth",
],
-
instrumentation_for: "SystemUIRobo-stub",
java_resource_dirs: ["tests/robolectric/config"],
plugins: [
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 70a74f064563..3c0480d150e0 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -1065,8 +1065,7 @@ private fun EmptyStateCta(contentPadding: PaddingValues, viewModel: BaseCommunal
) {
Icon(
imageVector = Icons.Default.Add,
- contentDescription =
- stringResource(R.string.label_for_button_in_empty_state_cta),
+ contentDescription = null,
modifier = Modifier.size(24.dp),
)
Spacer(Modifier.width(ButtonDefaults.IconSpacing))
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
index 3295dde55238..bcd4d925814b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
@@ -28,6 +28,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.internal.logging.UiEventLogger
import com.android.systemui.animation.Expandable
@@ -54,7 +55,7 @@ constructor(
VolumePanelUiEvent.VOLUME_PANEL_SPATIAL_AUDIO_POP_UP_SHOWN,
0,
null,
- viewModel.spatialAudioButtons.value.indexOfFirst { it.button.isActive }
+ viewModel.spatialAudioButtons.value.indexOfFirst { it.button.isActive },
)
val gravity = horizontalGravity or Gravity.BOTTOM
volumePanelPopup.show(expandable, gravity, { Title() }, { Content(it) })
@@ -95,14 +96,14 @@ constructor(
icon = { Icon(icon = buttonViewModel.button.icon) },
label = {
Text(
- modifier = Modifier.basicMarquee(),
text = label,
style = MaterialTheme.typography.labelMedium,
color = LocalContentColor.current,
textAlign = TextAlign.Center,
- maxLines = 2
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
)
- }
+ },
)
}
}
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/log/src/com/android/systemui/log/LogBuffer.kt
index 4b5e9de2cce7..72304a19c17d 100644
--- a/packages/SystemUI/log/src/com/android/systemui/log/LogBuffer.kt
+++ b/packages/SystemUI/log/src/com/android/systemui/log/LogBuffer.kt
@@ -75,6 +75,7 @@ constructor(
private val maxSize: Int,
private val logcatEchoTracker: LogcatEchoTracker,
private val systrace: Boolean = true,
+ private val systraceTrackName: String = DEFAULT_LOGBUFFER_TRACK_NAME,
) : MessageBuffer {
private val buffer = RingBuffer(maxSize) { LogMessageImpl.create() }
@@ -244,10 +245,11 @@ constructor(
}
private fun echoToSystrace(level: LogLevel, tag: String, strMessage: String) {
+ if (!Trace.isEnabled()) return
Trace.instantForTrack(
Trace.TRACE_TAG_APP,
- "UI Events",
- "$name - ${level.shortString} $tag: $strMessage"
+ systraceTrackName,
+ "$name - ${level.shortString} $tag: $strMessage",
)
}
@@ -261,6 +263,10 @@ constructor(
LogLevel.WTF -> Log.wtf(message.tag, strMessage, message.exception)
}
}
+
+ companion object {
+ const val DEFAULT_LOGBUFFER_TRACK_NAME = "UI Events"
+ }
}
private const val TAG = "LogBuffer"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModelTest.kt
index 4936f8559bfb..d782d1e2612c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModelTest.kt
@@ -15,16 +15,25 @@
*/
package com.android.systemui.keyguard.ui.viewmodel
+import android.platform.test.annotations.EnableFlags
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.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.transitions.blurConfig
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -46,17 +55,33 @@ class AodToPrimaryBouncerTransitionViewModelTest : SysuiTestCase() {
transitionProgress = listOf(0.0f, 0.0f, 0.3f, 0.4f, 0.5f, 1.0f),
startValue = kosmos.blurConfig.maxBlurRadiusPx,
endValue = kosmos.blurConfig.maxBlurRadiusPx,
- transitionFactory = { value, state ->
- TransitionStep(
- from = KeyguardState.AOD,
- to = KeyguardState.PRIMARY_BOUNCER,
- value = value,
- transitionState = state,
- ownerName = "AodToPrimaryBouncerTransitionViewModelTest",
- )
- },
+ transitionFactory = ::step,
actualValuesProvider = { values },
checkInterpolatedValues = false,
)
}
+
+ @Test
+ @EnableFlags(Flags.FLAG_BOUNCER_UI_REVAMP)
+ fun aodToPrimaryBouncerHidesLockscreen() =
+ testScope.runTest {
+ val lockscreenAlpha by collectValues(underTest.lockscreenAlpha)
+ val notificationAlpha by collectValues(underTest.notificationAlpha)
+
+ val transitionSteps = listOf(step(0.0f, STARTED), step(0.5f), step(1.0f, FINISHED))
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(transitionSteps, testScope)
+ runCurrent()
+
+ lockscreenAlpha.forEach { assertThat(it).isEqualTo(0.0f) }
+ notificationAlpha.forEach { assertThat(it).isEqualTo(0.0f) }
+ }
+
+ private fun step(value: Float, transitionState: TransitionState = RUNNING) =
+ TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ value = value,
+ transitionState = transitionState,
+ ownerName = "AodToPrimaryBouncerTransitionViewModelTest",
+ )
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModelTest.kt
index 0d487509a83f..4d58f7ab118e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModelTest.kt
@@ -16,21 +16,26 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.platform.test.annotations.EnableFlags
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.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.transitions.blurConfig
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -85,6 +90,21 @@ class DozingToPrimaryBouncerTransitionViewModelTest : SysuiTestCase() {
)
}
+ @Test
+ @EnableFlags(Flags.FLAG_BOUNCER_UI_REVAMP)
+ fun dozingToPrimaryBouncerHidesLockscreen() =
+ testScope.runTest {
+ val lockscreenAlpha by collectValues(underTest.lockscreenAlpha)
+ val notificationAlpha by collectValues(underTest.notificationAlpha)
+
+ val transitionSteps = listOf(step(0.0f, STARTED), step(0.5f), step(1.0f, FINISHED))
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(transitionSteps, testScope)
+ runCurrent()
+
+ lockscreenAlpha.forEach { assertThat(it).isEqualTo(0.0f) }
+ notificationAlpha.forEach { assertThat(it).isEqualTo(0.0f) }
+ }
+
private fun step(value: Float, state: TransitionState = RUNNING): TransitionStep {
return TransitionStep(
from = KeyguardState.DOZING,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
index a06353171c33..6a33b5f58820 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
@@ -54,7 +54,7 @@ class QSTileLoggerTest : SysuiTestCase() {
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- whenever(logBufferFactory.create(any(), any(), any(), any())).thenReturn(logBuffer)
+ whenever(logBufferFactory.create(any(), any(), any(), any(), any())).thenReturn(logBuffer)
val tileSpec: TileSpec = TileSpec.create("chatty_tile")
underTest =
QSTileLogger(mapOf(tileSpec to chattyLogBuffer), logBufferFactory, statusBarController)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
index 92271198cac0..8d90d38a9eca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
@@ -212,7 +212,11 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
}
@Test
- @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+ @EnableFlags(
+ PromotedNotificationUi.FLAG_NAME,
+ StatusBarNotifChips.FLAG_NAME,
+ android.app.Flags.FLAG_API_RICH_ONGOING,
+ )
fun extractContent_fromProgressStyle() {
val entry = createEntry {
setStyle(ProgressStyle().addProgressSegment(Segment(100)).setProgress(75))
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index 55be9f79e598..ca98cbf20c3a 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -140,6 +140,11 @@ public interface ActivityStarter {
void postStartActivityDismissingKeyguard(Intent intent, int delay,
@Nullable ActivityTransitionAnimator.Controller animationController,
@Nullable String customMessage);
+ /** Posts a start activity intent that dismisses keyguard. */
+ void postStartActivityDismissingKeyguard(Intent intent, int delay,
+ @Nullable ActivityTransitionAnimator.Controller animationController,
+ @Nullable String customMessage,
+ @Nullable UserHandle userHandle);
void postStartActivityDismissingKeyguard(PendingIntent intent);
/**
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 2b71c87bfa27..d363e524a9f2 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -23,7 +23,7 @@ import android.os.IRemoteCallback;
import android.view.MotionEvent;
import com.android.systemui.shared.recents.ISystemUiProxy;
-// Next ID: 36
+// Next ID: 38
oneway interface IOverviewProxy {
void onActiveNavBarRegionChanges(in Region activeRegion) = 11;
@@ -144,4 +144,14 @@ oneway interface IOverviewProxy {
* TouchInteractionService is expected to send the reply once it has finished cleaning up.
*/
void onUnbind(IRemoteCallback reply) = 35;
+
+ /**
+ * Sent when {@link TaskbarDelegate#onDisplayReady} is called.
+ */
+ void onDisplayReady(int displayId) = 36;
+
+ /**
+ * Sent when {@link TaskbarDelegate#onDisplayRemoved} is called.
+ */
+ void onDisplayRemoved(int displayId) = 37;
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
index e3b55874de6f..26bf0bc258e8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
@@ -29,6 +30,7 @@ import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
/**
* Breaks down AOD->PRIMARY BOUNCER transition into discrete steps for corresponding views to
@@ -54,6 +56,12 @@ constructor(blurConfig: BlurConfig, animationFlow: KeyguardTransitionAnimationFl
override val windowBlurRadius: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx)
+ val lockscreenAlpha: Flow<Float> =
+ if (Flags.bouncerUiRevamp()) transitionAnimation.immediatelyTransitionTo(0.0f)
+ else emptyFlow()
+
+ val notificationAlpha = lockscreenAlpha
+
override val notificationBlurRadius: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0.0f)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
index c937d5c6453d..d9ca267f9445 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor.Companion.TO_PRIMARY_BOUNCER_DURATION
import com.android.systemui.keyguard.shared.model.Edge
@@ -29,6 +30,7 @@ import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
/**
* Breaks down DOZING->PRIMARY BOUNCER transition into discrete steps for corresponding views to
@@ -64,6 +66,13 @@ constructor(private val blurConfig: BlurConfig, animationFlow: KeyguardTransitio
},
onFinish = { blurConfig.maxBlurRadiusPx },
)
+
+ val lockscreenAlpha: Flow<Float> =
+ if (Flags.bouncerUiRevamp()) transitionAnimation.immediatelyTransitionTo(0.0f)
+ else emptyFlow()
+
+ val notificationAlpha: Flow<Float> = lockscreenAlpha
+
override val notificationBlurRadius: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index eaba5d5a149c..e51e05b8ab61 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -96,9 +96,12 @@ constructor(
private val aodToGoneTransitionViewModel: AodToGoneTransitionViewModel,
private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
private val aodToOccludedTransitionViewModel: AodToOccludedTransitionViewModel,
+ private val aodToPrimaryBouncerTransitionViewModel: AodToPrimaryBouncerTransitionViewModel,
private val dozingToGoneTransitionViewModel: DozingToGoneTransitionViewModel,
private val dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel,
private val dozingToOccludedTransitionViewModel: DozingToOccludedTransitionViewModel,
+ private val dozingToPrimaryBouncerTransitionViewModel:
+ DozingToPrimaryBouncerTransitionViewModel,
private val dreamingToAodTransitionViewModel: DreamingToAodTransitionViewModel,
private val dreamingToGoneTransitionViewModel: DreamingToGoneTransitionViewModel,
private val dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
@@ -243,9 +246,11 @@ constructor(
aodToGoneTransitionViewModel.lockscreenAlpha(viewState),
aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
aodToOccludedTransitionViewModel.lockscreenAlpha(viewState),
+ aodToPrimaryBouncerTransitionViewModel.lockscreenAlpha,
dozingToGoneTransitionViewModel.lockscreenAlpha(viewState),
dozingToLockscreenTransitionViewModel.lockscreenAlpha,
dozingToOccludedTransitionViewModel.lockscreenAlpha(viewState),
+ dozingToPrimaryBouncerTransitionViewModel.lockscreenAlpha,
dreamingToAodTransitionViewModel.lockscreenAlpha,
dreamingToGoneTransitionViewModel.lockscreenAlpha,
dreamingToLockscreenTransitionViewModel.lockscreenAlpha,
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt b/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
index 6351d7d28d07..c9d6f81dc79c 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
@@ -18,6 +18,7 @@ package com.android.systemui.log
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.LogBuffer.Companion.DEFAULT_LOGBUFFER_TRACK_NAME
import com.android.systemui.log.LogBufferHelper.Companion.adjustMaxSize
import com.android.systemui.log.echo.LogcatEchoTrackerAlways
import javax.inject.Inject
@@ -27,7 +28,7 @@ class LogBufferFactory
@Inject
constructor(
private val dumpManager: DumpManager,
- private val logcatEchoTracker: LogcatEchoTracker
+ private val logcatEchoTracker: LogcatEchoTracker,
) {
@JvmOverloads
fun create(
@@ -35,9 +36,11 @@ constructor(
maxSize: Int,
systrace: Boolean = true,
alwaysLogToLogcat: Boolean = false,
+ systraceTrackName: String = DEFAULT_LOGBUFFER_TRACK_NAME,
): LogBuffer {
val echoTracker = if (alwaysLogToLogcat) LogcatEchoTrackerAlways else logcatEchoTracker
- val buffer = LogBuffer(name, adjustMaxSize(maxSize), echoTracker, systrace)
+ val buffer =
+ LogBuffer(name, adjustMaxSize(maxSize), echoTracker, systrace, systraceTrackName)
dumpManager.registerBuffer(name, buffer)
return buffer
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 3f14b55e46a1..9270fff61c43 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -236,11 +236,29 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
@Override
public void onDisplayReady(int displayId) {
CommandQueue.Callbacks.super.onDisplayReady(displayId);
+ if (mOverviewProxyService.getProxy() == null) {
+ return;
+ }
+
+ try {
+ mOverviewProxyService.getProxy().onDisplayReady(displayId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "onDisplayReady() failed", e);
+ }
}
@Override
public void onDisplayRemoved(int displayId) {
CommandQueue.Callbacks.super.onDisplayRemoved(displayId);
+ if (mOverviewProxyService.getProxy() == null) {
+ return;
+ }
+
+ try {
+ mOverviewProxyService.getProxy().onDisplayRemoved(displayId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "onDisplayRemoved() failed", e);
+ }
}
// Separated into a method to keep setDependencies() clean/readable.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/dagger/NotificationsLogModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/dagger/NotificationsLogModule.kt
index d3359d39e959..6bcce3e21998 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/dagger/NotificationsLogModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/dagger/NotificationsLogModule.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.logging.dagger
+import com.android.app.tracing.TrackGroupUtils.trackGroup
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBufferFactory
@@ -44,7 +45,11 @@ object NotificationsLogModule {
@SysUISingleton
@NotificationHeadsUpLog
fun provideNotificationHeadsUpLogBuffer(factory: LogBufferFactory): LogBuffer {
- return factory.create("NotifHeadsUpLog", 1000)
+ return factory.create(
+ "NotifHeadsUpLog",
+ 1000,
+ systraceTrackName = notifPipelineTrack("NotifHeadsUpLog"),
+ )
}
/** Provides a logging buffer for logs related to inflation of notifications. */
@@ -52,7 +57,11 @@ object NotificationsLogModule {
@SysUISingleton
@NotifInflationLog
fun provideNotifInflationLogBuffer(factory: LogBufferFactory): LogBuffer {
- return factory.create("NotifInflationLog", 250)
+ return factory.create(
+ "NotifInflationLog",
+ 250,
+ systraceTrackName = notifPipelineTrack("NotifInflationLog"),
+ )
}
/** Provides a logging buffer for all logs related to the data layer of notifications. */
@@ -60,7 +69,11 @@ object NotificationsLogModule {
@SysUISingleton
@NotifInteractionLog
fun provideNotifInteractionLogBuffer(factory: LogBufferFactory): LogBuffer {
- return factory.create("NotifInteractionLog", 50)
+ return factory.create(
+ "NotifInteractionLog",
+ 50,
+ systraceTrackName = notifPipelineTrack("NotifInteractionLog"),
+ )
}
/** Provides a logging buffer for notification interruption calculations. */
@@ -68,7 +81,11 @@ object NotificationsLogModule {
@SysUISingleton
@NotificationInterruptLog
fun provideNotificationInterruptLogBuffer(factory: LogBufferFactory): LogBuffer {
- return factory.create("NotifInterruptLog", 100)
+ return factory.create(
+ "NotifInterruptLog",
+ 100,
+ systraceTrackName = notifPipelineTrack("NotifInterruptLog"),
+ )
}
/** Provides a logging buffer for all logs related to notifications on the lockscreen. */
@@ -91,7 +108,12 @@ object NotificationsLogModule {
if (Compile.IS_DEBUG && notifPipelineFlags.isDevLoggingEnabled()) {
maxSize *= 10
}
- return factory.create("NotifLog", maxSize, Compile.IS_DEBUG /* systrace */)
+ return factory.create(
+ "NotifLog",
+ maxSize,
+ /* systrace= */ Compile.IS_DEBUG,
+ systraceTrackName = notifPipelineTrack("NotifLog"),
+ )
}
/** Provides a logging buffer for all logs related to remote input controller. */
@@ -107,7 +129,11 @@ object NotificationsLogModule {
@SysUISingleton
@NotificationRenderLog
fun provideNotificationRenderLogBuffer(factory: LogBufferFactory): LogBuffer {
- return factory.create("NotifRenderLog", 100)
+ return factory.create(
+ "NotifRenderLog",
+ 100,
+ systraceTrackName = notifPipelineTrack("NotifRenderLog"),
+ )
}
/** Provides a logging buffer for all logs related to managing notification sections. */
@@ -150,3 +176,13 @@ object NotificationsLogModule {
return factory.create("VisualStabilityLog", 50, /* maxSize */ false /* systrace */)
}
}
+
+private const val NOTIF_PIPELINE_TRACK_GROUP_NAME = "Notification pipeline"
+
+/**
+ * This generates a track name that is hierarcically collapsed inside
+ * [NOTIF_PIPELINE_TRACK_GROUP_NAME] in perfetto traces.
+ */
+private fun notifPipelineTrack(trackName: String): String {
+ return trackGroup(NOTIF_PIPELINE_TRACK_GROUP_NAME, trackName)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 495b50869458..f57107141f61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -127,7 +127,6 @@ import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrim
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrollState;
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.policy.ScrollAdapter;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
import com.android.systemui.util.Assert;
@@ -282,11 +281,9 @@ public class NotificationStackScrollLayout
private boolean mExpandedInThisMotion;
private boolean mShouldShowShelfOnly;
protected boolean mScrollingEnabled;
- private boolean mIsCurrentUserSetup;
protected FooterView mFooterView;
protected EmptyShadeView mEmptyShadeView;
private boolean mClearAllInProgress;
- private FooterClearAllListener mFooterClearAllListener;
private boolean mFlingAfterUpEvent;
/**
* Was the scroller scrolled to the top when the down motion was observed?
@@ -467,7 +464,6 @@ public class NotificationStackScrollLayout
boolean mHeadsUpAnimatingAway;
private Consumer<Boolean> mHeadsUpAnimatingAwayListener;
private int mStatusBarState;
- private int mUpcomingStatusBarState;
private boolean mHeadsUpGoingAwayAnimationsAllowed = true;
private final Runnable mReflingAndAnimateScroll = this::animateScroll;
private int mCornerRadius;
@@ -498,7 +494,6 @@ public class NotificationStackScrollLayout
private float mLastSentExpandedHeight;
private boolean mWillExpand;
private int mGapHeight;
- private boolean mIsRemoteInputActive;
/**
* The extra inset during the full shade transition
@@ -572,10 +567,8 @@ public class NotificationStackScrollLayout
private boolean mDismissUsingRowTranslationX = true;
private ExpandableNotificationRow mTopHeadsUpRow;
private NotificationStackScrollLayoutController.TouchHandler mTouchHandler;
- private final ScreenOffAnimationController mScreenOffAnimationController;
private boolean mShouldUseSplitNotificationShade;
private boolean mShouldSkipTopPaddingAnimationAfterFold = false;
- private boolean mHasFilteredOutSeenNotifications;
@Nullable private SplitShadeStateController mSplitShadeStateController = null;
private boolean mIsSmallLandscapeLockscreenEnabled = false;
private boolean mSuppressHeightUpdates;
@@ -636,9 +629,6 @@ public class NotificationStackScrollLayout
};
@Nullable
- private OnClickListener mManageButtonClickListener;
-
- @Nullable
private WallpaperInteractor mWallpaperInteractor;
public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
@@ -650,8 +640,6 @@ public class NotificationStackScrollLayout
mDebugLines = mFeatureFlags.isEnabled(Flags.NSSL_DEBUG_LINES);
mDebugRemoveAnimation = mFeatureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION);
mSectionsManager = Dependency.get(NotificationSectionsManager.class);
- mScreenOffAnimationController =
- Dependency.get(ScreenOffAnimationController.class);
mSectionsManager.initialize(this);
mSections = mSectionsManager.createSectionsForBuckets();
@@ -5403,7 +5391,6 @@ public class NotificationStackScrollLayout
println(pw, "suppressChildrenMeasureLayout", mSuppressChildrenMeasureAndLayout);
println(pw, "scrollY", mAmbientState.getScrollY());
println(pw, "showShelfOnly", mShouldShowShelfOnly);
- println(pw, "isCurrentUserSetup", mIsCurrentUserSetup);
println(pw, "hideAmount", mAmbientState.getHideAmount());
println(pw, "ambientStateSwipingUp", mAmbientState.isSwipingUp());
println(pw, "maxDisplayedNotifications", mMaxDisplayedNotifications);
@@ -6793,10 +6780,6 @@ public class NotificationStackScrollLayout
void onClearAll(@SelectedRows int selectedRows);
}
- interface FooterClearAllListener {
- void onClearAll();
- }
-
interface ClearAllAnimationListener {
void onAnimationEnd(
List<ExpandableNotificationRow> viewsToRemove, @SelectedRows int selectedRows);
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 f0455fc3a22b..c1d022600559 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
@@ -49,9 +49,11 @@ import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.AodToGoneTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.AodToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.AodToOccludedTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AodToPrimaryBouncerTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.DozingToGlanceableHubTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.DozingToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.DozingToOccludedTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DozingToPrimaryBouncerTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.GoneToAodTransitionViewModel
@@ -131,9 +133,12 @@ constructor(
private val aodToGoneTransitionViewModel: AodToGoneTransitionViewModel,
private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
private val aodToOccludedTransitionViewModel: AodToOccludedTransitionViewModel,
+ private val aodToPrimaryBouncerTransitionViewModel: AodToPrimaryBouncerTransitionViewModel,
dozingToGlanceableHubTransitionViewModel: DozingToGlanceableHubTransitionViewModel,
private val dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel,
private val dozingToOccludedTransitionViewModel: DozingToOccludedTransitionViewModel,
+ private val dozingToPrimaryBouncerTransitionViewModel:
+ DozingToPrimaryBouncerTransitionViewModel,
private val dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
private val glanceableHubToLockscreenTransitionViewModel:
GlanceableHubToLockscreenTransitionViewModel,
@@ -554,8 +559,10 @@ constructor(
aodToGoneTransitionViewModel.notificationAlpha(viewState),
aodToLockscreenTransitionViewModel.notificationAlpha,
aodToOccludedTransitionViewModel.lockscreenAlpha(viewState),
+ aodToPrimaryBouncerTransitionViewModel.notificationAlpha,
dozingToLockscreenTransitionViewModel.lockscreenAlpha,
dozingToOccludedTransitionViewModel.lockscreenAlpha(viewState),
+ dozingToPrimaryBouncerTransitionViewModel.notificationAlpha,
dreamingToLockscreenTransitionViewModel.lockscreenAlpha,
goneToAodTransitionViewModel.notificationAlpha,
goneToDreamingTransitionViewModel.lockscreenAlpha,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
index 4751293a16cc..5a63c0cd84e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
@@ -312,6 +312,25 @@ constructor(
}
}
+ override fun postStartActivityDismissingKeyguard(
+ intent: Intent,
+ delay: Int,
+ animationController: ActivityTransitionAnimator.Controller?,
+ customMessage: String?,
+ userHandle: UserHandle?,
+ ) {
+ postOnUiThread(delay) {
+ activityStarterInternal.startActivityDismissingKeyguard(
+ intent = intent,
+ onlyProvisioned = true,
+ dismissShade = true,
+ animationController = animationController,
+ customMessage = customMessage,
+ userHandle = userHandle,
+ )
+ }
+ }
+
override fun dismissKeyguardThenExecute(
action: OnDismissAction,
cancel: Runnable?,
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
index 411e06ed1339..0c8dc11f4327 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
@@ -293,7 +293,7 @@ public class QuickAccessWalletController {
intent = getSysUiWalletIntent();
}
startQuickAccessViaIntent(intent, hasCard, activityStarter,
- animationController);
+ animationController, mQuickAccessWalletClient.getUser());
});
}
@@ -323,7 +323,8 @@ public class QuickAccessWalletController {
private void startQuickAccessViaIntent(Intent intent,
boolean hasCard,
ActivityStarter activityStarter,
- ActivityTransitionAnimator.Controller animationController) {
+ ActivityTransitionAnimator.Controller animationController,
+ UserHandle user) {
if (hasCard) {
activityStarter.startActivity(intent, true /* dismissShade */,
animationController, true /* showOverLockscreenWhenLocked */);
@@ -331,7 +332,9 @@ public class QuickAccessWalletController {
activityStarter.postStartActivityDismissingKeyguard(
intent,
/* delay= */ 0,
- animationController);
+ animationController,
+ null,
+ user);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index 111492c3e227..18e1b6eb323d 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -163,15 +163,13 @@ public class WalletActivity extends ComponentActivity implements
if (mKeyguardStateController.isUnlocked()) {
mUiEventLogger.log(WalletUiEvent.QAW_SHOW_ALL);
- mActivityStarter.startActivity(
- mWalletClient.createWalletIntent(), true);
+ startWalletActivity();
finish();
} else {
mUiEventLogger.log(WalletUiEvent.QAW_UNLOCK_FROM_SHOW_ALL_BUTTON);
mKeyguardDismissUtil.executeWhenUnlocked(() -> {
mUiEventLogger.log(WalletUiEvent.QAW_SHOW_ALL);
- mActivityStarter.startActivity(
- mWalletClient.createWalletIntent(), true);
+ startWalletActivity();
finish();
return false;
}, false, true);
@@ -193,6 +191,11 @@ public class WalletActivity extends ComponentActivity implements
});
}
+ private void startWalletActivity() {
+ mActivityStarter.startActivity(mWalletClient.createWalletIntent(), true,
+ null, true, mWalletClient.getUser());
+ }
+
@Override
protected void onStart() {
super.onStart();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
index 733e2edaec84..8e97c86ba507 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.wallet.controller;
import static android.service.quickaccesswallet.Flags.FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
@@ -35,6 +36,7 @@ import static org.mockito.Mockito.when;
import android.app.PendingIntent;
import android.app.role.RoleManager;
import android.content.Intent;
+import android.os.UserHandle;
import android.platform.test.annotations.EnableFlags;
import android.service.quickaccesswallet.GetWalletCardsRequest;
import android.service.quickaccesswallet.QuickAccessWalletClient;
@@ -59,7 +61,6 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
import java.util.List;
@@ -98,6 +99,7 @@ public class QuickAccessWalletControllerTest extends SysuiTestCase {
when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(true);
when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(true);
when(mQuickAccessWalletClient.isWalletFeatureAvailableWhenDeviceLocked()).thenReturn(true);
+ when(mQuickAccessWalletClient.getUser()).thenReturn(UserHandle.of(0));
mClock.setElapsedRealtime(100L);
doAnswer(invocation -> {
@@ -269,7 +271,8 @@ public class QuickAccessWalletControllerTest extends SysuiTestCase {
public void getQuickAccessUiIntent_noCards_noPendingIntent_startsWalletActivity() {
mController.startQuickAccessUiIntent(mActivityStarter, mAnimationController, false);
verify(mActivityStarter).postStartActivityDismissingKeyguard(mIntentCaptor.capture(), eq(0),
- any(ActivityTransitionAnimator.Controller.class));
+ any(ActivityTransitionAnimator.Controller.class), eq(null),
+ eq(UserHandle.of(0)));
Intent intent = mIntentCaptor.getValue();
assertEquals(intent.getAction(), Intent.ACTION_VIEW);
assertEquals(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
index abbfa93edd17..1c0f97d294df 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
@@ -56,9 +56,11 @@ val Kosmos.keyguardRootViewModel by Fixture {
aodToGoneTransitionViewModel = aodToGoneTransitionViewModel,
aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
aodToOccludedTransitionViewModel = aodToOccludedTransitionViewModel,
+ aodToPrimaryBouncerTransitionViewModel = aodToPrimaryBouncerTransitionViewModel,
dozingToGoneTransitionViewModel = dozingToGoneTransitionViewModel,
dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel,
dozingToOccludedTransitionViewModel = dozingToOccludedTransitionViewModel,
+ dozingToPrimaryBouncerTransitionViewModel = dozingToPrimaryBouncerTransitionViewModel,
dreamingToAodTransitionViewModel = dreamingToAodTransitionViewModel,
dreamingToGoneTransitionViewModel = dreamingToGoneTransitionViewModel,
dreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
index 60e092c9709b..8461da77796d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
@@ -28,9 +28,11 @@ import com.android.systemui.keyguard.ui.viewmodel.aodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.aodToGoneTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.aodToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.aodToOccludedTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.aodToPrimaryBouncerTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.dozingToGlanceableHubTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.dozingToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.dozingToOccludedTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.dozingToPrimaryBouncerTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.dreamingToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.glanceableHubToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.goneToAodTransitionViewModel
@@ -78,9 +80,11 @@ val Kosmos.sharedNotificationContainerViewModel by Fixture {
aodToGoneTransitionViewModel = aodToGoneTransitionViewModel,
aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
aodToOccludedTransitionViewModel = aodToOccludedTransitionViewModel,
+ aodToPrimaryBouncerTransitionViewModel = aodToPrimaryBouncerTransitionViewModel,
dozingToGlanceableHubTransitionViewModel = dozingToGlanceableHubTransitionViewModel,
dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel,
dozingToOccludedTransitionViewModel = dozingToOccludedTransitionViewModel,
+ dozingToPrimaryBouncerTransitionViewModel = dozingToPrimaryBouncerTransitionViewModel,
dreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel,
goneToAodTransitionViewModel = goneToAodTransitionViewModel,
goneToDozingTransitionViewModel = goneToDozingTransitionViewModel,
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
index b94fa2f59162..8b758d29a2ac 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
@@ -39,6 +39,8 @@ import android.view.MotionEvent.PointerProperties;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
+import androidx.annotation.VisibleForTesting;
+
/**
* Implements "Automatically click on mouse stop" feature.
*
@@ -69,10 +71,10 @@ public class AutoclickController extends BaseEventStreamTransformation {
private final int mUserId;
// Lazily created on the first mouse motion event.
- private ClickScheduler mClickScheduler;
- private AutoclickSettingsObserver mAutoclickSettingsObserver;
- private AutoclickIndicatorScheduler mAutoclickIndicatorScheduler;
- private AutoclickIndicatorView mAutoclickIndicatorView;
+ @VisibleForTesting ClickScheduler mClickScheduler;
+ @VisibleForTesting AutoclickSettingsObserver mAutoclickSettingsObserver;
+ @VisibleForTesting AutoclickIndicatorScheduler mAutoclickIndicatorScheduler;
+ @VisibleForTesting AutoclickIndicatorView mAutoclickIndicatorView;
private WindowManager mWindowManager;
public AutoclickController(Context context, int userId, AccessibilityTraceManager trace) {
@@ -360,7 +362,8 @@ public class AutoclickController extends BaseEventStreamTransformation {
* moving. The click is first scheduled when a mouse movement is detected, and then further
* delayed on every sufficient mouse movement.
*/
- final private class ClickScheduler implements Runnable {
+ @VisibleForTesting
+ final class ClickScheduler implements Runnable {
/**
* Minimal distance pointer has to move relative to anchor in order for movement not to be
* discarded as noise. Anchor is the position of the last MOVE event that was not considered
@@ -474,6 +477,11 @@ public class AutoclickController extends BaseEventStreamTransformation {
}
}
+ @VisibleForTesting
+ int getDelayForTesting() {
+ return mDelay;
+ }
+
/**
* Updates the time at which click sequence should occur.
*
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java b/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java
index bf5015176f8c..f87dcdb200bb 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java
@@ -28,6 +28,8 @@ import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.LinearInterpolator;
+import androidx.annotation.VisibleForTesting;
+
// A visual indicator for the autoclick feature.
public class AutoclickIndicatorView extends View {
private static final String TAG = AutoclickIndicatorView.class.getSimpleName();
@@ -37,7 +39,7 @@ public class AutoclickIndicatorView extends View {
static final int MINIMAL_ANIMATION_DURATION = 50;
- private float mRadius = AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT;
+ private int mRadius = AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT;
private final Paint mPaint;
@@ -112,6 +114,11 @@ public class AutoclickIndicatorView extends View {
mRadius = radius;
}
+ @VisibleForTesting
+ int getRadiusForTesting() {
+ return mRadius;
+ }
+
public void redrawIndicator() {
showIndicator = true;
invalidate();
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
index abfb8268bd9a..df47c98d6433 100644
--- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
@@ -98,6 +98,8 @@ public class ContextualSearchManagerService extends SystemService {
private static final int MSG_INVALIDATE_TOKEN = 1;
private static final int MAX_TOKEN_VALID_DURATION_MS = 1_000 * 60 * 10; // 10 minutes
+ private static final boolean DEBUG = false;
+
private final Context mContext;
private final ActivityTaskManagerInternal mAtmInternal;
private final PackageManagerInternal mPackageManager;
@@ -121,6 +123,7 @@ public class ContextualSearchManagerService extends SystemService {
final Bundle data,
final int activityIndex,
final int activityCount) {
+
final IContextualSearchCallback callback;
synchronized (mLock) {
callback = mStateCallback;
@@ -160,7 +163,7 @@ public class ContextualSearchManagerService extends SystemService {
public ContextualSearchManagerService(@NonNull Context context) {
super(context);
- if (DEBUG_USER) Log.d(TAG, "ContextualSearchManagerService created");
+ if (DEBUG) Log.d(TAG, "ContextualSearchManagerService created");
mContext = context;
mAtmInternal = Objects.requireNonNull(
LocalServices.getService(ActivityTaskManagerInternal.class));
@@ -206,7 +209,7 @@ public class ContextualSearchManagerService extends SystemService {
mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_PACKAGE);
mTemporaryHandler = null;
}
- if (DEBUG_USER) Log.d(TAG, "mTemporaryPackage reset.");
+ if (DEBUG) Log.d(TAG, "mTemporaryPackage reset.");
mTemporaryPackage = null;
updateSecureSetting();
}
@@ -239,7 +242,7 @@ public class ContextualSearchManagerService extends SystemService {
mTemporaryPackage = temporaryPackage;
updateSecureSetting();
mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_PACKAGE, durationMs);
- if (DEBUG_USER) Log.d(TAG, "mTemporaryPackage set to " + mTemporaryPackage);
+ if (DEBUG) Log.d(TAG, "mTemporaryPackage set to " + mTemporaryPackage);
}
}
@@ -256,7 +259,7 @@ public class ContextualSearchManagerService extends SystemService {
+ durationMs + ")");
}
mTokenValidDurationMs = durationMs;
- if (DEBUG_USER) Log.d(TAG, "mTokenValidDurationMs set to " + durationMs);
+ if (DEBUG) Log.d(TAG, "mTokenValidDurationMs set to " + durationMs);
}
}
@@ -268,12 +271,12 @@ public class ContextualSearchManagerService extends SystemService {
private Intent getResolvedLaunchIntent(int userId) {
synchronized (this) {
- if(DEBUG_USER) Log.d(TAG, "Attempting to getResolvedLaunchIntent");
+ if(DEBUG) Log.d(TAG, "Attempting to getResolvedLaunchIntent");
// If mTemporaryPackage is not null, use it to get the ContextualSearch intent.
String csPkgName = getContextualSearchPackageName();
if (csPkgName.isEmpty()) {
// Return null if csPackageName is not specified.
- if (DEBUG_USER) Log.w(TAG, "getContextualSearchPackageName is empty");
+ if (DEBUG) Log.w(TAG, "getContextualSearchPackageName is empty");
return null;
}
Intent launchIntent = new Intent(
@@ -282,12 +285,12 @@ public class ContextualSearchManagerService extends SystemService {
ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivityAsUser(
launchIntent, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId);
if (resolveInfo == null) {
- if (DEBUG_USER) Log.w(TAG, "resolveInfo is null");
+ if (DEBUG) Log.w(TAG, "resolveInfo is null");
return null;
}
ComponentName componentName = resolveInfo.getComponentInfo().getComponentName();
if (componentName == null) {
- if (DEBUG_USER) Log.w(TAG, "componentName is null");
+ if (DEBUG) Log.w(TAG, "componentName is null");
return null;
}
launchIntent.setComponent(componentName);
@@ -298,11 +301,11 @@ public class ContextualSearchManagerService extends SystemService {
private Intent getContextualSearchIntent(int entrypoint, int userId, CallbackToken mToken) {
final Intent launchIntent = getResolvedLaunchIntent(userId);
if (launchIntent == null) {
- if (DEBUG_USER) Log.w(TAG, "Failed getContextualSearchIntent: launchIntent is null");
+ if (DEBUG) Log.w(TAG, "Failed getContextualSearchIntent: launchIntent is null");
return null;
}
- if (DEBUG_USER) Log.d(TAG, "Launch component: " + launchIntent.getComponent());
+ if (DEBUG) Log.d(TAG, "Launch component: " + launchIntent.getComponent());
launchIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION
| FLAG_ACTIVITY_NO_USER_ACTION | FLAG_ACTIVITY_CLEAR_TASK);
launchIntent.putExtra(
@@ -355,7 +358,7 @@ public class ContextualSearchManagerService extends SystemService {
TYPE_NAVIGATION_BAR_PANEL,
TYPE_POINTER));
} else {
- if (DEBUG_USER) Log.w(TAG, "Can't capture contextual screenshot: mWmInternal is null");
+ if (DEBUG) Log.w(TAG, "Can't capture contextual screenshot: mWmInternal is null");
shb = null;
}
final Bitmap bm = shb != null ? shb.asBitmap() : null;
@@ -429,7 +432,7 @@ public class ContextualSearchManagerService extends SystemService {
mTokenHandler.removeMessages(MSG_INVALIDATE_TOKEN);
mTokenHandler = null;
}
- if (DEBUG_USER) Log.d(TAG, "mToken invalidated.");
+ if (DEBUG) Log.d(TAG, "mToken invalidated.");
mToken = null;
}
}
@@ -459,7 +462,7 @@ public class ContextualSearchManagerService extends SystemService {
@Override
public void startContextualSearch(int entrypoint) {
synchronized (this) {
- if (DEBUG_USER) Log.d(TAG, "startContextualSearch entrypoint: " + entrypoint);
+ if (DEBUG) Log.d(TAG, "startContextualSearch entrypoint: " + entrypoint);
enforcePermission("startContextualSearch");
final int callingUserId = Binder.getCallingUserHandle().getIdentifier();
@@ -474,7 +477,7 @@ public class ContextualSearchManagerService extends SystemService {
getContextualSearchIntent(entrypoint, callingUserId, mToken);
if (launchIntent != null) {
int result = invokeContextualSearchIntent(launchIntent, callingUserId);
- if (DEBUG_USER) Log.d(TAG, "Launch result: " + result);
+ if (DEBUG) Log.d(TAG, "Launch result: " + result);
}
});
}
@@ -484,11 +487,11 @@ public class ContextualSearchManagerService extends SystemService {
public void getContextualSearchState(
@NonNull IBinder token,
@NonNull IContextualSearchCallback callback) {
- if (DEBUG_USER) {
+ if (DEBUG) {
Log.i(TAG, "getContextualSearchState token: " + token + ", callback: " + callback);
}
if (mToken == null || !mToken.getToken().equals(token)) {
- if (DEBUG_USER) {
+ if (DEBUG) {
Log.e(TAG, "getContextualSearchState: invalid token, returning error");
}
try {
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index c19d2c9091c3..21c9b876ab46 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -16,6 +16,9 @@
package com.android.server.display;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
@@ -1181,6 +1184,14 @@ public class BrightnessTracker {
}
public RootTaskInfo getFocusedStack() throws RemoteException {
+ if (UserManager.isVisibleBackgroundUsersEnabled()) {
+ // In MUMD (Multiple Users on Multiple Displays) system, the top most focused stack
+ // could be on the secondary display with a user signed on its display so get the
+ // root task info only on the default display.
+ return ActivityTaskManager.getService().getRootTaskInfoOnDisplay(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_UNDEFINED,
+ Display.DEFAULT_DISPLAY);
+ }
return ActivityTaskManager.getService().getFocusedRootTaskInfo();
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 0226650ec560..42a47d4a037e 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -8440,8 +8440,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mTmpConfig.updateFrom(resolvedConfig);
newParentConfiguration = mTmpConfig;
}
-
- mAppCompatController.getAspectRatioPolicy().reset();
+ final AppCompatAspectRatioPolicy aspectRatioPolicy =
+ mAppCompatController.getAspectRatioPolicy();
+ aspectRatioPolicy.reset();
mIsEligibleForFixedOrientationLetterbox = false;
mResolveConfigHint.resolveTmpOverrides(mDisplayContent, newParentConfiguration,
isFixedRotationTransforming());
@@ -8472,12 +8473,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// If activity in fullscreen mode is letterboxed because of fixed orientation then bounds
// are already calculated in resolveFixedOrientationConfiguration.
// Don't apply aspect ratio if app is overridden to fullscreen by device user/manufacturer.
- if (!mAppCompatController.getAspectRatioPolicy()
- .isLetterboxedForFixedOrientationAndAspectRatio()
- && !mAppCompatController.getAspectRatioOverrides()
- .hasFullscreenOverride()) {
- resolveAspectRatioRestriction(newParentConfiguration);
- }
+ aspectRatioPolicy.resolveAspectRatioRestrictionIfNeeded(newParentConfiguration);
final AppCompatDisplayInsets appCompatDisplayInsets = getAppCompatDisplayInsets();
final AppCompatSizeCompatModePolicy scmPolicy =
mAppCompatController.getSizeCompatModePolicy();
@@ -8509,8 +8505,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Fixed orientation letterboxing is possible on both large screen devices
// with ignoreOrientationRequest enabled and on phones in split screen even with
// ignoreOrientationRequest disabled.
- && (mAppCompatController.getAspectRatioPolicy()
- .isLetterboxedForFixedOrientationAndAspectRatio()
+ && (aspectRatioPolicy.isLetterboxedForFixedOrientationAndAspectRatio()
// Limiting check for aspect ratio letterboxing to devices with enabled
// ignoreOrientationRequest. This avoids affecting phones where apps may
// not expect the change of smallestScreenWidthDp after rotation which is
@@ -8518,7 +8513,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// accurate on phones shouldn't make the big difference and is expected
// to be already well-tested by apps.
|| (isIgnoreOrientationRequest
- && mAppCompatController.getAspectRatioPolicy().isAspectRatioApplied()))) {
+ && aspectRatioPolicy.isAspectRatioApplied()))) {
// TODO(b/264034555): Use mDisplayContent to calculate smallestScreenWidthDp from all
// rotations and only re-calculate if parent bounds have non-orientation size change.
resolvedConfig.smallestScreenWidthDp =
@@ -9007,37 +9002,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
new Rect(resolvedBounds));
}
- /**
- * Resolves aspect ratio restrictions for an activity. If the bounds are restricted by
- * aspect ratio, the position will be adjusted later in {@link #updateResolvedBoundsPosition
- * within parent's app bounds to balance the visual appearance. The policy of aspect ratio has
- * higher priority than the requested override bounds.
- */
- private void resolveAspectRatioRestriction(Configuration newParentConfiguration) {
- final Configuration resolvedConfig = getResolvedOverrideConfiguration();
- final Rect parentAppBounds = mResolveConfigHint.mParentAppBoundsOverride;
- final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
- final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
- // Use tmp bounds to calculate aspect ratio so we can know whether the activity should use
- // restricted size (resolved bounds may be the requested override bounds).
- mTmpBounds.setEmpty();
- final AppCompatAspectRatioPolicy aspectRatioPolicy = mAppCompatController
- .getAspectRatioPolicy();
- aspectRatioPolicy.applyAspectRatioForLetterbox(mTmpBounds, parentAppBounds, parentBounds);
- // If the out bounds is not empty, it means the activity cannot fill parent's app bounds,
- // then they should be aligned later in #updateResolvedBoundsPosition()
- if (!mTmpBounds.isEmpty()) {
- resolvedBounds.set(mTmpBounds);
- }
- if (!resolvedBounds.isEmpty() && !resolvedBounds.equals(parentBounds)) {
- // Compute the configuration based on the resolved bounds. If aspect ratio doesn't
- // restrict, the bounds should be the requested override bounds.
- mResolveConfigHint.mTmpOverrideDisplayInfo = getFixedRotationTransformDisplayInfo();
- computeConfigByResolveHint(resolvedConfig, newParentConfiguration);
- aspectRatioPolicy.setLetterboxBoundsForAspectRatio(new Rect(resolvedBounds));
- }
- }
-
@Override
public Rect getBounds() {
// TODO(b/268458693): Refactor configuration inheritance in case of translucent activities
diff --git a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
index 4ecd0bec9880..ab1778a1a32e 100644
--- a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
@@ -56,6 +56,8 @@ class AppCompatAspectRatioPolicy {
@NonNull
private final AppCompatAspectRatioState mAppCompatAspectRatioState;
+ private final Rect mTmpBounds = new Rect();
+
AppCompatAspectRatioPolicy(@NonNull ActivityRecord activityRecord,
@NonNull TransparentPolicy transparentPolicy,
@NonNull AppCompatOverrides appCompatOverrides) {
@@ -222,6 +224,45 @@ class AppCompatAspectRatioPolicy {
return getMaxAspectRatio() != 0 || getMinAspectRatio() != 0;
}
+ /**
+ * Resolves aspect ratio restrictions for an activity. If the bounds are restricted by
+ * aspect ratio, the position will be adjusted later in {@link #updateResolvedBoundsPosition}
+ * within parent's app bounds to balance the visual appearance. The policy of aspect ratio has
+ * higher priority than the requested override bounds.
+ */
+ void resolveAspectRatioRestrictionIfNeeded(@NonNull Configuration newParentConfiguration) {
+ // If activity in fullscreen mode is letterboxed because of fixed orientation then bounds
+ // are already calculated in resolveFixedOrientationConfiguration.
+ // Don't apply aspect ratio if app is overridden to fullscreen by device user/manufacturer.
+ if (isLetterboxedForFixedOrientationAndAspectRatio()
+ || getOverrides().hasFullscreenOverride()) {
+ return;
+ }
+ final Configuration resolvedConfig = mActivityRecord.getResolvedOverrideConfiguration();
+ final Rect parentAppBounds =
+ mActivityRecord.mResolveConfigHint.mParentAppBoundsOverride;
+ final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
+ final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
+ // Use tmp bounds to calculate aspect ratio so we can know whether the activity should
+ // use restricted size (resolved bounds may be the requested override bounds).
+ mTmpBounds.setEmpty();
+ applyAspectRatioForLetterbox(mTmpBounds, parentAppBounds, parentBounds);
+ // If the out bounds is not empty, it means the activity cannot fill parent's app
+ // bounds, then they should be aligned later in #updateResolvedBoundsPosition().
+ if (!mTmpBounds.isEmpty()) {
+ resolvedBounds.set(mTmpBounds);
+ }
+ if (!resolvedBounds.isEmpty() && !resolvedBounds.equals(parentBounds)) {
+ // Compute the configuration based on the resolved bounds. If aspect ratio doesn't
+ // restrict, the bounds should be the requested override bounds.
+ // TODO(b/384473893): Improve ActivityRecord usage here.
+ mActivityRecord.mResolveConfigHint.mTmpOverrideDisplayInfo =
+ mActivityRecord.getFixedRotationTransformDisplayInfo();
+ mActivityRecord.computeConfigByResolveHint(resolvedConfig, newParentConfiguration);
+ setLetterboxBoundsForAspectRatio(new Rect(resolvedBounds));
+ }
+ }
+
private boolean isParentFullscreenPortrait() {
final WindowContainer<?> parent = mActivityRecord.getParent();
return parent != null
@@ -364,6 +405,11 @@ class AppCompatAspectRatioPolicy {
&& !dc.getIgnoreOrientationRequest();
}
+ @NonNull
+ private AppCompatAspectRatioOverrides getOverrides() {
+ return mActivityRecord.mAppCompatController.getAspectRatioOverrides();
+ }
+
private static class AppCompatAspectRatioState {
// Whether the aspect ratio restrictions applied to the activity bounds
// in applyAspectRatio().
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7a88338d8ac5..c6136f316c3e 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -5101,6 +5101,7 @@ class Task extends TaskFragment {
mTranslucentActivityWaiting = r;
mPendingConvertFromTranslucentActivity = r;
mUndrawnActivitiesBelowTopTranslucent.clear();
+ updateTaskDescription();
mHandler.sendEmptyMessageDelayed(TRANSLUCENT_TIMEOUT_MSG, TRANSLUCENT_CONVERSION_TIMEOUT);
}
@@ -5110,6 +5111,7 @@ class Task extends TaskFragment {
+ " but is " + r);
}
mPendingConvertFromTranslucentActivity = null;
+ updateTaskDescription();
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AutoclickControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AutoclickControllerTest.java
new file mode 100644
index 000000000000..acce813ff659
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AutoclickControllerTest.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.server.testutils.MockitoUtilsKt.eq;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertNull;
+
+import android.content.Context;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.testing.TestableLooper;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/** Test cases for {@link AutoclickController}. */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class AutoclickControllerTest {
+
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ @Rule
+ public TestableContext mTestableContext =
+ new TestableContext(getInstrumentation().getContext());
+
+ private TestableLooper mTestableLooper;
+ @Mock private AccessibilityTraceManager mMockTrace;
+ @Mock private WindowManager mMockWindowManager;
+ private AutoclickController mController;
+
+ @Before
+ public void setUp() {
+ mTestableLooper = TestableLooper.get(this);
+ mTestableContext.addMockSystemService(Context.WINDOW_SERVICE, mMockWindowManager);
+ mController =
+ new AutoclickController(mTestableContext, mTestableContext.getUserId(), mMockTrace);
+ }
+
+ @After
+ public void tearDown() {
+ mTestableLooper.processAllMessages();
+ }
+
+ @Test
+ public void onMotionEvent_lazyInitClickScheduler() {
+ assertNull(mController.mClickScheduler);
+
+ injectFakeMouseActionDownEvent();
+
+ assertNotNull(mController.mClickScheduler);
+ }
+
+ @Test
+ public void onMotionEvent_nonMouseSource_notInitClickScheduler() {
+ assertNull(mController.mClickScheduler);
+
+ injectFakeNonMouseActionDownEvent();
+
+ assertNull(mController.mClickScheduler);
+ }
+
+ @Test
+ public void onMotionEvent_lazyInitAutoclickSettingsObserver() {
+ assertNull(mController.mAutoclickSettingsObserver);
+
+ injectFakeMouseActionDownEvent();
+
+ assertNotNull(mController.mAutoclickSettingsObserver);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onMotionEvent_flagOn_lazyInitAutoclickIndicatorScheduler() {
+ assertNull(mController.mAutoclickIndicatorScheduler);
+
+ injectFakeMouseActionDownEvent();
+
+ assertNotNull(mController.mAutoclickIndicatorScheduler);
+ }
+
+ @Test
+ @DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onMotionEvent_flagOff_notInitAutoclickIndicatorScheduler() {
+ assertNull(mController.mAutoclickIndicatorScheduler);
+
+ injectFakeMouseActionDownEvent();
+
+ assertNull(mController.mAutoclickIndicatorScheduler);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onMotionEvent_flagOn_lazyInitAutoclickIndicatorView() {
+ assertNull(mController.mAutoclickIndicatorView);
+
+ injectFakeMouseActionDownEvent();
+
+ assertNotNull(mController.mAutoclickIndicatorView);
+ }
+
+ @Test
+ @DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onMotionEvent_flagOff_notInitAutoclickIndicatorView() {
+ assertNull(mController.mAutoclickIndicatorView);
+
+ injectFakeMouseActionDownEvent();
+
+ assertNull(mController.mAutoclickIndicatorView);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onMotionEvent_flagOn_addAutoclickIndicatorViewToWindowManager() {
+ injectFakeMouseActionDownEvent();
+
+ verify(mMockWindowManager).addView(eq(mController.mAutoclickIndicatorView), any());
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onDestroy_flagOn_removeAutoclickIndicatorViewToWindowManager() {
+ injectFakeMouseActionDownEvent();
+
+ mController.onDestroy();
+
+ verify(mMockWindowManager).removeView(mController.mAutoclickIndicatorView);
+ }
+
+ @Test
+ public void onMotionEvent_initClickSchedulerDelayFromSetting() {
+ injectFakeMouseActionDownEvent();
+
+ int delay =
+ Settings.Secure.getIntForUser(
+ mTestableContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY,
+ AccessibilityManager.AUTOCLICK_DELAY_DEFAULT,
+ mTestableContext.getUserId());
+ assertEquals(delay, mController.mClickScheduler.getDelayForTesting());
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onMotionEvent_flagOn_initCursorAreaSizeFromSetting() {
+ injectFakeMouseActionDownEvent();
+
+ int size =
+ Settings.Secure.getIntForUser(
+ mTestableContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_AUTOCLICK_CURSOR_AREA_SIZE,
+ AccessibilityManager.AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT,
+ mTestableContext.getUserId());
+ assertEquals(size, mController.mAutoclickIndicatorView.getRadiusForTesting());
+ }
+
+ @Test
+ public void onDestroy_clearClickScheduler() {
+ injectFakeMouseActionDownEvent();
+
+ mController.onDestroy();
+
+ assertNull(mController.mClickScheduler);
+ }
+
+ @Test
+ public void onDestroy_clearAutoclickSettingsObserver() {
+ injectFakeMouseActionDownEvent();
+
+ mController.onDestroy();
+
+ assertNull(mController.mAutoclickSettingsObserver);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onDestroy_flagOn_clearAutoclickIndicatorScheduler() {
+ injectFakeMouseActionDownEvent();
+
+ mController.onDestroy();
+
+ assertNull(mController.mAutoclickIndicatorScheduler);
+ }
+
+ private void injectFakeMouseActionDownEvent() {
+ MotionEvent event = getFakeMotionDownEvent();
+ event.setSource(InputDevice.SOURCE_MOUSE);
+ mController.onMotionEvent(event, event, /* policyFlags= */ 0);
+ }
+
+ private void injectFakeNonMouseActionDownEvent() {
+ MotionEvent event = getFakeMotionDownEvent();
+ event.setSource(InputDevice.SOURCE_KEYBOARD);
+ mController.onMotionEvent(event, event, /* policyFlags= */ 0);
+ }
+
+ private MotionEvent getFakeMotionDownEvent() {
+ return MotionEvent.obtain(
+ /* downTime= */ 0,
+ /* eventTime= */ 0,
+ /* action= */ MotionEvent.ACTION_DOWN,
+ /* x= */ 0,
+ /* y= */ 0,
+ /* metaState= */ 0);
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 38d3d2fec942..724d7e7c111c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -92,6 +92,7 @@ import android.util.Xml;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.SurfaceControl;
+import android.view.WindowInsetsController;
import android.window.TaskFragmentOrganizer;
import androidx.test.filters.MediumTest;
@@ -2109,6 +2110,43 @@ public class TaskTests extends WindowTestsBase {
assertEquals(Color.RED, task.getTaskDescription().getBackgroundColor());
}
+ @Test
+ public void testUpdateTopOpaqueSystemBarsAppearanceWhenActivityBecomesTransparent() {
+ final Task task = createTask(mDisplayContent);
+ final ActivityRecord activity = createActivityRecord(task);
+ final ActivityManager.TaskDescription td = new ActivityManager.TaskDescription();
+ td.setSystemBarsAppearance(
+ WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND);
+ activity.setTaskDescription(td);
+
+ assertEquals(WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND,
+ task.getTaskDescription().getTopOpaqueSystemBarsAppearance());
+
+ activity.setOccludesParent(false);
+
+ assertEquals(0, task.getTaskDescription().getTopOpaqueSystemBarsAppearance());
+ }
+
+ @Test
+ public void testUpdateTopOpaqueSystemBarsAppearanceWhenActivityBecomesOpaque() {
+ final Task task = createTask(mDisplayContent);
+ final ActivityRecord activity = createActivityRecord(task);
+ activity.setOccludesParent(false);
+
+ final ActivityManager.TaskDescription td = new ActivityManager.TaskDescription();
+ td.setSystemBarsAppearance(
+ WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND);
+ activity.setTaskDescription(td);
+
+ assertEquals(0, task.getTaskDescription().getTopOpaqueSystemBarsAppearance());
+
+ activity.setOccludesParent(true);
+
+ assertEquals(WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND,
+ task.getTaskDescription().getTopOpaqueSystemBarsAppearance());
+
+ }
+
private Task getTestTask() {
return new TaskBuilder(mSupervisor).setCreateActivity(true).build();
}