summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/IActivityManager.aidl9
-rw-r--r--core/java/android/os/BatteryConsumer.java3
-rw-r--r--core/java/android/view/ViewRootImpl.java87
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHistory.java30
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java149
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/Operations.java27
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/PaintContext.java91
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java349
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java174
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java136
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java51
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java36
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java5
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java8
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java86
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java242
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java117
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java134
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java157
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java171
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java202
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java44
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java96
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java85
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java83
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java2
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java85
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java116
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java136
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java204
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java6
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java2
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java93
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java206
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java2
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java67
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java74
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java63
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java99
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java14
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java85
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java1
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java2
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java309
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java6
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java172
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java101
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java198
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java360
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java3
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java123
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java452
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java68
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java1
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java75
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java96
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java67
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java157
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java48
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java49
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java259
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java81
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java370
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java66
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java13
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java5
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java92
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java87
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java2
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java18
-rw-r--r--core/res/res/values/config.xml5
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java69
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java169
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java24
-rw-r--r--media/java/android/media/IMediaRouterService.aidl3
-rw-r--r--media/java/android/media/MediaRouter2.java9
-rw-r--r--packages/SettingsLib/res/values/arrays.xml20
-rw-r--r--packages/SettingsLib/res/values/strings.xml3
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt97
-rw-r--r--packages/SystemUI/res/layout/auth_container_view.xml2
-rw-r--r--packages/SystemUI/res/values/styles.xml5
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java6
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java98
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java25
-rw-r--r--services/core/java/com/android/server/am/AppProfiler.java20
-rw-r--r--services/core/java/com/android/server/am/AppStartInfoTracker.java64
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java60
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java75
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java52
-rw-r--r--services/core/java/com/android/server/media/MediaRouterService.java46
-rw-r--r--services/core/java/com/android/server/power/stats/AudioPowerStatsProcessor.java37
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryStatsImpl.java48
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java12
-rw-r--r--services/core/java/com/android/server/power/stats/FlashlightPowerStatsProcessor.java37
-rw-r--r--services/core/java/com/android/server/power/stats/VideoPowerStatsProcessor.java37
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperCropper.java30
-rw-r--r--services/permission/java/com/android/server/permission/access/appop/AppOpService.kt28
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java47
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java2
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java39
110 files changed, 7188 insertions, 1139 deletions
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 15b13dc97554..ffb920b907ab 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -757,6 +757,15 @@ interface IActivityManager {
void addStartInfoTimestamp(int key, long timestampNs, int userId);
/**
+ * Reports view related timestamps to be added to the calling apps most
+ * recent {@link ApplicationStartInfo}.
+ *
+ * @param renderThreadDrawStartTimeNs Clock monotonic time in nanoseconds of RenderThread draw start
+ * @param framePresentedTimeNs Clock monotonic time in nanoseconds of frame presented
+ */
+ oneway void reportStartInfoViewTimestamps(long renderThreadDrawStartTimeNs, long framePresentedTimeNs);
+
+ /**
* Return a list of {@link ApplicationExitInfo} records.
*
* <p class="note"> Note: System stores these historical information in a ring buffer, older
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index b41753413baf..7bdd53d00215 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -197,6 +197,9 @@ public abstract class BatteryConsumer {
POWER_COMPONENT_MOBILE_RADIO,
POWER_COMPONENT_WIFI,
POWER_COMPONENT_BLUETOOTH,
+ POWER_COMPONENT_AUDIO,
+ POWER_COMPONENT_VIDEO,
+ POWER_COMPONENT_FLASHLIGHT,
};
static final int COLUMN_INDEX_BATTERY_CONSUMER_TYPE = 0;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index beb1a5d4ee15..cb82278ca577 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -177,6 +177,7 @@ import android.graphics.Region;
import android.graphics.RenderNode;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
+import android.hardware.SyncFence;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.hardware.display.DisplayManagerGlobal;
@@ -218,6 +219,7 @@ import android.util.proto.ProtoOutputStream;
import android.view.InputDevice.InputSourceClass;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl.Transaction;
+import android.view.SurfaceControl.TransactionStats;
import android.view.View.AttachInfo;
import android.view.View.FocusDirection;
import android.view.View.MeasureSpec;
@@ -292,6 +294,7 @@ import java.util.OptionalInt;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* The top of a view hierarchy, implementing the needed protocol between View
@@ -1188,6 +1191,13 @@ public final class ViewRootImpl implements ViewParent,
private String mFpsTraceName;
private String mLargestViewTraceName;
+ private final boolean mAppStartInfoTimestampsFlagValue;
+ @GuardedBy("this")
+ private boolean mAppStartTimestampsSent = false;
+ private boolean mAppStartTrackingStarted = false;
+ private long mRenderThreadDrawStartTimeNs = -1;
+ private long mFirstFramePresentedTimeNs = -1;
+
private static boolean sToolkitSetFrameRateReadOnlyFlagValue;
private static boolean sToolkitFrameRateFunctionEnablingReadOnlyFlagValue;
private static boolean sToolkitMetricsForFrameRateDecisionFlagValue;
@@ -1304,6 +1314,8 @@ public final class ViewRootImpl implements ViewParent,
} else {
mSensitiveContentProtectionService = null;
}
+
+ mAppStartInfoTimestampsFlagValue = android.app.Flags.appStartInfoTimestamps();
}
public static void addFirstDrawHandler(Runnable callback) {
@@ -2576,6 +2588,12 @@ public final class ViewRootImpl implements ViewParent,
notifySurfaceDestroyed();
}
destroySurface();
+
+ // Reset so they can be sent again for warm starts.
+ mAppStartTimestampsSent = false;
+ mAppStartTrackingStarted = false;
+ mRenderThreadDrawStartTimeNs = -1;
+ mFirstFramePresentedTimeNs = -1;
}
}
}
@@ -4374,6 +4392,30 @@ public final class ViewRootImpl implements ViewParent,
reportDrawFinished(t, seqId);
}
});
+
+ // Only trigger once per {@link ViewRootImpl} instance, so don't add listener if
+ // {link mTransactionCompletedTimeNs} has already been set.
+ if (mAppStartInfoTimestampsFlagValue && !mAppStartTrackingStarted) {
+ mAppStartTrackingStarted = true;
+ Transaction transaction = new Transaction();
+ transaction.addTransactionCompletedListener(mExecutor,
+ new Consumer<TransactionStats>() {
+ @Override
+ public void accept(TransactionStats transactionStats) {
+ SyncFence presentFence = transactionStats.getPresentFence();
+ if (presentFence.awaitForever()) {
+ if (mFirstFramePresentedTimeNs == -1) {
+ // Only trigger once per {@link ViewRootImpl} instance.
+ mFirstFramePresentedTimeNs = presentFence.getSignalTime();
+ maybeSendAppStartTimes();
+ }
+ }
+ presentFence.close();
+ }
+ });
+ applyTransactionOnDraw(transaction);
+ }
+
if (DEBUG_BLAST) {
Log.d(mTag, "Setup new sync=" + mWmsRequestSyncGroup.getName());
}
@@ -4381,6 +4423,45 @@ public final class ViewRootImpl implements ViewParent,
mWmsRequestSyncGroup.add(this, null /* runnable */);
}
+ private void maybeSendAppStartTimes() {
+ synchronized (this) {
+ if (mAppStartTimestampsSent) {
+ // Don't send timestamps more than once.
+ return;
+ }
+
+ // If we already have {@link mRenderThreadDrawStartTimeNs} then pass it through, if not
+ // post to main thread and check if we have it there.
+ if (mRenderThreadDrawStartTimeNs != -1) {
+ sendAppStartTimesLocked();
+ } else {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (ViewRootImpl.this) {
+ if (mRenderThreadDrawStartTimeNs == -1) {
+ return;
+ }
+ sendAppStartTimesLocked();
+ }
+ }
+ });
+ }
+ }
+ }
+
+ @GuardedBy("this")
+ private void sendAppStartTimesLocked() {
+ try {
+ ActivityManager.getService().reportStartInfoViewTimestamps(
+ mRenderThreadDrawStartTimeNs, mFirstFramePresentedTimeNs);
+ mAppStartTimestampsSent = true;
+ } catch (RemoteException e) {
+ // Ignore, timestamps may be lost.
+ if (DBG) Log.d(TAG, "Exception attempting to report start timestamps.", e);
+ }
+ }
+
/**
* Helper used to notify the service to block projection when a sensitive
* view (the view displays sensitive content) is attached to the window.
@@ -5567,7 +5648,13 @@ public final class ViewRootImpl implements ViewParent,
registerCallbackForPendingTransactions();
}
+ long timeNs = SystemClock.uptimeNanos();
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
+
+ // Only trigger once per {@link ViewRootImpl} instance.
+ if (mAppStartInfoTimestampsFlagValue && mRenderThreadDrawStartTimeNs == -1) {
+ mRenderThreadDrawStartTimeNs = timeNs;
+ }
} else {
// If we get here with a disabled & requested hardware renderer, something went
// wrong (an invalidate posted right before we destroyed the hardware surface
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 244165f5e814..5c270e0e874c 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -1514,6 +1514,36 @@ public class BatteryStatsHistory {
}
/**
+ * Records an event when some state2 flag changes to true.
+ */
+ public void recordState2StartEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags,
+ int uid, String name) {
+ synchronized (this) {
+ mHistoryCur.states2 |= stateFlags;
+ mHistoryCur.eventCode = EVENT_STATE_CHANGE | EVENT_FLAG_START;
+ mHistoryCur.eventTag = mHistoryCur.localEventTag;
+ mHistoryCur.eventTag.uid = uid;
+ mHistoryCur.eventTag.string = name;
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ }
+ }
+
+ /**
+ * Records an event when some state2 flag changes to false.
+ */
+ public void recordState2StopEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags,
+ int uid, String name) {
+ synchronized (this) {
+ mHistoryCur.states2 &= ~stateFlags;
+ mHistoryCur.eventCode = EVENT_STATE_CHANGE | EVENT_FLAG_FINISH;
+ mHistoryCur.eventTag = mHistoryCur.localEventTag;
+ mHistoryCur.eventTag.uid = uid;
+ mHistoryCur.eventTag.string = name;
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ }
+ }
+
+ /**
* Records an event when some state2 flag changes to false.
*/
public void recordState2StopEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags) {
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 55f2dee95a34..00262be06623 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
@@ -30,7 +30,7 @@ public class CoreDocument {
ArrayList<Operation> mOperations;
RemoteComposeState mRemoteComposeState = new RemoteComposeState();
-
+ TimeVariables mTimeVariables = new TimeVariables();
// Semantic version of the document
Version mVersion = new Version(0, 1, 0);
@@ -70,6 +70,7 @@ public class CoreDocument {
public void setWidth(int width) {
this.mWidth = width;
+ mRemoteComposeState.setWindowWidth(width);
}
public int getHeight() {
@@ -78,6 +79,8 @@ public class CoreDocument {
public void setHeight(int height) {
this.mHeight = height;
+ mRemoteComposeState.setWindowHeight(height);
+
}
public RemoteComposeBuffer getBuffer() {
@@ -111,21 +114,21 @@ public class CoreDocument {
/**
* Sets the way the player handles the content
*
- * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
+ * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
* @param alignment set the alignment of the content (TOP|CENTER|BOTTOM|START|END)
- * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
- * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes
- * the LAYOUT modes are:
- * - LAYOUT_MATCH_PARENT
- * - LAYOUT_WRAP_CONTENT
- * or adding an horizontal mode and a vertical mode:
- * - LAYOUT_HORIZONTAL_MATCH_PARENT
- * - LAYOUT_HORIZONTAL_WRAP_CONTENT
- * - LAYOUT_HORIZONTAL_FIXED
- * - LAYOUT_VERTICAL_MATCH_PARENT
- * - LAYOUT_VERTICAL_WRAP_CONTENT
- * - LAYOUT_VERTICAL_FIXED
- * The LAYOUT_*_FIXED modes will use the intrinsic document size
+ * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
+ * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes
+ * the LAYOUT modes are:
+ * - LAYOUT_MATCH_PARENT
+ * - LAYOUT_WRAP_CONTENT
+ * or adding an horizontal mode and a vertical mode:
+ * - LAYOUT_HORIZONTAL_MATCH_PARENT
+ * - LAYOUT_HORIZONTAL_WRAP_CONTENT
+ * - LAYOUT_HORIZONTAL_FIXED
+ * - LAYOUT_VERTICAL_MATCH_PARENT
+ * - LAYOUT_VERTICAL_WRAP_CONTENT
+ * - LAYOUT_VERTICAL_FIXED
+ * The LAYOUT_*_FIXED modes will use the intrinsic document size
*/
public void setRootContentBehavior(int scroll, int alignment, int sizing, int mode) {
this.mContentScroll = scroll;
@@ -138,8 +141,8 @@ public class CoreDocument {
* Given dimensions w x h of where to paint the content, returns the corresponding scale factor
* according to the contentSizing information
*
- * @param w horizontal dimension of the rendering area
- * @param h vertical dimension of the rendering area
+ * @param w horizontal dimension of the rendering area
+ * @param h vertical dimension of the rendering area
* @param scaleOutput will contain the computed scale factor
*/
public void computeScale(float w, float h, float[] scaleOutput) {
@@ -154,37 +157,43 @@ public class CoreDocument {
float scale = Math.min(1f, Math.min(scaleX, scaleY));
contentScaleX = scale;
contentScaleY = scale;
- } break;
+ }
+ break;
case RootContentBehavior.SCALE_FIT: {
float scaleX = w / mWidth;
float scaleY = h / mHeight;
float scale = Math.min(scaleX, scaleY);
contentScaleX = scale;
contentScaleY = scale;
- } break;
+ }
+ break;
case RootContentBehavior.SCALE_FILL_WIDTH: {
float scale = w / mWidth;
contentScaleX = scale;
contentScaleY = scale;
- } break;
+ }
+ break;
case RootContentBehavior.SCALE_FILL_HEIGHT: {
float scale = h / mHeight;
contentScaleX = scale;
contentScaleY = scale;
- } break;
+ }
+ break;
case RootContentBehavior.SCALE_CROP: {
float scaleX = w / mWidth;
float scaleY = h / mHeight;
float scale = Math.max(scaleX, scaleY);
contentScaleX = scale;
contentScaleY = scale;
- } break;
+ }
+ break;
case RootContentBehavior.SCALE_FILL_BOUNDS: {
float scaleX = w / mWidth;
float scaleY = h / mHeight;
contentScaleX = scaleX;
contentScaleY = scaleY;
- } break;
+ }
+ break;
default:
// nothing
}
@@ -197,10 +206,10 @@ public class CoreDocument {
* Given dimensions w x h of where to paint the content, returns the corresponding translation
* according to the contentAlignment information
*
- * @param w horizontal dimension of the rendering area
- * @param h vertical dimension of the rendering area
- * @param contentScaleX the horizontal scale we are going to use for the content
- * @param contentScaleY the vertical scale we are going to use for the content
+ * @param w horizontal dimension of the rendering area
+ * @param h vertical dimension of the rendering area
+ * @param contentScaleX the horizontal scale we are going to use for the content
+ * @param contentScaleY the vertical scale we are going to use for the content
* @param translateOutput will contain the computed translation
*/
private void computeTranslate(float w, float h, float contentScaleX, float contentScaleY,
@@ -215,26 +224,32 @@ public class CoreDocument {
switch (horizontalContentAlignment) {
case RootContentBehavior.ALIGNMENT_START: {
// nothing
- } break;
+ }
+ break;
case RootContentBehavior.ALIGNMENT_HORIZONTAL_CENTER: {
translateX = (w - contentWidth) / 2f;
- } break;
+ }
+ break;
case RootContentBehavior.ALIGNMENT_END: {
translateX = w - contentWidth;
- } break;
+ }
+ break;
default:
// nothing (same as alignment_start)
}
switch (verticalContentAlignment) {
case RootContentBehavior.ALIGNMENT_TOP: {
// nothing
- } break;
+ }
+ break;
case RootContentBehavior.ALIGNMENT_VERTICAL_CENTER: {
translateY = (h - contentHeight) / 2f;
- } break;
+ }
+ break;
case RootContentBehavior.ALIGNMENT_BOTTOM: {
translateY = h - contentHeight;
- } break;
+ }
+ break;
default:
// nothing (same as alignment_top)
}
@@ -291,7 +306,13 @@ public class CoreDocument {
this.mMetadata = metadata;
}
- public boolean contains(float x, float y) {
+ /**
+ * Returns true if x,y coordinate is within bounds
+ * @param x x-coordinate
+ * @param y y-coordinate
+ * @return x,y coordinate is within bounds
+ */
+ public boolean contains(float x, float y) {
return x >= mLeft && x < mRight
&& y >= mTop && y < mBottom;
}
@@ -341,16 +362,22 @@ public class CoreDocument {
public void initializeContext(RemoteContext context) {
mRemoteComposeState.reset();
mClickAreas.clear();
-
+ mRemoteComposeState.setNextId(RemoteComposeState.START_ID);
context.mDocument = this;
context.mRemoteComposeState = mRemoteComposeState;
-
// mark context to be in DATA mode, which will skip the painting ops.
context.mMode = RemoteContext.ContextMode.DATA;
- for (Operation op: mOperations) {
+ mTimeVariables.updateTime(context);
+
+ for (Operation op : mOperations) {
+ if (op instanceof VariableSupport) {
+ ((VariableSupport) op).updateVariables(context);
+ ((VariableSupport) op).registerListening(context);
+ }
op.apply(context);
}
context.mMode = RemoteContext.ContextMode.UNSET;
+
}
///////////////////////////////////////////////////////////////////////////////////////////////
@@ -375,7 +402,7 @@ public class CoreDocument {
* @param minorVersion minor version number, increased when adding new features
* @param patch patch level, increased upon bugfixes
*/
- void setVersion(int majorVersion, int minorVersion, int patch) {
+ void setVersion(int majorVersion, int minorVersion, int patch) {
mVersion = new Version(majorVersion, minorVersion, patch);
}
@@ -389,13 +416,13 @@ public class CoreDocument {
* the click coordinates will be the one reported; the order of addition of those click areas
* is therefore meaningful.
*
- * @param id the id of the area, which will be reported on click
+ * @param id the id of the area, which will be reported on click
* @param contentDescription the content description (used for accessibility)
- * @param left the left coordinate of the click area (in pixels)
- * @param top the top coordinate of the click area (in pixels)
- * @param right the right coordinate of the click area (in pixels)
- * @param bottom the bottom coordinate of the click area (in pixels)
- * @param metadata arbitrary metadata associated with the are, also reported on click
+ * @param left the left coordinate of the click area (in pixels)
+ * @param top the top coordinate of the click area (in pixels)
+ * @param right the right coordinate of the click area (in pixels)
+ * @param bottom the bottom coordinate of the click area (in pixels)
+ * @param metadata arbitrary metadata associated with the are, also reported on click
*/
public void addClickArea(int id, String contentDescription,
float left, float top, float right, float bottom, String metadata) {
@@ -417,7 +444,7 @@ public class CoreDocument {
* listeners.
*/
public void onClick(float x, float y) {
- for (ClickAreaRepresentation clickArea: mClickAreas) {
+ for (ClickAreaRepresentation clickArea : mClickAreas) {
if (clickArea.contains(x, y)) {
warnClickListeners(clickArea);
}
@@ -430,7 +457,7 @@ public class CoreDocument {
* @param id the click area id
*/
public void performClick(int id) {
- for (ClickAreaRepresentation clickArea: mClickAreas) {
+ for (ClickAreaRepresentation clickArea : mClickAreas) {
if (clickArea.mId == id) {
warnClickListeners(clickArea);
}
@@ -441,17 +468,36 @@ public class CoreDocument {
* Warn click listeners when a click area is activated
*/
private void warnClickListeners(ClickAreaRepresentation clickArea) {
- for (ClickCallbacks listener: mClickListeners) {
+ for (ClickCallbacks listener : mClickListeners) {
listener.click(clickArea.mId, clickArea.mMetadata);
}
}
- ///////////////////////////////////////////////////////////////////////////////////////////////
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ for (Operation op : mOperations) {
+ builder.append(op.toString());
+ builder.append("\n");
+ }
+ return builder.toString();
+ }
+
+ //////////////////////////////////////////////////////////////////////////
// Painting
- ///////////////////////////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////
private final float[] mScaleOutput = new float[2];
private final float[] mTranslateOutput = new float[2];
+ private int mRepaintNext = -1; // delay to next repaint -1 = don't 1 = asap
+
+ /**
+ * Returns > 0 if it needs to repaint
+ * @return
+ */
+ public int needsRepaint() {
+ return mRepaintNext;
+ }
/**
* Paint the document
@@ -475,6 +521,11 @@ public class CoreDocument {
context.mPaintContext.translate(mTranslateOutput[0], mTranslateOutput[1]);
context.mPaintContext.scale(mScaleOutput[0], mScaleOutput[1]);
}
+ mTimeVariables.updateTime(context);
+ context.loadFloat(RemoteContext.ID_WINDOW_WIDTH, getWidth());
+ context.loadFloat(RemoteContext.ID_WINDOW_HEIGHT, getHeight());
+ mRepaintNext = context.updateOps();
+
for (Operation op : mOperations) {
// operations will only be executed if no theme is set (ie UNSPECIFIED)
// or the theme is equal as the one passed in argument to paint.
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 54b277a2ac58..4b45ab691215 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
@@ -19,6 +19,7 @@ import com.android.internal.widget.remotecompose.core.operations.BitmapData;
import com.android.internal.widget.remotecompose.core.operations.ClickArea;
import com.android.internal.widget.remotecompose.core.operations.ClipPath;
import com.android.internal.widget.remotecompose.core.operations.ClipRect;
+import com.android.internal.widget.remotecompose.core.operations.ColorExpression;
import com.android.internal.widget.remotecompose.core.operations.DrawArc;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmap;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmapInt;
@@ -28,9 +29,12 @@ import com.android.internal.widget.remotecompose.core.operations.DrawOval;
import com.android.internal.widget.remotecompose.core.operations.DrawPath;
import com.android.internal.widget.remotecompose.core.operations.DrawRect;
import com.android.internal.widget.remotecompose.core.operations.DrawRoundRect;
+import com.android.internal.widget.remotecompose.core.operations.DrawText;
+import com.android.internal.widget.remotecompose.core.operations.DrawTextAnchored;
import com.android.internal.widget.remotecompose.core.operations.DrawTextOnPath;
-import com.android.internal.widget.remotecompose.core.operations.DrawTextRun;
import com.android.internal.widget.remotecompose.core.operations.DrawTweenPath;
+import com.android.internal.widget.remotecompose.core.operations.FloatConstant;
+import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
import com.android.internal.widget.remotecompose.core.operations.Header;
import com.android.internal.widget.remotecompose.core.operations.MatrixRestore;
import com.android.internal.widget.remotecompose.core.operations.MatrixRotate;
@@ -42,7 +46,10 @@ import com.android.internal.widget.remotecompose.core.operations.PaintData;
import com.android.internal.widget.remotecompose.core.operations.PathData;
import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
import com.android.internal.widget.remotecompose.core.operations.RootContentDescription;
+import com.android.internal.widget.remotecompose.core.operations.ShaderData;
import com.android.internal.widget.remotecompose.core.operations.TextData;
+import com.android.internal.widget.remotecompose.core.operations.TextFromFloat;
+import com.android.internal.widget.remotecompose.core.operations.TextMerge;
import com.android.internal.widget.remotecompose.core.operations.Theme;
import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap;
@@ -67,9 +74,10 @@ public class Operations {
public static final int DRAW_BITMAP = 44;
public static final int DRAW_BITMAP_INT = 66;
public static final int DATA_BITMAP = 101;
+ public static final int DATA_SHADER = 45;
public static final int DATA_TEXT = 102;
-/////////////////////////////=====================
+ /////////////////////////////=====================
public static final int CLIP_PATH = 38;
public static final int CLIP_RECT = 39;
public static final int PAINT_VALUES = 40;
@@ -91,6 +99,12 @@ public class Operations {
public static final int MATRIX_SAVE = 130;
public static final int MATRIX_RESTORE = 131;
public static final int MATRIX_SET = 132;
+ public static final int DATA_FLOAT = 80;
+ public static final int ANIMATED_FLOAT = 81;
+ public static final int DRAW_TEXT_ANCHOR = 133;
+ public static final int COLOR_EXPRESSIONS = 134;
+ public static final int TEXT_FROM_FLOAT = 135;
+ public static final int TEXT_MERGE = 136;
/////////////////////////////////////////======================
public static IntMap<CompanionOperation> map = new IntMap<>();
@@ -114,7 +128,7 @@ public class Operations {
map.put(DRAW_RECT, DrawRect.COMPANION);
map.put(DRAW_ROUND_RECT, DrawRoundRect.COMPANION);
map.put(DRAW_TEXT_ON_PATH, DrawTextOnPath.COMPANION);
- map.put(DRAW_TEXT_RUN, DrawTextRun.COMPANION);
+ map.put(DRAW_TEXT_RUN, DrawText.COMPANION);
map.put(DRAW_TWEEN_PATH, DrawTweenPath.COMPANION);
map.put(DATA_PATH, PathData.COMPANION);
map.put(PAINT_VALUES, PaintData.COMPANION);
@@ -126,6 +140,13 @@ public class Operations {
map.put(MATRIX_TRANSLATE, MatrixTranslate.COMPANION);
map.put(CLIP_PATH, ClipPath.COMPANION);
map.put(CLIP_RECT, ClipRect.COMPANION);
+ map.put(DATA_SHADER, ShaderData.COMPANION);
+ map.put(DATA_FLOAT, FloatConstant.COMPANION);
+ map.put(ANIMATED_FLOAT, FloatExpression.COMPANION);
+ map.put(DRAW_TEXT_ANCHOR, DrawTextAnchored.COMPANION);
+ map.put(COLOR_EXPRESSIONS, ColorExpression.COMPANION);
+ map.put(TEXT_FROM_FLOAT, TextFromFloat.COMPANION);
+ map.put(TEXT_MERGE, TextMerge.COMPANION);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
index eece8ad52b60..ecd0efceacf3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
@@ -68,7 +68,35 @@ public abstract class PaintContext {
public abstract void drawTextOnPath(int textId, int pathId, float hOffset, float vOffset);
- public abstract void drawTextRun(int textID,
+ /**
+ * Return the dimensions (left, top, right, bottom).
+ * Relative to a drawTextRun x=0, y=0;
+ *
+ * @param textId
+ * @param start
+ * @param end if end is -1 it means the whole string
+ * @param monospace measure with better support for monospace
+ * @param bounds the bounds (left, top, right, bottom)
+ */
+ public abstract void getTextBounds(int textId,
+ int start,
+ int end,
+ boolean monospace,
+ float[]bounds);
+
+ /**
+ * Draw a text starting ast x,y
+ *
+ * @param textId reference to the text
+ * @param start
+ * @param end
+ * @param contextStart
+ * @param contextEnd
+ * @param x
+ * @param y
+ * @param rtl
+ */
+ public abstract void drawTextRun(int textId,
int start,
int end,
int contextStart,
@@ -77,6 +105,14 @@ public abstract class PaintContext {
float y,
boolean rtl);
+ /**
+ * Draw an interpolation between two paths
+ * @param path1Id
+ * @param path2Id
+ * @param tween 0.0 = is path1 1.0 is path2
+ * @param start
+ * @param stop
+ */
public abstract void drawTweenPath(int path1Id,
int path2Id,
float tween,
@@ -85,21 +121,70 @@ public abstract class PaintContext {
public abstract void applyPaint(PaintBundle mPaintData);
- public abstract void mtrixScale(float scaleX, float scaleY, float centerX, float centerY);
-
+ /**
+ * Scale the rendering by scaleX and saleY (1.0 = no scale).
+ * Scaling is done about centerX,centerY.
+ *
+ * @param scaleX
+ * @param scaleY
+ * @param centerX
+ * @param centerY
+ */
+ public abstract void matrixScale(float scaleX, float scaleY, float centerX, float centerY);
+
+ /**
+ * Translate the rendering
+ * @param translateX
+ * @param translateY
+ */
public abstract void matrixTranslate(float translateX, float translateY);
+ /**
+ * Skew the rendering
+ * @param skewX
+ * @param skewY
+ */
public abstract void matrixSkew(float skewX, float skewY);
+ /**
+ * Rotate the rendering.
+ * Note rotates are cumulative.
+ * @param rotate angle to rotate
+ * @param pivotX x-coordinate about which to rotate
+ * @param pivotY y-coordinate about which to rotate
+ */
public abstract void matrixRotate(float rotate, float pivotX, float pivotY);
+ /**
+ * Save the current state of the transform
+ */
public abstract void matrixSave();
+ /**
+ * Restore the previously saved state of the transform
+ */
public abstract void matrixRestore();
+ /**
+ * Set the clip to a rectangle.
+ * Drawing outside the current clip region will have no effect
+ * @param left
+ * @param top
+ * @param right
+ * @param bottom
+ */
public abstract void clipRect(float left, float top, float right, float bottom);
+ /**
+ * Clip based on a path.
+ * @param pathId
+ * @param regionOp
+ */
public abstract void clipPath(int pathId, int regionOp);
+ /**
+ * Reset the paint
+ */
+ public abstract void reset();
}
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 c2e81318c09a..52fc3143d721 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
@@ -19,6 +19,7 @@ import com.android.internal.widget.remotecompose.core.operations.BitmapData;
import com.android.internal.widget.remotecompose.core.operations.ClickArea;
import com.android.internal.widget.remotecompose.core.operations.ClipPath;
import com.android.internal.widget.remotecompose.core.operations.ClipRect;
+import com.android.internal.widget.remotecompose.core.operations.ColorExpression;
import com.android.internal.widget.remotecompose.core.operations.DrawArc;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmap;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmapInt;
@@ -28,9 +29,12 @@ import com.android.internal.widget.remotecompose.core.operations.DrawOval;
import com.android.internal.widget.remotecompose.core.operations.DrawPath;
import com.android.internal.widget.remotecompose.core.operations.DrawRect;
import com.android.internal.widget.remotecompose.core.operations.DrawRoundRect;
+import com.android.internal.widget.remotecompose.core.operations.DrawText;
+import com.android.internal.widget.remotecompose.core.operations.DrawTextAnchored;
import com.android.internal.widget.remotecompose.core.operations.DrawTextOnPath;
-import com.android.internal.widget.remotecompose.core.operations.DrawTextRun;
import com.android.internal.widget.remotecompose.core.operations.DrawTweenPath;
+import com.android.internal.widget.remotecompose.core.operations.FloatConstant;
+import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
import com.android.internal.widget.remotecompose.core.operations.Header;
import com.android.internal.widget.remotecompose.core.operations.MatrixRestore;
import com.android.internal.widget.remotecompose.core.operations.MatrixRotate;
@@ -43,8 +47,12 @@ import com.android.internal.widget.remotecompose.core.operations.PathData;
import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
import com.android.internal.widget.remotecompose.core.operations.RootContentDescription;
import com.android.internal.widget.remotecompose.core.operations.TextData;
+import com.android.internal.widget.remotecompose.core.operations.TextFromFloat;
+import com.android.internal.widget.remotecompose.core.operations.TextMerge;
import com.android.internal.widget.remotecompose.core.operations.Theme;
+import com.android.internal.widget.remotecompose.core.operations.Utils;
import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
+import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation;
import java.io.File;
import java.io.FileInputStream;
@@ -58,9 +66,20 @@ import java.util.Arrays;
* Provides an abstract buffer to encode/decode RemoteCompose operations
*/
public class RemoteComposeBuffer {
+ public static final int EASING_CUBIC_STANDARD = FloatAnimation.CUBIC_STANDARD;
+ public static final int EASING_CUBIC_ACCELERATE = FloatAnimation.CUBIC_ACCELERATE;
+ public static final int EASING_CUBIC_DECELERATE = FloatAnimation.CUBIC_DECELERATE;
+ public static final int EASING_CUBIC_LINEAR = FloatAnimation.CUBIC_LINEAR;
+ public static final int EASING_CUBIC_ANTICIPATE = FloatAnimation.CUBIC_ANTICIPATE;
+ public static final int EASING_CUBIC_OVERSHOOT = FloatAnimation.CUBIC_OVERSHOOT;
+ public static final int EASING_CUBIC_CUSTOM = FloatAnimation.CUBIC_CUSTOM;
+ public static final int EASING_SPLINE_CUSTOM = FloatAnimation.SPLINE_CUSTOM;
+ public static final int EASING_EASE_OUT_BOUNCE = FloatAnimation.EASE_OUT_BOUNCE;
+ public static final int EASING_EASE_OUT_ELASTIC = FloatAnimation.EASE_OUT_ELASTIC;
WireBuffer mBuffer = new WireBuffer();
Platform mPlatform = null;
RemoteComposeState mRemoteComposeState;
+ private static final boolean DEBUG = false;
/**
* Provides an abstract buffer to encode/decode RemoteCompose operations
@@ -171,7 +190,7 @@ public class RemoteComposeBuffer {
*
* @param text the string to inject in the buffer
*/
- int addText(String text) {
+ public int addText(String text) {
int id = mRemoteComposeState.dataGetId(text);
if (id == -1) {
id = mRemoteComposeState.cache(text);
@@ -350,7 +369,6 @@ public class RemoteComposeBuffer {
addDrawPath(id);
}
-
/**
* Draw the specified path
*
@@ -426,12 +444,160 @@ public class RemoteComposeBuffer {
float y,
boolean rtl) {
int textId = addText(text);
- DrawTextRun.COMPANION.apply(
+ DrawText.COMPANION.apply(
mBuffer, textId, start, end,
contextStart, contextEnd, x, y, rtl);
}
/**
+ * Draw the text, with origin at (x,y). The origin is interpreted
+ * based on the Align setting in the paint.
+ *
+ * @param textId The text to be drawn
+ * @param start The index of the first character in text to draw
+ * @param end (end - 1) is the index of the last character in text to draw
+ * @param contextStart
+ * @param contextEnd
+ * @param x The x-coordinate of the origin of the text being drawn
+ * @param y The y-coordinate of the baseline of the text being drawn
+ * @param rtl Draw RTTL
+ */
+ public void addDrawTextRun(int textId,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean rtl) {
+ DrawText.COMPANION.apply(
+ mBuffer, textId, start, end,
+ contextStart, contextEnd, x, y, rtl);
+ }
+
+ /**
+ * Draw a text on canvas at relative to position (x, y),
+ * offset panX and panY.
+ * <br>
+ * The panning factors (panX, panY) mapped to the
+ * resulting bounding box of the text, in such a way that a
+ * panning factor of (0.0, 0.0) would center the text at (x, y)
+ * <ul>
+ * <li> Panning of -1.0, -1.0 - the text above & right of x,y.</li>
+ * <li>Panning of 1.0, 1.0 - the text is below and to the left</li>
+ * <li>Panning of 1.0, 0.0 - the test is centered & to the right of x,y</li>
+ * </ul>
+ * Setting panY to NaN results in y being the baseline of the text.
+ *
+ * @param text text to draw
+ * @param x Coordinate of the Anchor
+ * @param y Coordinate of the Anchor
+ * @param panX justifies text -1.0=right, 0.0=center, 1.0=left
+ * @param panY position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline
+ * @param flags 1 = RTL
+ */
+ public void drawTextAnchored(String text,
+ float x,
+ float y,
+ float panX,
+ float panY,
+ int flags) {
+ int textId = addText(text);
+ DrawTextAnchored.COMPANION.apply(
+ mBuffer, textId,
+ x, y,
+ panX, panY,
+ flags);
+ }
+
+ /**
+ * Add a text and id so that it can be used
+ *
+ * @param text
+ * @return
+ */
+ public int createTextId(String text) {
+ return addText(text);
+ }
+
+ /**
+ * Merge two text (from id's) output one id
+ * @param id1 left id
+ * @param id2 right id
+ * @return new id that merges the two text
+ */
+ public int textMerge(int id1, int id2) {
+ int textId = addText(id1 + "+" + id2);
+ TextMerge.COMPANION.apply(mBuffer, textId, id1, id2);
+ return textId;
+ }
+
+ public static final int PAD_AFTER_SPACE = TextFromFloat.PAD_AFTER_SPACE;
+ public static final int PAD_AFTER_NONE = TextFromFloat.PAD_AFTER_NONE;
+ public static final int PAD_AFTER_ZERO = TextFromFloat.PAD_AFTER_ZERO;
+ public static final int PAD_PRE_SPACE = TextFromFloat.PAD_PRE_SPACE;
+ public static final int PAD_PRE_NONE = TextFromFloat.PAD_PRE_NONE;
+ public static final int PAD_PRE_ZERO = TextFromFloat.PAD_PRE_ZERO;
+
+ /**
+ * Create a TextFromFloat command which creates text from a Float.
+ *
+ * @param value The value to convert
+ * @param digitsBefore the digits before the decimal point
+ * @param digitsAfter the digits after the decimal point
+ * @param flags configure the behaviour using PAD_PRE_* and PAD_AFTER* flags
+ * @return id of the string that can be passed to drawTextAnchored
+ */
+ public int createTextFromFloat(float value, short digitsBefore,
+ short digitsAfter, int flags) {
+ String placeHolder = Utils.floatToString(value)
+ + "(" + digitsBefore + "," + digitsAfter + "," + flags + ")";
+ int id = mRemoteComposeState.dataGetId(placeHolder);
+ if (id == -1) {
+ id = mRemoteComposeState.cache(placeHolder);
+ // TextData.COMPANION.apply(mBuffer, id, text);
+ }
+ TextFromFloat.COMPANION.apply(mBuffer, id, value, digitsBefore,
+ digitsAfter, flags);
+ return id;
+ }
+
+ /**
+ * Draw a text on canvas at relative to position (x, y),
+ * offset panX and panY.
+ * <br>
+ * The panning factors (panX, panY) mapped to the
+ * resulting bounding box of the text, in such a way that a
+ * panning factor of (0.0, 0.0) would center the text at (x, y)
+ * <ul>
+ * <li> Panning of -1.0, -1.0 - the text above & right of x,y.</li>
+ * <li>Panning of 1.0, 1.0 - the text is below and to the left</li>
+ * <li>Panning of 1.0, 0.0 - the test is centered & to the right of x,y</li>
+ * </ul>
+ * Setting panY to NaN results in y being the baseline of the text.
+ *
+ * @param textId text to draw
+ * @param x Coordinate of the Anchor
+ * @param y Coordinate of the Anchor
+ * @param panX justifies text -1.0=right, 0.0=center, 1.0=left
+ * @param panY position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline
+ * @param flags 1 = RTL
+ */
+ public void drawTextAnchored(int textId,
+ float x,
+ float y,
+ float panX,
+ float panY,
+ int flags) {
+
+ DrawTextAnchored.COMPANION.apply(
+ mBuffer, textId,
+ x, y,
+ panX, panY,
+ flags);
+ }
+
+ /**
* draw an interpolation between two paths that have the same pattern
* <p>
* Warning paths objects are not immutable and this is not taken into consideration
@@ -490,6 +656,10 @@ public class RemoteComposeBuffer {
return id;
}
+ /**
+ * Adds a paint Bundle to the doc
+ * @param paint
+ */
public void addPaint(PaintBundle paint) {
PaintData.COMPANION.apply(mBuffer, paint);
}
@@ -499,7 +669,9 @@ public class RemoteComposeBuffer {
mBuffer.setIndex(0);
while (mBuffer.available()) {
int opId = mBuffer.readByte();
- System.out.println(">>> " + opId);
+ if (DEBUG) {
+ Utils.log(">> " + opId);
+ }
CompanionOperation operation = Operations.map.get(opId);
if (operation == null) {
throw new RuntimeException("Unknown operation encountered " + opId);
@@ -519,7 +691,6 @@ public class RemoteComposeBuffer {
Theme.COMPANION.apply(mBuffer, theme);
}
-
static String version() {
return "v1.0";
}
@@ -654,8 +825,8 @@ public class RemoteComposeBuffer {
/**
* Add a pre-concat of the current matrix with the specified scale.
*
- * @param scaleX The amount to scale in X
- * @param scaleY The amount to scale in Y
+ * @param scaleX The amount to scale in X
+ * @param scaleY The amount to scale in Y
*/
public void addMatrixScale(float scaleX, float scaleY) {
MatrixScale.COMPANION.apply(mBuffer, scaleX, scaleY, Float.NaN, Float.NaN);
@@ -673,12 +844,174 @@ public class RemoteComposeBuffer {
MatrixScale.COMPANION.apply(mBuffer, scaleX, scaleY, centerX, centerY);
}
+ /**
+ * sets the clip based on clip id
+ * @param pathId 0 clears the clip
+ */
public void addClipPath(int pathId) {
ClipPath.COMPANION.apply(mBuffer, pathId);
}
+ /**
+ * Sets the clip based on clip rec
+ * @param left
+ * @param top
+ * @param right
+ * @param bottom
+ */
public void addClipRect(float left, float top, float right, float bottom) {
ClipRect.COMPANION.apply(mBuffer, left, top, right, bottom);
}
+
+ /**
+ * Add a float return a NaN number pointing to that float
+ * @param value
+ * @return
+ */
+ public float addFloat(float value) {
+ int id = mRemoteComposeState.cacheFloat(value);
+ FloatConstant.COMPANION.apply(mBuffer, id, value);
+ return Utils.asNan(id);
+ }
+
+ /**
+ * Add a float that is a computation based on variables
+ * @param value A RPN style float operation i.e. "4, 3, ADD" outputs 7
+ * @return NaN id of the result of the calculation
+ */
+ public float addAnimatedFloat(float... value) {
+ int id = mRemoteComposeState.cache(value);
+ FloatExpression.COMPANION.apply(mBuffer, id, value, null);
+ return Utils.asNan(id);
+ }
+
+ /**
+ * Add a float that is a computation based on variables.
+ * see packAnimation
+ * @param value A RPN style float operation i.e. "4, 3, ADD" outputs 7
+ * @param animation Array of floats that represents animation
+ * @return NaN id of the result of the calculation
+ */
+ public float addAnimatedFloat(float[] value, float[] animation) {
+ int id = mRemoteComposeState.cache(value);
+ FloatExpression.COMPANION.apply(mBuffer, id, value, animation);
+ return Utils.asNan(id);
+ }
+
+ /**
+ * Add a color that represents the tween between two colors
+ * @param color1
+ * @param color2
+ * @param tween
+ * @return id of the color (color ids are short)
+ */
+ public short addColorExpression(int color1, int color2, float tween) {
+ ColorExpression c = new ColorExpression(0, 0, color1, color2, tween);
+ short id = (short) mRemoteComposeState.cache(c);
+ c.mId = id;
+ c.write(mBuffer);
+ return id;
+ }
+
+ /**
+ * Add a color that represents the tween between two colors where color1
+ * is the id of a color
+ * @param color1
+ * @param color2
+ * @param tween
+ * @return id of the color (color ids are short)
+ */
+ public short addColorExpression(short color1, int color2, float tween) {
+ ColorExpression c = new ColorExpression(0, 1, color1, color2, tween);
+ short id = (short) mRemoteComposeState.cache(c);
+ c.mId = id;
+ c.write(mBuffer);
+ return id;
+ }
+
+ /**
+ * Add a color that represents the tween between two colors where color2
+ * is the id of a color
+ * @param color1
+ * @param color2
+ * @param tween
+ * @return id of the color (color ids are short)
+ */
+ public short addColorExpression(int color1, short color2, float tween) {
+ ColorExpression c = new ColorExpression(0, 2, color1, color2, tween);
+ short id = (short) mRemoteComposeState.cache(c);
+ c.mId = id;
+ c.write(mBuffer);
+ return id;
+ }
+
+ /**
+ * Add a color that represents the tween between two colors where color1 &
+ * color2 are the ids of colors
+ * @param color1
+ * @param color2
+ * @param tween
+ * @return id of the color (color ids are short)
+ */
+ public short addColorExpression(short color1, short color2, float tween) {
+ ColorExpression c = new ColorExpression(0, 3, color1, color2, tween);
+ short id = (short) mRemoteComposeState.cache(c);
+ c.mId = id;
+ c.write(mBuffer);
+ return id;
+ }
+
+ /**
+ * Color calculated by Hue saturation and value.
+ * (as floats they can be variables used to create color transitions)
+ * @param hue
+ * @param sat
+ * @param value
+ * @return id of the color (color ids are short)
+ */
+ public short addColorExpression(float hue, float sat, float value) {
+ ColorExpression c = new ColorExpression(0, hue, sat, value);
+ short id = (short) mRemoteComposeState.cache(c);
+ c.mId = id;
+ c.write(mBuffer);
+ return id;
+ }
+
+ /**
+ * Color calculated by Alpha, Hue saturation and value.
+ * (as floats they can be variables used to create color transitions)
+ * @param alpha
+ * @param hue
+ * @param sat
+ * @param value
+ * @return id of the color (color ids are short)
+ */
+ public short addColorExpression(int alpha, float hue, float sat, float value) {
+ ColorExpression c = new ColorExpression(0, alpha, hue, sat, value);
+ short id = (short) mRemoteComposeState.cache(c);
+ c.mId = id;
+ c.write(mBuffer);
+ return id;
+ }
+
+ /**
+ * create and animation based on description and return as an array of
+ * floats. see addAnimatedFloat
+ * @param duration
+ * @param type
+ * @param spec
+ * @param initialValue
+ * @param wrap
+ * @return
+ */
+ public static float[] packAnimation(float duration,
+ int type,
+ float[] spec,
+ float initialValue,
+ float wrap) {
+
+ return FloatAnimation.packToFloatArray(duration, type, spec, initialValue, wrap);
+ }
+
}
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 17e8c839a080..66a37e677499 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
@@ -15,26 +15,53 @@
*/
package com.android.internal.widget.remotecompose.core;
+import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_CONTINUOUS_SEC;
+import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_TIME_IN_MIN;
+import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_TIME_IN_SEC;
+import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_WINDOW_HEIGHT;
+import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_WINDOW_WIDTH;
+
import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap;
+import java.util.ArrayList;
import java.util.HashMap;
/**
* Represents runtime state for a RemoteCompose document
+ * State includes things like the value of variables
*/
public class RemoteComposeState {
-
+ public static final int START_ID = 42;
+ private static final int MAX_FLOATS = 500;
private final IntMap<Object> mIntDataMap = new IntMap<>();
private final IntMap<Boolean> mIntWrittenMap = new IntMap<>();
private final HashMap<Object, Integer> mDataIntMap = new HashMap();
+ private final float[] mFloatMap = new float[MAX_FLOATS]; // efficient cache
+ private final int[] mColorMap = new int[MAX_FLOATS]; // efficient cache
+ private int mNextId = START_ID;
- private static int sNextId = 42;
+ {
+ for (int i = 0; i < mFloatMap.length; i++) {
+ mFloatMap[i] = Float.NaN;
+ }
+ }
- public Object getFromId(int id) {
+ /**
+ * Get Object based on id. The system will cache things like bitmaps
+ * Paths etc. They can be accessed with this command
+ * @param id
+ * @return
+ */
+ public Object getFromId(int id) {
return mIntDataMap.get(id);
}
- public boolean containsId(int id) {
+ /**
+ * true if the cache contain this id
+ * @param id
+ * @return
+ */
+ public boolean containsId(int id) {
return mIntDataMap.get(id) != null;
}
@@ -69,6 +96,65 @@ public class RemoteComposeState {
}
/**
+ * Insert an item in the cache
+ */
+ public void update(int id, Object item) {
+ mDataIntMap.remove(mIntDataMap.get(id));
+ mDataIntMap.put(item, id);
+ mIntDataMap.put(id, item);
+ }
+
+ /**
+ * Insert an item in the cache
+ */
+ public int cacheFloat(float item) {
+ int id = nextId();
+ mFloatMap[id] = item;
+ return id;
+ }
+
+ /**
+ * Insert an item in the cache
+ */
+ public void cacheFloat(int id, float item) {
+ mFloatMap[id] = item;
+ }
+
+ /**
+ * Insert an item in the cache
+ */
+ public void updateFloat(int id, float item) {
+ mFloatMap[id] = item;
+ }
+
+ /**
+ * get float
+ */
+ public float getFloat(int id) {
+ return mFloatMap[id];
+ }
+
+ /**
+ * Get the float value
+ *
+ * @param id
+ * @return
+ */
+ public int getColor(int id) {
+ return mColorMap[id];
+ }
+
+ /**
+ * Modify the color at id.
+ * @param id
+ * @param color
+ */
+ public void updateColor(int id, int color) {
+ mColorMap[id] = color;
+ }
+
+
+ /**
* Method to determine if a cached value has been written to the documents WireBuffer based on
* its id.
*/
@@ -79,22 +165,90 @@ public class RemoteComposeState {
/**
* Method to mark that a value, represented by its id, has been written to the WireBuffer
*/
- public void markWritten(int id) {
+ public void markWritten(int id) {
mIntWrittenMap.put(id, true);
}
/**
- * Clear the record of the values that have been written to the WireBuffer.
+ * Clear the record of the values that have been written to the WireBuffer.
*/
void reset() {
mIntWrittenMap.clear();
}
- public static int nextId() {
- return sNextId++;
+ /**
+ * Get the next available id
+ * @return
+ */
+ public int nextId() {
+ return mNextId++;
}
- public static void setNextId(int id) {
- sNextId = id;
+
+ /**
+ * Set the next id
+ * @param id
+ */
+ public void setNextId(int id) {
+ mNextId = id;
+ }
+
+ IntMap<ArrayList<VariableSupport>> mVarListeners = new IntMap<>();
+ ArrayList<VariableSupport> mAllVarListeners = new ArrayList<>();
+
+ private void add(int id, VariableSupport variableSupport) {
+ ArrayList<VariableSupport> v = mVarListeners.get(id);
+ if (v == null) {
+ v = new ArrayList<VariableSupport>();
+ mVarListeners.put(id, v);
+ }
+ v.add(variableSupport);
+ mAllVarListeners.add(variableSupport);
+ }
+
+ /**
+ * Commands that listen to variables add themselves.
+ * @param id
+ * @param variableSupport
+ */
+ public void listenToVar(int id, VariableSupport variableSupport) {
+ add(id, variableSupport);
+ }
+
+ /**
+ * List of Commands that need to be updated
+ * @param context
+ * @return
+ */
+ public int getOpsToUpdate(RemoteContext context) {
+ for (VariableSupport vs : mAllVarListeners) {
+ vs.updateVariables(context);
+ }
+ if (mVarListeners.get(ID_CONTINUOUS_SEC) != null) {
+ return 1;
+ }
+ if (mVarListeners.get(ID_TIME_IN_SEC) != null) {
+ return 1000;
+ }
+ if (mVarListeners.get(ID_TIME_IN_MIN) != null) {
+ return 1000 * 60;
+ }
+ return -1;
+ }
+
+ /**
+ * Set the width of the overall document on screen.
+ * @param width
+ */
+ public void setWindowWidth(float width) {
+ updateFloat(ID_WINDOW_WIDTH, width);
+ }
+
+ /**
+ * Set the width of the overall document on screen.
+ * @param height
+ */
+ public void setWindowHeight(float height) {
+ updateFloat(ID_WINDOW_HEIGHT, height);
}
}
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 d16cbc5a1a16..7e721684be0a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,10 @@
*/
package com.android.internal.widget.remotecompose.core;
+import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
+import com.android.internal.widget.remotecompose.core.operations.ShaderData;
import com.android.internal.widget.remotecompose.core.operations.Theme;
+import com.android.internal.widget.remotecompose.core.operations.Utils;
/**
* Specify an abstract context used to playback RemoteCompose documents
@@ -27,7 +30,7 @@ import com.android.internal.widget.remotecompose.core.operations.Theme;
public abstract class RemoteContext {
protected CoreDocument mDocument;
public RemoteComposeState mRemoteComposeState;
-
+ long mStart = System.nanoTime(); // todo This should be set at a hi level
protected PaintContext mPaintContext = null;
ContextMode mMode = ContextMode.UNSET;
@@ -37,9 +40,40 @@ public abstract class RemoteContext {
public float mWidth = 0f;
public float mHeight = 0f;
+ /**
+ * Load a path under an id.
+ * Paths can be use in clip drawPath and drawTweenPath
+ * @param instanceId
+ * @param floatPath
+ */
public abstract void loadPathData(int instanceId, float[] floatPath);
/**
+ * Associate a name with a give id.
+ *
+ * @param varName
+ * @param varId
+ * @param varType
+ */
+ public abstract void loadVariableName(String varName, int varId, int varType);
+
+ /**
+ * Save a color under a given id
+ * @param id
+ * @param color
+ */
+ public abstract void loadColor(int id, int color);
+
+ /**
+ * gets the time animation clock as float in seconds
+ * @return a monotonic time in seconds (arbitrary zero point)
+ */
+ public float getAnimationTime() {
+ return (System.nanoTime() - mStart) * 1E-9f;
+ }
+
+
+ /**
* The context can be used in a few different mode, allowing operations to skip being executed:
* - UNSET : all operations will get executed
* - DATA : only operations dealing with DATA (eg loading a bitmap) should execute
@@ -96,6 +130,8 @@ public abstract class RemoteContext {
public void header(int majorVersion, int minorVersion, int patchVersion,
int width, int height, long capabilities
) {
+ mRemoteComposeState.setWindowWidth(width);
+ mRemoteComposeState.setWindowHeight(height);
mDocument.setVersion(majorVersion, minorVersion, patchVersion);
mDocument.setWidth(width);
mDocument.setHeight(height);
@@ -137,9 +173,105 @@ public abstract class RemoteContext {
///////////////////////////////////////////////////////////////////////////////////////////////
// Data handling
///////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Save a bitmap under an imageId
+ * @param imageId
+ * @param width
+ * @param height
+ * @param bitmap
+ */
public abstract void loadBitmap(int imageId, int width, int height, byte[] bitmap);
+
+ /**
+ * Save a string under a given id
+ * @param id
+ * @param text
+ */
public abstract void loadText(int id, String text);
+ /**
+ * Get a string given an id
+ * @param id
+ * @return
+ */
+ public abstract String getText(int id);
+
+ /**
+ * Load a float
+ * @param id
+ * @param value
+ */
+ public abstract void loadFloat(int id, float value);
+
+ /**
+ * Load an animated float associated with an id
+ * Todo: Remove?
+ * @param id
+ * @param animatedFloat
+ */
+ public abstract void loadAnimatedFloat(int id, FloatExpression animatedFloat);
+
+ /**
+ * Save a shader under and ID
+ * @param id
+ * @param value
+ */
+ public abstract void loadShader(int id, ShaderData value);
+
+ /**
+ * Get a float given an id
+ * @param id
+ * @return
+ */
+ public abstract float getFloat(int id);
+
+ /**
+ * Get the color given and ID
+ * @param id
+ * @return
+ */
+ public abstract int getColor(int id);
+
+ /**
+ * called to notify system that a command is interested in a variable
+ * @param id
+ * @param variableSupport
+ */
+ public abstract void listensTo(int id, VariableSupport variableSupport);
+
+ /**
+ * Notify commands with variables have changed
+ * @return
+ */
+ public abstract int updateOps();
+
+ /**
+ * Get a shader given the id
+ * @param id
+ * @return
+ */
+ public abstract ShaderData getShader(int id);
+
+ public static final int ID_CONTINUOUS_SEC = 1;
+ public static final int ID_TIME_IN_SEC = 2;
+ public static final int ID_TIME_IN_MIN = 3;
+ public static final int ID_TIME_IN_HR = 4;
+ public static final int ID_WINDOW_WIDTH = 5;
+ public static final int ID_WINDOW_HEIGHT = 6;
+ public static final int ID_COMPONENT_WIDTH = 7;
+ public static final int ID_COMPONENT_HEIGHT = 8;
+ public static final int ID_CALENDAR_MONTH = 9;
+
+ public static final float FLOAT_CONTINUOUS_SEC = Utils.asNan(ID_CONTINUOUS_SEC);
+ public static final float FLOAT_TIME_IN_SEC = Utils.asNan(ID_TIME_IN_SEC);
+ public static final float FLOAT_TIME_IN_MIN = Utils.asNan(ID_TIME_IN_MIN);
+ public static final float FLOAT_TIME_IN_HR = Utils.asNan(ID_TIME_IN_HR);
+ public static final float FLOAT_CALENDAR_MONTH = Utils.asNan(ID_CALENDAR_MONTH);
+ public static final float FLOAT_WINDOW_WIDTH = Utils.asNan(ID_WINDOW_WIDTH);
+ public static final float FLOAT_WINDOW_HEIGHT = Utils.asNan(ID_WINDOW_HEIGHT);
+ public static final float FLOAT_COMPONENT_WIDTH = Utils.asNan(ID_COMPONENT_WIDTH);
+ public static final float FLOAT_COMPONENT_HEIGHT = Utils.asNan(ID_COMPONENT_HEIGHT);
///////////////////////////////////////////////////////////////////////////////////////////////
// Click handling
///////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
new file mode 100644
index 000000000000..e9708b75de27
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
@@ -0,0 +1,51 @@
+/*
+ * 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;
+
+import java.time.LocalDateTime;
+
+/**
+ * This generates the standard system variables for time.
+ */
+public class TimeVariables {
+ /**
+ * This class populates all time variables in the system
+ * @param context
+ */
+ public void updateTime(RemoteContext context) {
+ LocalDateTime dateTime = LocalDateTime.now();
+ // This define the time in the format
+ // seconds run from Midnight=0 quantized to seconds hour 0..3599
+ // minutes run from Midnight=0 quantized to minutes 0..1439
+ // hours run from Midnight=0 quantized to Hours 0-23
+ // CONTINUOUS_SEC is seconds from midnight looping every hour 0-3600
+ // CONTINUOUS_SEC is accurate to milliseconds due to float precession
+ int month = dateTime.getDayOfMonth();
+ int hour = dateTime.getHour();
+ int minute = dateTime.getMinute();
+ int seconds = dateTime.getSecond();
+ int currentMinute = hour * 60 + minute;
+ int currentSeconds = minute * 60 + seconds;
+ float sec = currentSeconds + dateTime.getNano() * 1E-9f;
+
+ context.loadFloat(RemoteContext.ID_CONTINUOUS_SEC, sec);
+ context.loadFloat(RemoteContext.ID_TIME_IN_SEC, currentSeconds);
+ context.loadFloat(RemoteContext.ID_TIME_IN_MIN, currentMinute);
+ context.loadFloat(RemoteContext.ID_TIME_IN_HR, hour);
+ context.loadFloat(RemoteContext.ID_CALENDAR_MONTH, month);
+
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java b/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java
new file mode 100644
index 000000000000..d59b1bc65256
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java
@@ -0,0 +1,36 @@
+/*
+ * 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;
+
+/**
+ * Interface for operators that interact with variables
+ * Threw this they register to listen to particular variables
+ * and are notified when they change
+ */
+public interface VariableSupport {
+ /**
+ * Call to allow an operator to register interest in variables.
+ * Typically they call context.listensTo(id, this)
+ * @param context
+ */
+ void registerListening(RemoteContext context);
+
+ /**
+ * Called to be notified that the variables you are interested have changed.
+ * @param context
+ */
+ void updateVariables(RemoteContext context);
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
index 76b714443990..f1863225b766 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
@@ -51,11 +51,12 @@ public class BitmapData implements Operation {
@Override
public String toString() {
- return "BITMAP DATA $imageId";
+ return "BITMAP DATA " + mImageId;
}
public static class Companion implements CompanionOperation {
- private Companion() {}
+ private Companion() {
+ }
@Override
public String name() {
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 8d4a787148ef..e6d5fe7043fd 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
@@ -24,6 +24,11 @@ import com.android.internal.widget.remotecompose.core.WireBuffer;
import java.util.List;
+/**
+ * Defines a path that clips a the subsequent drawing commands
+ * Use MatrixSave and MatrixRestore commands to remove clip
+ * TODO allow id 0 to mean null?
+ */
public class ClipPath extends PaintOperation {
public static final Companion COMPANION = new Companion();
int mId;
@@ -93,5 +98,4 @@ public class ClipPath extends PaintOperation {
public void paint(PaintContext context) {
context.clipPath(mId, mRegionOp);
}
-}
-
+} \ No newline at end of file
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java
index 803618a91737..613ecebf9100 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java
@@ -15,88 +15,36 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
-import java.util.List;
-
-public class ClipRect extends PaintOperation {
- public static final Companion COMPANION = new Companion();
- float mLeft;
- float mTop;
- float mRight;
- float mBottom;
+/**
+ * Support clip with a rectangle
+ */
+public class ClipRect extends DrawBase4 {
+ public static final Companion COMPANION =
+ new Companion(Operations.CLIP_RECT) {
+ @Override
+ public Operation construct(float x1,
+ float y1,
+ float x2,
+ float y2) {
+ return new ClipRect(x1, y1, x2, y2);
+ }
+ };
public ClipRect(
float left,
float top,
float right,
float bottom) {
- mLeft = left;
- mTop = top;
- mRight = right;
- mBottom = bottom;
-
- }
-
- @Override
- public void write(WireBuffer buffer) {
- COMPANION.apply(buffer, mLeft, mTop, mRight, mBottom);
- }
-
- @Override
- public String toString() {
- return "ClipRect " + mLeft + " " + mTop
- + " " + mRight + " " + mBottom + ";";
- }
-
- public static class Companion implements CompanionOperation {
- private Companion() {
- }
-
- @Override
- public void read(WireBuffer buffer, List<Operation> operations) {
- float sLeft = buffer.readFloat();
- float srcTop = buffer.readFloat();
- float srcRight = buffer.readFloat();
- float srcBottom = buffer.readFloat();
-
- ClipRect op = new ClipRect(sLeft, srcTop, srcRight, srcBottom);
- operations.add(op);
- }
-
- @Override
- public String name() {
- return "ClipRect";
- }
-
- @Override
- public int id() {
- return Operations.CLIP_RECT;
- }
-
- public void apply(WireBuffer buffer,
- float left,
- float top,
- float right,
- float bottom) {
- buffer.start(Operations.CLIP_RECT);
- buffer.writeFloat(left);
- buffer.writeFloat(top);
- buffer.writeFloat(right);
- buffer.writeFloat(bottom);
- }
+ super(left, top, right, bottom);
+ mName = "ClipRect";
}
@Override
public void paint(PaintContext context) {
- context.clipRect(mLeft,
- mTop,
- mRight,
- mBottom);
+ context.clipRect(mX1, mY1, mX2, mY2);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
new file mode 100644
index 000000000000..7d28cea35850
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Operation to Colors
+ * Color modes
+ * mMode = 0 two colors and a tween
+ * mMode = 1 color1 is a colorID.
+ * mMode = 2 color2 is a colorID.
+ * mMode = 3 color1 & color2 are ids
+ * mMode = 4 H S V mode
+ */
+public class ColorExpression implements Operation, VariableSupport {
+ public int mId;
+ int mMode;
+ public int mColor1;
+ public int mColor2;
+ public float mTween = 0.0f;
+
+
+ public float mHue = 0; // only in Mode 4
+ public float mSat = 0;
+ public float mValue = 0;
+ public float mOutHue = 0; // only in Mode 4
+ public float mOutSat = 0;
+ public float mOutValue = 0;
+ public int mAlpha = 0xFF; // only used in hsv mode
+
+ public float mOutTween = 0.0f;
+ public int mOutColor1;
+ public int mOutColor2;
+ public static final Companion COMPANION = new Companion();
+ public static final int HSV_MODE = 4;
+ public ColorExpression(int id, float hue, float sat, float value) {
+ mMode = HSV_MODE;
+ mAlpha = 0xFF;
+ mOutHue = mHue = hue;
+ mOutSat = mSat = sat;
+ mOutValue = mValue = value;
+ mColor1 = Float.floatToRawIntBits(hue);
+ mColor2 = Float.floatToRawIntBits(sat);
+ mTween = value;
+ }
+ public ColorExpression(int id, int alpha, float hue, float sat, float value) {
+ mMode = HSV_MODE;
+ mAlpha = alpha;
+ mOutHue = mHue = hue;
+ mOutSat = mSat = sat;
+ mOutValue = mValue = value;
+ mColor1 = Float.floatToRawIntBits(hue);
+ mColor2 = Float.floatToRawIntBits(sat);
+ mTween = value;
+ }
+
+ public ColorExpression(int id, int mode, int color1, int color2, float tween) {
+ this.mId = id;
+ this.mMode = mode & 0xFF;
+ this.mAlpha = (mode >> 16) & 0xFF;
+ if (mMode == HSV_MODE) {
+ mOutHue = mHue = Float.intBitsToFloat(color1);
+ mOutSat = mSat = Float.intBitsToFloat(color2);
+ mOutValue = mValue = tween;
+ }
+ this.mColor1 = color1;
+ this.mColor2 = color2;
+ this.mTween = tween;
+ this.mOutTween = tween;
+ this.mOutColor1 = color1;
+ this.mOutColor2 = color2;
+
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ if (mMode == 4) {
+ if (Float.isNaN(mHue)) {
+ mOutHue = context.getFloat(Utils.idFromNan(mHue));
+ }
+ if (Float.isNaN(mSat)) {
+ mOutSat = context.getFloat(Utils.idFromNan(mSat));
+ }
+ if (Float.isNaN(mValue)) {
+ mOutValue = context.getFloat(Utils.idFromNan(mValue));
+ }
+ }
+ if (Float.isNaN(mTween)) {
+ mOutTween = context.getFloat(Utils.idFromNan(mTween));
+ }
+ if ((mMode & 1) == 1) {
+ mOutColor1 = context.getColor(mColor1);
+ }
+ if ((mMode & 2) == 2) {
+ mOutColor2 = context.getColor(mColor2);
+ }
+ }
+
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ if (mMode == 4) {
+ if (Float.isNaN(mHue)) {
+ context.listensTo(Utils.idFromNan(mHue), this);
+ }
+ if (Float.isNaN(mSat)) {
+ context.listensTo(Utils.idFromNan(mSat), this);
+ }
+ if (Float.isNaN(mValue)) {
+ context.listensTo(Utils.idFromNan(mValue), this);
+ }
+ return;
+ }
+ if (Float.isNaN(mTween)) {
+ context.listensTo(Utils.idFromNan(mTween), this);
+ }
+ if ((mMode & 1) == 1) {
+ context.listensTo(mColor1, this);
+ }
+ if ((mMode & 2) == 2) {
+ context.listensTo(mColor2, this);
+ }
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ if (mMode == 4) {
+ context.loadColor(mId, (mAlpha << 24)
+ | (0xFFFFFF & Utils.hsvToRgb(mOutHue, mOutSat, mOutValue)));
+ return;
+ }
+ if (mOutTween == 0.0) {
+ context.loadColor(mId, mColor1);
+ } else {
+ if ((mMode & 1) == 1) {
+ mOutColor1 = context.getColor(mColor1);
+ }
+ if ((mMode & 2) == 2) {
+ mOutColor2 = context.getColor(mColor2);
+ }
+
+ context.loadColor(mId,
+ Utils.interpolateColor(mOutColor1, mOutColor2, mOutTween));
+ }
+
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ int mode = mMode | (mAlpha << 16);
+ COMPANION.apply(buffer, mId, mode, mColor1, mColor2, mTween);
+ }
+
+ @Override
+ public String toString() {
+ if (mMode == 4) {
+ return "ColorExpression[" + mId + "] = hsv (" + Utils.floatToString(mHue)
+ + ", " + Utils.floatToString(mSat)
+ + ", " + Utils.floatToString(mValue) + ")";
+ }
+
+ String c1 = (mMode & 1) == 1 ? "[" + mColor1 + "]" : Utils.colorInt(mColor1);
+ String c2 = (mMode & 2) == 2 ? "[" + mColor2 + "]" : Utils.colorInt(mColor2);
+ return "ColorExpression[" + mId + "] = tween(" + c1
+ + ", " + c2 + ", "
+ + Utils.floatToString(mTween) + ")";
+ }
+
+ public static class Companion implements CompanionOperation {
+ private Companion() {
+ }
+
+ @Override
+ public String name() {
+ return "ColorExpression";
+ }
+
+ @Override
+ public int id() {
+ return Operations.COLOR_EXPRESSIONS;
+ }
+
+ /**
+ * Call to write a ColorExpression object on the buffer
+ * @param buffer
+ * @param id of the ColorExpression object
+ * @param mode if colors are id or actual values
+ * @param color1
+ * @param color2
+ * @param tween
+ */
+ public void apply(WireBuffer buffer,
+ int id, int mode,
+ int color1, int color2, float tween) {
+ buffer.start(Operations.COLOR_EXPRESSIONS);
+ buffer.writeInt(id);
+ buffer.writeInt(mode);
+ buffer.writeInt(color1);
+ buffer.writeInt(color2);
+ buffer.writeFloat(tween);
+
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ int id = buffer.readInt();
+ int mode = buffer.readInt();
+ int color1 = buffer.readInt();
+ int color2 = buffer.readInt();
+ float tween = buffer.readFloat();
+
+ operations.add(new ColorExpression(id, mode, color1, color2, tween));
+ }
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java
index e829975cd39b..c1768647bde6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java
@@ -15,107 +15,36 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
-import java.util.List;
-
-public class DrawArc extends PaintOperation {
- public static final Companion COMPANION = new Companion();
- float mLeft;
- float mTop;
- float mRight;
- float mBottom;
- float mStartAngle;
- float mSweepAngle;
-
- public DrawArc(
- float left,
- float top,
- float right,
- float bottom,
- float startAngle,
- float sweepAngle) {
- mLeft = left;
- mTop = top;
- mRight = right;
- mBottom = bottom;
- mStartAngle = startAngle;
- mSweepAngle = sweepAngle;
- }
-
- @Override
- public void write(WireBuffer buffer) {
- COMPANION.apply(buffer, mLeft,
- mTop,
- mRight,
- mBottom,
- mStartAngle,
- mSweepAngle);
- }
-
- @Override
- public String toString() {
- return "DrawArc " + mLeft + " " + mTop
- + " " + mRight + " " + mBottom + " "
- + "- " + mStartAngle + " " + mSweepAngle + ";";
- }
-
- public static class Companion implements CompanionOperation {
- private Companion() {
- }
-
- @Override
- public void read(WireBuffer buffer, List<Operation> operations) {
- float sLeft = buffer.readFloat();
- float srcTop = buffer.readFloat();
- float srcRight = buffer.readFloat();
- float srcBottom = buffer.readFloat();
- float mStartAngle = buffer.readFloat();
- float mSweepAngle = buffer.readFloat();
- DrawArc op = new DrawArc(sLeft, srcTop, srcRight, srcBottom,
- mStartAngle, mSweepAngle);
- operations.add(op);
- }
-
- @Override
- public String name() {
- return "DrawArc";
- }
-
- @Override
- public int id() {
- return Operations.DRAW_ARC;
- }
-
- public void apply(WireBuffer buffer,
- float left,
- float top,
- float right,
- float bottom,
- float startAngle,
- float sweepAngle) {
- buffer.start(Operations.DRAW_ARC);
- buffer.writeFloat(left);
- buffer.writeFloat(top);
- buffer.writeFloat(right);
- buffer.writeFloat(bottom);
- buffer.writeFloat(startAngle);
- buffer.writeFloat(sweepAngle);
- }
+public class DrawArc extends DrawBase6 {
+ public static final Companion COMPANION =
+ new Companion(Operations.DRAW_ARC) {
+ @Override
+ public Operation construct(float v1,
+ float v2,
+ float v3,
+ float v4,
+ float v5,
+ float v6) {
+ return new DrawArc(v1, v2, v3, v4, v5, v6);
+ }
+ };
+
+ public DrawArc(float v1,
+ float v2,
+ float v3,
+ float v4,
+ float v5,
+ float v6) {
+ super(v1, v2, v3, v4, v5, v6);
+ mName = "DrawArc";
}
@Override
public void paint(PaintContext context) {
- context.drawArc(mLeft,
- mTop,
- mRight,
- mBottom,
- mStartAngle,
- mSweepAngle);
+ context.drawArc(mV1, mV2, mV3, mV4, mV5, mV6);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
new file mode 100644
index 000000000000..0963c1337b70
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Base class for commands that take 3 float
+ */
+public abstract class DrawBase2 extends PaintOperation
+ implements VariableSupport {
+ public static final Companion COMPANION =
+ new Companion(Operations.DRAW_CIRCLE) {
+ @Override
+ public Operation construct(float x1, float y1) {
+ // subclass should return new DrawX(x1, y1);
+ return null;
+ }
+ };
+ protected String mName = "DrawRectBase";
+ float mV1;
+ float mV2;
+ float mValue1;
+ float mValue2;
+
+ public DrawBase2(float v1, float v2) {
+ mValue1 = v1;
+ mValue2 = v2;
+ mV1 = v1;
+ mV2 = v2;
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ mV1 = (Float.isNaN(mValue1))
+ ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
+ mV2 = (Float.isNaN(mValue2))
+ ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ if (Float.isNaN(mValue1)) {
+ context.listensTo(Utils.idFromNan(mValue1), this);
+ }
+ if (Float.isNaN(mValue2)) {
+ context.listensTo(Utils.idFromNan(mValue2), this);
+ }
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mV1, mV2);
+ }
+
+ @Override
+ public String toString() {
+ return mName + " " + floatToString(mV1) + " " + floatToString(mV2);
+ }
+
+ public static class Companion implements CompanionOperation {
+ public final int OP_CODE;
+
+ protected Companion(int code) {
+ OP_CODE = code;
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ float v1 = buffer.readFloat();
+ float v2 = buffer.readFloat();
+
+ Operation op = construct(v1, v2);
+ operations.add(op);
+ }
+
+ /**
+ * Override to construct a 2 float value operation
+ * @param x1
+ * @param y1
+ * @return
+ */
+ public Operation construct(float x1, float y1) {
+ return null;
+ }
+
+ @Override
+ public String name() {
+ return "DrawRect";
+ }
+
+ @Override
+ public int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ * @param buffer
+ * @param x1
+ * @param y1
+ */
+ public void apply(WireBuffer buffer,
+ float x1,
+ float y1) {
+ buffer.start(OP_CODE);
+ buffer.writeFloat(x1);
+ buffer.writeFloat(y1);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
new file mode 100644
index 000000000000..56b2f1f7bb86
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Base class for commands that take 3 float
+ */
+public abstract class DrawBase3 extends PaintOperation
+ implements VariableSupport {
+ public static final Companion COMPANION =
+ new Companion(Operations.DRAW_CIRCLE) {
+ @Override
+ public Operation construct(float x1, float y1, float x2) {
+ // subclass should return new DrawX(x1, y1, x2, y2);
+ return null;
+ }
+ };
+ protected String mName = "DrawRectBase";
+ float mV1;
+ float mV2;
+ float mV3;
+ float mValue1;
+ float mValue2;
+ float mValue3;
+
+ public DrawBase3(
+ float v1,
+ float v2,
+ float v3) {
+ mValue1 = v1;
+ mValue2 = v2;
+ mValue3 = v3;
+
+ mV1 = v1;
+ mV2 = v2;
+ mV3 = v3;
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ mV1 = (Float.isNaN(mValue1))
+ ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
+ mV2 = (Float.isNaN(mValue2))
+ ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
+ mV3 = (Float.isNaN(mValue3))
+ ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3;
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ if (Float.isNaN(mValue1)) {
+ context.listensTo(Utils.idFromNan(mValue1), this);
+ }
+ if (Float.isNaN(mValue2)) {
+ context.listensTo(Utils.idFromNan(mValue2), this);
+ }
+ if (Float.isNaN(mValue3)) {
+ context.listensTo(Utils.idFromNan(mValue3), this);
+ }
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mV1, mV2, mV3);
+ }
+
+ @Override
+ public String toString() {
+ return mName + " " + floatToString(mV1) + " " + floatToString(mV2)
+ + " " + floatToString(mV3);
+ }
+
+ public static class Companion implements CompanionOperation {
+ public final int OP_CODE;
+
+ protected Companion(int code) {
+ OP_CODE = code;
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ float v1 = buffer.readFloat();
+ float v2 = buffer.readFloat();
+ float v3 = buffer.readFloat();
+
+ Operation op = construct(v1, v2, v3);
+ operations.add(op);
+ }
+
+ /**
+ * Construct and Operation from the 3 variables.
+ * This must be overridden by subclasses
+ * @param x1
+ * @param y1
+ * @param x2
+ * @return
+ */
+ public Operation construct(float x1,
+ float y1,
+ float x2) {
+ return null;
+ }
+
+ @Override
+ public String name() {
+ return "DrawRect";
+ }
+
+ @Override
+ public int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ * @param buffer
+ * @param x1
+ * @param y1
+ * @param x2
+ */
+ public void apply(WireBuffer buffer,
+ float x1,
+ float y1,
+ float x2) {
+ buffer.start(OP_CODE);
+ buffer.writeFloat(x1);
+ buffer.writeFloat(y1);
+ buffer.writeFloat(x2);
+
+ }
+ }
+}
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
new file mode 100644
index 000000000000..ec35a160079c
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Base class for draw commands that take 4 floats
+ */
+public abstract class DrawBase4 extends PaintOperation
+ implements VariableSupport {
+ public static final Companion COMPANION =
+ new Companion(Operations.DRAW_RECT) {
+ @Override
+ public Operation construct(float x1, float y1, float x2, float y2) {
+ // return new DrawRectBase(x1, y1, x2, y2);
+ return null;
+ }
+ };
+ protected String mName = "DrawRectBase";
+ float mX1;
+ float mY1;
+ float mX2;
+ float mY2;
+ float mX1Value;
+ float mY1Value;
+ float mX2Value;
+ float mY2Value;
+
+ public DrawBase4(
+ float x1,
+ float y1,
+ float x2,
+ float y2) {
+ mX1Value = x1;
+ mY1Value = y1;
+ mX2Value = x2;
+ mY2Value = y2;
+
+ mX1 = x1;
+ mY1 = y1;
+ mX2 = x2;
+ mY2 = y2;
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ mX1 = (Float.isNaN(mX1Value))
+ ? context.getFloat(Utils.idFromNan(mX1Value)) : mX1Value;
+ mY1 = (Float.isNaN(mY1Value))
+ ? context.getFloat(Utils.idFromNan(mY1Value)) : mY1Value;
+ mX2 = (Float.isNaN(mX2Value))
+ ? context.getFloat(Utils.idFromNan(mX2Value)) : mX2Value;
+ mY2 = (Float.isNaN(mY2Value))
+ ? context.getFloat(Utils.idFromNan(mY2Value)) : mY2Value;
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ if (Float.isNaN(mX1Value)) {
+ context.listensTo(Utils.idFromNan(mX1Value), this);
+ }
+ if (Float.isNaN(mY1Value)) {
+ context.listensTo(Utils.idFromNan(mY1Value), this);
+ }
+ if (Float.isNaN(mX2Value)) {
+ context.listensTo(Utils.idFromNan(mX2Value), this);
+ }
+ if (Float.isNaN(mY2Value)) {
+ context.listensTo(Utils.idFromNan(mY2Value), this);
+ }
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mX1, mY1, mX2, mY2);
+ }
+
+ @Override
+ public String toString() {
+ return mName + " " + floatToString(mX1Value, mX1) + " " + floatToString(mY1Value, mY1)
+ + " " + floatToString(mX2Value, mX2) + " " + floatToString(mY2Value, mY2);
+ }
+
+ public static class Companion implements CompanionOperation {
+ public final int OP_CODE;
+
+ protected Companion(int code) {
+ OP_CODE = code;
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ float sLeft = buffer.readFloat();
+ float srcTop = buffer.readFloat();
+ float srcRight = buffer.readFloat();
+ float srcBottom = buffer.readFloat();
+
+ Operation op = construct(sLeft, srcTop, srcRight, srcBottom);
+ operations.add(op);
+ }
+
+ /**
+ * Construct and Operation from the 3 variables.
+ * @param x1
+ * @param y1
+ * @param x2
+ * @param y2
+ * @return
+ */
+ public Operation construct(float x1,
+ float y1,
+ float x2,
+ float y2) {
+ return null;
+ }
+
+ @Override
+ public String name() {
+ return "DrawRect";
+ }
+
+ @Override
+ public int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ * @param buffer
+ * @param x1
+ * @param y1
+ * @param x2
+ * @param y2
+ */
+ public void apply(WireBuffer buffer,
+ float x1,
+ float y1,
+ float x2,
+ float y2) {
+ buffer.start(OP_CODE);
+ buffer.writeFloat(x1);
+ buffer.writeFloat(y1);
+ buffer.writeFloat(x2);
+ buffer.writeFloat(y2);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java
new file mode 100644
index 000000000000..2f4335e7f412
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Base class for draw commands the take 6 floats
+ */
+public abstract class DrawBase6 extends PaintOperation
+ implements VariableSupport {
+ public static final Companion COMPANION =
+ new Companion(Operations.DRAW_RECT) {
+ public Operation construct(float x1, float y1, float x2, float y2) {
+ // return new DrawRectBase(x1, y1, x2, y2);
+ return null;
+ }
+ };
+ protected String mName = "DrawRectBase";
+ float mV1;
+ float mV2;
+ float mV3;
+ float mV4;
+ float mV5;
+ float mV6;
+ float mValue1;
+ float mValue2;
+ float mValue3;
+ float mValue4;
+ float mValue5;
+ float mValue6;
+
+ public DrawBase6(
+ float v1,
+ float v2,
+ float v3,
+ float v4,
+ float v5,
+ float v6) {
+ mValue1 = v1;
+ mValue2 = v2;
+ mValue3 = v3;
+ mValue4 = v4;
+ mValue5 = v5;
+ mValue6 = v6;
+
+ mV1 = v1;
+ mV2 = v2;
+ mV3 = v3;
+ mV4 = v4;
+ mV5 = v5;
+ mV6 = v6;
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ mV1 = (Float.isNaN(mValue1))
+ ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
+ mV2 = (Float.isNaN(mValue2))
+ ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
+ mV3 = (Float.isNaN(mValue3))
+ ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3;
+ mV4 = (Float.isNaN(mValue4))
+ ? context.getFloat(Utils.idFromNan(mValue4)) : mValue4;
+ mV5 = (Float.isNaN(mValue5))
+ ? context.getFloat(Utils.idFromNan(mValue5)) : mValue5;
+ mV6 = (Float.isNaN(mValue6))
+ ? context.getFloat(Utils.idFromNan(mValue6)) : mValue6;
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ if (Float.isNaN(mValue1)) {
+ context.listensTo(Utils.idFromNan(mValue1), this);
+ }
+ if (Float.isNaN(mValue2)) {
+ context.listensTo(Utils.idFromNan(mValue2), this);
+ }
+ if (Float.isNaN(mValue3)) {
+ context.listensTo(Utils.idFromNan(mValue3), this);
+ }
+ if (Float.isNaN(mValue4)) {
+ context.listensTo(Utils.idFromNan(mValue4), this);
+ }
+ if (Float.isNaN(mValue5)) {
+ context.listensTo(Utils.idFromNan(mValue5), this);
+ }
+ if (Float.isNaN(mValue6)) {
+ context.listensTo(Utils.idFromNan(mValue6), this);
+ }
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mV1, mV2, mV3, mV4, mV5, mV6);
+ }
+
+ @Override
+ public String toString() {
+ return mName + " " + floatToString(mV1) + " " + floatToString(mV2)
+ + " " + floatToString(mV3) + " " + floatToString(mV4);
+ }
+
+ public static class Companion implements CompanionOperation {
+ public final int OP_CODE;
+
+ protected Companion(int code) {
+ OP_CODE = code;
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ float sv1 = buffer.readFloat();
+ float sv2 = buffer.readFloat();
+ float sv3 = buffer.readFloat();
+ float sv4 = buffer.readFloat();
+ float sv5 = buffer.readFloat();
+ float sv6 = buffer.readFloat();
+
+ Operation op = construct(sv1, sv2, sv3, sv4, sv5, sv6);
+ operations.add(op);
+ }
+
+ /**
+ * writes out a the operation to the buffer.
+ * @param v1
+ * @param v2
+ * @param v3
+ * @param v4
+ * @param v5
+ * @param v6
+ * @return
+ */
+ public Operation construct(float v1,
+ float v2,
+ float v3,
+ float v4,
+ float v5,
+ float v6) {
+ return null;
+ }
+
+ @Override
+ public String name() {
+ return "DrawRect";
+ }
+
+ @Override
+ public int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ * @param buffer
+ * @param v1
+ * @param v2
+ * @param v3
+ * @param v4
+ * @param v5
+ * @param v6
+ */
+ public void apply(WireBuffer buffer,
+ float v1,
+ float v2,
+ float v3,
+ float v4,
+ float v5,
+ float v6) {
+ buffer.start(OP_CODE);
+ buffer.writeFloat(v1);
+ buffer.writeFloat(v2);
+ buffer.writeFloat(v3);
+ buffer.writeFloat(v4);
+ buffer.writeFloat(v5);
+ buffer.writeFloat(v6);
+ }
+ }
+}
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 2e971f533ed2..ca40d12fd3f9 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
@@ -20,16 +20,22 @@ import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import java.util.List;
-public class DrawBitmap extends PaintOperation {
+public class DrawBitmap extends PaintOperation implements VariableSupport {
public static final Companion COMPANION = new Companion();
float mLeft;
float mTop;
float mRight;
float mBottom;
+ float mOutputLeft;
+ float mOutputTop;
+ float mOutputRight;
+ float mOutputBottom;
int mId;
int mDescriptionId = 0;
@@ -49,6 +55,34 @@ public class DrawBitmap extends PaintOperation {
}
@Override
+ public void updateVariables(RemoteContext context) {
+ mOutputLeft = (Float.isNaN(mLeft))
+ ? context.getFloat(Utils.idFromNan(mLeft)) : mLeft;
+ mOutputTop = (Float.isNaN(mTop))
+ ? context.getFloat(Utils.idFromNan(mTop)) : mTop;
+ mOutputRight = (Float.isNaN(mRight))
+ ? context.getFloat(Utils.idFromNan(mRight)) : mRight;
+ mOutputBottom = (Float.isNaN(mBottom))
+ ? context.getFloat(Utils.idFromNan(mBottom)) : mBottom;
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ if (Float.isNaN(mLeft)) {
+ context.listensTo(Utils.idFromNan(mLeft), this);
+ }
+ if (Float.isNaN(mTop)) {
+ context.listensTo(Utils.idFromNan(mTop), this);
+ }
+ if (Float.isNaN(mRight)) {
+ context.listensTo(Utils.idFromNan(mRight), this);
+ }
+ if (Float.isNaN(mBottom)) {
+ context.listensTo(Utils.idFromNan(mBottom), this);
+ }
+ }
+
+ @Override
public void write(WireBuffer buffer) {
COMPANION.apply(buffer, mId, mLeft, mTop, mRight, mBottom, mDescriptionId);
}
@@ -105,9 +139,9 @@ public class DrawBitmap extends PaintOperation {
@Override
public void paint(PaintContext context) {
- context.drawBitmap(mId, mLeft,
- mTop,
- mRight,
- mBottom);
+ context.drawBitmap(mId, mOutputLeft,
+ mOutputTop,
+ mOutputRight,
+ mOutputBottom);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java
index 9ce754da1b1b..3a22e4f72720 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java
@@ -1,89 +1,31 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
package com.android.internal.widget.remotecompose.core.operations;
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
-import java.util.List;
-
-public class DrawCircle extends PaintOperation {
- public static final Companion COMPANION = new Companion();
- float mCenterX;
- float mCenterY;
- float mRadius;
-
- public DrawCircle(float centerX, float centerY, float radius) {
- mCenterX = centerX;
- mCenterY = centerY;
- mRadius = radius;
- }
-
- @Override
- public void write(WireBuffer buffer) {
- COMPANION.apply(buffer, mCenterX,
- mCenterY,
- mRadius);
- }
-
- @Override
- public String toString() {
- return "";
- }
-
- public static class Companion implements CompanionOperation {
- private Companion() {
- }
-
- @Override
- public void read(WireBuffer buffer, List<Operation> operations) {
- float centerX = buffer.readFloat();
- float centerY = buffer.readFloat();
- float radius = buffer.readFloat();
-
- DrawCircle op = new DrawCircle(centerX, centerY, radius);
- operations.add(op);
- }
-
- @Override
- public String name() {
- return "";
- }
-
- @Override
- public int id() {
- return 0;
- }
-
- public void apply(WireBuffer buffer, float centerX, float centerY, float radius) {
- buffer.start(Operations.DRAW_CIRCLE);
- buffer.writeFloat(centerX);
- buffer.writeFloat(centerY);
- buffer.writeFloat(radius);
- }
+public class DrawCircle extends DrawBase3 {
+ public static final Companion COMPANION =
+ new Companion(Operations.DRAW_CIRCLE) {
+ @Override
+ public Operation construct(float x1,
+ float y1,
+ float x2
+ ) {
+ return new DrawCircle(x1, y1, x2);
+ }
+ };
+
+ public DrawCircle(
+ float left,
+ float top,
+ float right) {
+ super(left, top, right);
+ mName = "DrawCircle";
}
@Override
public void paint(PaintContext context) {
- context.drawCircle(mCenterX,
- mCenterY,
- mRadius);
+ context.drawCircle(mV1, mV2, mV3);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java
index c7a8315a2274..c70c6eaa449d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java
@@ -15,83 +15,28 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
-
-import java.util.List;
-
-public class DrawLine extends PaintOperation {
- public static final Companion COMPANION = new Companion();
- float mX1;
- float mY1;
- float mX2;
- float mY2;
-
- public DrawLine(
- float x1,
- float y1,
- float x2,
- float y2) {
- mX1 = x1;
- mY1 = y1;
- mX2 = x2;
- mY2 = y2;
- }
-
- @Override
- public void write(WireBuffer buffer) {
- COMPANION.apply(buffer, mX1,
- mY1,
- mX2,
- mY2);
- }
-
- @Override
- public String toString() {
- return "DrawArc " + mX1 + " " + mY1
- + " " + mX2 + " " + mY2 + ";";
- }
-
- public static class Companion implements CompanionOperation {
- private Companion() {
- }
-
- @Override
- public void read(WireBuffer buffer, List<Operation> operations) {
- float x1 = buffer.readFloat();
- float y1 = buffer.readFloat();
- float x2 = buffer.readFloat();
- float y2 = buffer.readFloat();
-
- DrawLine op = new DrawLine(x1, y1, x2, y2);
- operations.add(op);
- }
-
- @Override
- public String name() {
- return "DrawLine";
- }
+public class DrawLine extends DrawBase4 {
+ public static final Companion COMPANION = new Companion(Operations.DRAW_LINE) {
@Override
- public int id() {
- return Operations.DRAW_LINE;
+ public Operation construct(float x1,
+ float y1,
+ float x2,
+ float y2) {
+ return new DrawLine(x1, y1, x2, y2);
}
+ };
- public void apply(WireBuffer buffer,
- float x1,
- float y1,
- float x2,
- float y2) {
- buffer.start(Operations.DRAW_LINE);
- buffer.writeFloat(x1);
- buffer.writeFloat(y1);
- buffer.writeFloat(x2);
- buffer.writeFloat(y2);
- }
+ public DrawLine(
+ float left,
+ float top,
+ float right,
+ float bottom) {
+ super(left, top, right, bottom);
+ mName = "DrawLine";
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java
index 714375335cb2..ba1799422e80 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java
@@ -15,88 +15,33 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
-
-import java.util.List;
-
-public class DrawOval extends PaintOperation {
- public static final Companion COMPANION = new Companion();
- float mLeft;
- float mTop;
- float mRight;
- float mBottom;
+public class DrawOval extends DrawBase4 {
+ public static final Companion COMPANION =
+ new Companion(Operations.DRAW_OVAL) {
+ @Override
+ public Operation construct(float x1,
+ float y1,
+ float x2,
+ float y2) {
+ return new DrawOval(x1, y1, x2, y2);
+ }
+ };
public DrawOval(
float left,
float top,
float right,
float bottom) {
- mLeft = left;
- mTop = top;
- mRight = right;
- mBottom = bottom;
- }
-
- @Override
- public void write(WireBuffer buffer) {
- COMPANION.apply(buffer, mLeft, mTop, mRight, mBottom);
- }
-
- @Override
- public String toString() {
- return "DrawOval " + mLeft + " " + mTop
- + " " + mRight + " " + mBottom + ";";
- }
-
- public static class Companion implements CompanionOperation {
- private Companion() {
- }
-
- @Override
- public void read(WireBuffer buffer, List<Operation> operations) {
- float sLeft = buffer.readFloat();
- float srcTop = buffer.readFloat();
- float srcRight = buffer.readFloat();
- float srcBottom = buffer.readFloat();
-
- DrawOval op = new DrawOval(sLeft, srcTop, srcRight, srcBottom);
- operations.add(op);
- }
-
- @Override
- public String name() {
- return "DrawOval";
- }
-
- @Override
- public int id() {
- return Operations.DRAW_OVAL;
- }
-
- public void apply(WireBuffer buffer,
- float left,
- float top,
- float right,
- float bottom) {
- buffer.start(Operations.DRAW_OVAL);
- buffer.writeFloat(left);
- buffer.writeFloat(top);
- buffer.writeFloat(right);
- buffer.writeFloat(bottom);
- }
+ super(left, top, right, bottom);
+ mName = "DrawOval";
}
@Override
public void paint(PaintContext context) {
- context.drawOval(mLeft,
- mTop,
- mRight,
- mBottom);
+ context.drawOval(mX1, mY1, mX2, mY2);
}
}
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 7b8a9e95d9cb..6dbc5a628c98 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
@@ -41,7 +41,7 @@ public class DrawPath extends PaintOperation {
@Override
public String toString() {
- return "DrawPath " + ";";
+ return "DrawPath " + "[" + mId + "]" + ", " + mStart + ", " + mEnd;
}
public static class Companion implements CompanionOperation {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java
index 4775241faa6f..633aed4a4dbe 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java
@@ -15,88 +15,37 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
-import java.util.List;
-
-public class DrawRect extends PaintOperation {
- public static final Companion COMPANION = new Companion();
- float mLeft;
- float mTop;
- float mRight;
- float mBottom;
+/**
+ * Draw a Rectangle
+ */
+public class DrawRect extends DrawBase4 {
+ public static final Companion COMPANION =
+ new Companion(Operations.DRAW_RECT) {
+ @Override
+ public Operation construct(float x1,
+ float y1,
+ float x2,
+ float y2) {
+ return new DrawRect(x1, y1, x2, y2);
+ }
+ };
public DrawRect(
float left,
float top,
float right,
float bottom) {
- mLeft = left;
- mTop = top;
- mRight = right;
- mBottom = bottom;
- }
-
- @Override
- public void write(WireBuffer buffer) {
- COMPANION.apply(buffer, mLeft, mTop, mRight, mBottom);
- }
-
- @Override
- public String toString() {
- return "DrawRect " + mLeft + " " + mTop
- + " " + mRight + " " + mBottom + ";";
- }
-
- public static class Companion implements CompanionOperation {
- private Companion() {
- }
-
- @Override
- public void read(WireBuffer buffer, List<Operation> operations) {
- float sLeft = buffer.readFloat();
- float srcTop = buffer.readFloat();
- float srcRight = buffer.readFloat();
- float srcBottom = buffer.readFloat();
-
- DrawRect op = new DrawRect(sLeft, srcTop, srcRight, srcBottom);
- operations.add(op);
- }
-
- @Override
- public String name() {
- return "DrawRect";
- }
-
- @Override
- public int id() {
- return Operations.DRAW_RECT;
- }
-
- public void apply(WireBuffer buffer,
- float left,
- float top,
- float right,
- float bottom) {
- buffer.start(Operations.DRAW_RECT);
- buffer.writeFloat(left);
- buffer.writeFloat(top);
- buffer.writeFloat(right);
- buffer.writeFloat(bottom);
- }
+ super(left, top, right, bottom);
+ mName = "DrawRect";
}
@Override
public void paint(PaintContext context) {
- context.drawRect(mLeft,
- mTop,
- mRight,
- mBottom);
+ context.drawRect(mX1, mY1, mX2, mY2);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
index 8da16e768b7f..b9d0a6728b95 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
@@ -15,104 +15,40 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
-import java.util.List;
-
-public class DrawRoundRect extends PaintOperation {
- public static final Companion COMPANION = new Companion();
- float mLeft;
- float mTop;
- float mRight;
- float mBottom;
- float mRadiusX;
- float mRadiusY;
-
- public DrawRoundRect(
- float left,
- float top,
- float right,
- float bottom,
- float radiusX,
- float radiusY) {
- mLeft = left;
- mTop = top;
- mRight = right;
- mBottom = bottom;
- mRadiusX = radiusX;
- mRadiusY = radiusY;
- }
-
- @Override
- public void write(WireBuffer buffer) {
- COMPANION.apply(buffer, mLeft, mTop, mRight, mBottom, mRadiusX, mRadiusY);
- }
-
- @Override
- public String toString() {
- return "DrawRoundRect " + mLeft + " " + mTop
- + " " + mRight + " " + mBottom
- + " (" + mRadiusX + " " + mRadiusY + ");";
- }
-
- public static class Companion implements CompanionOperation {
- private Companion() {
- }
-
- @Override
- public void read(WireBuffer buffer, List<Operation> operations) {
- float sLeft = buffer.readFloat();
- float srcTop = buffer.readFloat();
- float srcRight = buffer.readFloat();
- float srcBottom = buffer.readFloat();
- float srcRadiusX = buffer.readFloat();
- float srcRadiusY = buffer.readFloat();
-
- DrawRoundRect op = new DrawRoundRect(sLeft, srcTop, srcRight,
- srcBottom, srcRadiusX, srcRadiusY);
- operations.add(op);
- }
-
- @Override
- public String name() {
- return "DrawOval";
- }
-
- @Override
- public int id() {
- return Operations.DRAW_ROUND_RECT;
- }
-
- public void apply(WireBuffer buffer,
- float left,
- float top,
- float right,
- float bottom,
- float radiusX,
- float radiusY) {
- buffer.start(Operations.DRAW_ROUND_RECT);
- buffer.writeFloat(left);
- buffer.writeFloat(top);
- buffer.writeFloat(right);
- buffer.writeFloat(bottom);
- buffer.writeFloat(radiusX);
- buffer.writeFloat(radiusY);
- }
+/**
+ * Draw a rounded rectangle
+ */
+public class DrawRoundRect extends DrawBase6 {
+ public static final Companion COMPANION =
+ new Companion(Operations.DRAW_ROUND_RECT) {
+ @Override
+ public Operation construct(float v1,
+ float v2,
+ float v3,
+ float v4,
+ float v5,
+ float v6) {
+ return new DrawRoundRect(v1, v2, v3, v4, v5, v6);
+ }
+ };
+
+ public DrawRoundRect(float v1,
+ float v2,
+ float v3,
+ float v4,
+ float v5,
+ float v6) {
+ super(v1, v2, v3, v4, v5, v6);
+ mName = "ClipRect";
}
@Override
public void paint(PaintContext context) {
- context.drawRoundRect(mLeft,
- mTop,
- mRight,
- mBottom,
- mRadiusX,
- mRadiusY
+ context.drawRoundRect(mV1, mV2, mV3, mV4, mV5, mV6
);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java
new file mode 100644
index 000000000000..f8f8afdf68cd
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Draw Text
+ */
+public class DrawText extends PaintOperation {
+ public static final Companion COMPANION = new Companion();
+ int mTextID;
+ int mStart = 0;
+ int mEnd = 0;
+ int mContextStart = 0;
+ int mContextEnd = 0;
+ float mX = 0f;
+ float mY = 0f;
+ boolean mRtl = false;
+
+ public DrawText(int textID,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean rtl) {
+ mTextID = textID;
+ mStart = start;
+ mEnd = end;
+ mContextStart = contextStart;
+ mContextEnd = contextEnd;
+ mX = x;
+ mY = y;
+ mRtl = rtl;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mTextID, mStart, mEnd, mContextStart, mContextEnd, mX, mY, mRtl);
+
+ }
+
+ @Override
+ public String toString() {
+ return "DrawTextRun [" + mTextID + "] " + mStart + ", " + mEnd + ", " + mX + ", " + mY;
+ }
+
+ public static class Companion implements CompanionOperation {
+ private Companion() {
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ int text = buffer.readInt();
+ int start = buffer.readInt();
+ int end = buffer.readInt();
+ int contextStart = buffer.readInt();
+ int contextEnd = buffer.readInt();
+ float x = buffer.readFloat();
+ float y = buffer.readFloat();
+ boolean rtl = buffer.readBoolean();
+ DrawText op = new DrawText(text, start, end, contextStart, contextEnd, x, y, rtl);
+
+ operations.add(op);
+ }
+
+ @Override
+ public String name() {
+ return "";
+ }
+
+ @Override
+ public int id() {
+ return 0;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ * @param buffer
+ * @param textID
+ * @param start
+ * @param end
+ * @param contextStart
+ * @param contextEnd
+ * @param x
+ * @param y
+ * @param rtl
+ */
+ public void apply(WireBuffer buffer,
+ int textID,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean rtl) {
+ buffer.start(Operations.DRAW_TEXT_RUN);
+ buffer.writeInt(textID);
+ buffer.writeInt(start);
+ buffer.writeInt(end);
+ buffer.writeInt(contextStart);
+ buffer.writeInt(contextEnd);
+ buffer.writeFloat(x);
+ buffer.writeFloat(y);
+ buffer.writeBoolean(rtl);
+ }
+ }
+
+ @Override
+ public void paint(PaintContext context) {
+ context.drawTextRun(mTextID, mStart, mEnd, mContextStart, mContextEnd, mX, mY, mRtl);
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java
new file mode 100644
index 000000000000..4f0641f34d84
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Draw Text in Anchored to a point
+ */
+public class DrawTextAnchored extends PaintOperation implements VariableSupport {
+ public static final Companion COMPANION = new Companion();
+ int mTextID;
+ float mX;
+ float mY;
+ float mPanX;
+ float mPanY;
+ int mFlags;
+ float mOutX;
+ float mOutY;
+ float mOutPanX;
+ float mOutPanY;
+
+ public static final int ANCHOR_TEXT_RTL = 1;
+ public static final int ANCHOR_MONOSPACE_MEASURE = 2;
+
+ public DrawTextAnchored(int textID,
+ float x,
+ float y,
+ float panX,
+ float panY,
+ int flags) {
+ mTextID = textID;
+ mX = x;
+ mY = y;
+ mOutX = mX;
+ mOutY = mY;
+ mFlags = flags;
+ mOutPanX = mPanX = panX;
+ mOutPanY = mPanY = panY;
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ mOutX = (Float.isNaN(mX))
+ ? context.getFloat(Utils.idFromNan(mX)) : mX;
+ mOutY = (Float.isNaN(mY))
+ ? context.getFloat(Utils.idFromNan(mY)) : mY;
+ mOutPanX = (Float.isNaN(mPanX))
+ ? context.getFloat(Utils.idFromNan(mPanX)) : mPanX;
+ mOutPanY = (Float.isNaN(mPanY))
+ ? context.getFloat(Utils.idFromNan(mPanY)) : mPanY;
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ if (Float.isNaN(mX)) {
+ context.listensTo(Utils.idFromNan(mX), this);
+ }
+ if (Float.isNaN(mY)) {
+ context.listensTo(Utils.idFromNan(mY), this);
+ }
+ if (Float.isNaN(mPanX)) {
+ context.listensTo(Utils.idFromNan(mPanX), this);
+ }
+ if (Float.isNaN(mPanY) && Utils.idFromNan(mPanY) > 0) {
+ context.listensTo(Utils.idFromNan(mPanY), this);
+ }
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mTextID, mX,
+ mY,
+ mPanX,
+ mPanY,
+ mFlags);
+ }
+
+ @Override
+ public String toString() {
+ return "DrawTextAnchored [" + mTextID + "] " + floatToStr(mX) + ", "
+ + floatToStr(mY) + ", "
+ + floatToStr(mPanX) + ", " + floatToStr(mPanY) + ", "
+ + Integer.toBinaryString(mFlags);
+ }
+
+ private static String floatToStr(float v) {
+ if (Float.isNaN(v)) {
+ return "[" + Utils.idFromNan(v) + "]";
+ }
+ return Float.toString(v);
+ }
+
+ public static class Companion implements CompanionOperation {
+ private Companion() {
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ int textID = buffer.readInt();
+ float x = buffer.readFloat();
+ float y = buffer.readFloat();
+ float panX = buffer.readFloat();
+ float panY = buffer.readFloat();
+ int flags = buffer.readInt();
+
+ DrawTextAnchored op = new DrawTextAnchored(textID,
+ x, y,
+ panX, panY,
+ flags);
+
+ operations.add(op);
+ }
+
+ @Override
+ public String name() {
+ return "";
+ }
+
+ @Override
+ public int id() {
+ return 0;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ * @param buffer
+ * @param textID
+ * @param x
+ * @param y
+ * @param panX
+ * @param panY
+ * @param flags
+ */
+ public void apply(WireBuffer buffer,
+ int textID,
+ float x,
+ float y,
+ float panX,
+ float panY,
+ int flags) {
+ buffer.start(Operations.DRAW_TEXT_ANCHOR);
+ buffer.writeInt(textID);
+ buffer.writeFloat(x);
+ buffer.writeFloat(y);
+ buffer.writeFloat(panX);
+ buffer.writeFloat(panY);
+ buffer.writeInt(flags);
+ }
+ }
+
+ float[] mBounds = new float[4];
+
+ private float getHorizontalOffset() {
+ // TODO scale TextSize / BaseTextSize;
+ float scale = 1.0f;
+
+ float textWidth = scale * (mBounds[2] - mBounds[0]);
+ float boxWidth = 0;
+ return (boxWidth - textWidth) * (1 + mOutPanX) / 2.f
+ - (scale * mBounds[0]);
+ }
+
+ private float getVerticalOffset() {
+ // TODO scale TextSize / BaseTextSize;
+ float scale = 1.0f;
+ float boxHeight = 0;
+ float textHeight = scale * (mBounds[3] - mBounds[1]);
+ return (boxHeight - textHeight) * (1 - mOutPanY) / 2
+ - (scale * mBounds[1]);
+ }
+
+ @Override
+ public void paint(PaintContext context) {
+ context.getTextBounds(mTextID, 0, -1,
+ (mFlags & ANCHOR_MONOSPACE_MEASURE) != 0, mBounds);
+ float x = mOutX + getHorizontalOffset();
+ float y = (Float.isNaN(mOutPanY)) ? mOutY : mOutY + getVerticalOffset();
+ context.drawTextRun(mTextID, 0, -1, 0, 1, x, y,
+ (mFlags & ANCHOR_TEXT_RTL) == 1);
+ }
+}
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 1856e3097ec0..b1a01724c315 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
@@ -24,6 +24,9 @@ import com.android.internal.widget.remotecompose.core.WireBuffer;
import java.util.List;
+/**
+ * Draw text along a path.
+ */
public class DrawTextOnPath extends PaintOperation {
public static final Companion COMPANION = new Companion();
int mPathId;
@@ -45,7 +48,8 @@ public class DrawTextOnPath extends PaintOperation {
@Override
public String toString() {
- return "DrawTextOnPath " + " " + mPathId + ";";
+ return "DrawTextOnPath [" + mTextId + "] [" + mPathId + "] "
+ + mHOffset + ", " + mVOffset;
}
public static class Companion implements CompanionOperation {
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 ef0a4ad2eff3..48fc94ee5f27 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
@@ -58,7 +58,7 @@ public class DrawTweenPath extends PaintOperation {
public String toString() {
return "DrawTweenPath " + mPath1Id + " " + mPath2Id
+ " " + mTween + " " + mStart + " "
- + "- " + mStop + ";";
+ + "- " + mStop;
}
public static class Companion implements CompanionOperation {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
new file mode 100644
index 000000000000..576b53f9fc6c
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Operation to deal with Text data
+ */
+public class FloatConstant implements Operation {
+ public int mTextId;
+ public float mValue;
+ public static final Companion COMPANION = new Companion();
+ public static final int MAX_STRING_SIZE = 4000;
+
+ public FloatConstant(int textId, float value) {
+ this.mTextId = textId;
+ this.mValue = value;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mTextId, mValue);
+ }
+
+ @Override
+ public String toString() {
+ return "FloatConstant[" + mTextId + "] = " + mValue + "";
+ }
+
+ public static class Companion implements CompanionOperation {
+ private Companion() {}
+
+ @Override
+ public String name() {
+ return "FloatExpression";
+ }
+
+ @Override
+ public int id() {
+ return Operations.DATA_FLOAT;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ * @param buffer
+ * @param textId
+ * @param value
+ */
+ public void apply(WireBuffer buffer, int textId, float value) {
+ buffer.start(Operations.DATA_FLOAT);
+ buffer.writeInt(textId);
+ buffer.writeFloat(value);
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ int textId = buffer.readInt();
+
+ float value = buffer.readFloat();
+ operations.add(new FloatConstant(textId, value));
+ }
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ context.loadFloat(mTextId, mValue);
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java
new file mode 100644
index 000000000000..354f41b813e0
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.operations.utilities.AnimatedFloatExpression;
+import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Operation to deal with AnimatedFloats
+ * This is designed to be an optimized calculation for things like
+ * injecting the width of the component int draw rect
+ * As well as supporting generalized animation floats.
+ * The floats represent a RPN style calculator
+ */
+public class FloatExpression implements Operation, VariableSupport {
+ public int mId;
+ public float[] mSrcValue;
+ public float[] mSrcAnimation;
+ public FloatAnimation mFloatAnimation;
+ public float[] mPreCalcValue;
+ private float mLastChange = Float.NaN;
+ AnimatedFloatExpression mExp = new AnimatedFloatExpression();
+ public static final Companion COMPANION = new Companion();
+ public static final int MAX_STRING_SIZE = 4000;
+
+ public FloatExpression(int id, float[] value, float[] animation) {
+ this.mId = id;
+ this.mSrcValue = value;
+ this.mSrcAnimation = animation;
+ if (mSrcAnimation != null) {
+ mFloatAnimation = new FloatAnimation(mSrcAnimation);
+ }
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ if (mPreCalcValue == null || mPreCalcValue.length != mSrcValue.length) {
+ mPreCalcValue = new float[mSrcValue.length];
+ }
+ //Utils.log("updateVariables ");
+ boolean value_changed = false;
+ for (int i = 0; i < mSrcValue.length; i++) {
+ float v = mSrcValue[i];
+ if (Float.isNaN(v) && !AnimatedFloatExpression.isMathOperator(v)) {
+ float newValue = context.getFloat(Utils.idFromNan(v));
+ if (mFloatAnimation != null) {
+ if (mPreCalcValue[i] != newValue) {
+ mLastChange = context.getAnimationTime();
+ value_changed = true;
+ mPreCalcValue[i] = newValue;
+ }
+ } else {
+ mPreCalcValue[i] = newValue;
+ }
+ } else {
+ mPreCalcValue[i] = mSrcValue[i];
+ }
+ }
+ if (value_changed && mFloatAnimation != null) {
+ float v = mExp.eval(Arrays.copyOf(mPreCalcValue, mPreCalcValue.length));
+ if (Float.isNaN(mFloatAnimation.getTargetValue())) {
+ mFloatAnimation.setInitialValue(v);
+ } else {
+ mFloatAnimation.setInitialValue(mFloatAnimation.getTargetValue());
+ }
+ mFloatAnimation.setTargetValue(v);
+ }
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ for (int i = 0; i < mSrcValue.length; i++) {
+ float v = mSrcValue[i];
+ if (Float.isNaN(v) && !AnimatedFloatExpression.isMathOperator(v)) {
+ context.listensTo(Utils.idFromNan(v), this);
+ }
+ }
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ updateVariables(context);
+ float t = context.getAnimationTime();
+ if (Float.isNaN(mLastChange)) {
+ mLastChange = t;
+ }
+ if (mFloatAnimation != null) {
+ float f = mFloatAnimation.get(t - mLastChange);
+ context.loadFloat(mId, f);
+ } else {
+ context.loadFloat(mId, mExp.eval(Arrays.copyOf(mPreCalcValue, mPreCalcValue.length)));
+ }
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mId, mSrcValue, mSrcAnimation);
+ }
+
+ @Override
+ public String toString() {
+ String[] labels = new String[mSrcValue.length];
+ for (int i = 0; i < mSrcValue.length; i++) {
+ if (Float.isNaN(mSrcValue[i])) {
+ labels[i] = "[" + Utils.idFromNan(mSrcValue[i]) + "]";
+ }
+
+ }
+ return "FloatExpression[" + mId + "] = ("
+ + AnimatedFloatExpression.toString(mPreCalcValue, labels) + ")";
+ }
+
+ public static class Companion implements CompanionOperation {
+ private Companion() {
+ }
+
+ @Override
+ public String name() {
+ return "FloatExpression";
+ }
+
+ @Override
+ public int id() {
+ return Operations.ANIMATED_FLOAT;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ * @param buffer
+ * @param id
+ * @param value
+ * @param animation
+ */
+ public void apply(WireBuffer buffer, int id, float[] value, float[] animation) {
+ buffer.start(Operations.ANIMATED_FLOAT);
+ buffer.writeInt(id);
+
+ int len = value.length;
+ if (animation != null) {
+ len |= (animation.length << 16);
+ }
+ buffer.writeInt(len);
+
+ for (int i = 0; i < value.length; i++) {
+ buffer.writeFloat(value[i]);
+ }
+ if (animation != null) {
+ for (int i = 0; i < animation.length; i++) {
+ buffer.writeFloat(animation[i]);
+ }
+ }
+
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ int id = buffer.readInt();
+ int len = buffer.readInt();
+ int valueLen = len & 0xFFFF;
+ int animLen = (len >> 16) & 0xFFFF;
+ float[] values = new float[valueLen];
+ for (int i = 0; i < values.length; i++) {
+ values[i] = buffer.readFloat();
+ }
+
+ float[] animation;
+ if (animLen != 0) {
+ animation = new float[animLen];
+ for (int i = 0; i < animation.length; i++) {
+ animation[i] = buffer.readFloat();
+ }
+ } else {
+ animation = null;
+ }
+ operations.add(new FloatExpression(id, values, animation));
+ }
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+
+}
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 482e0e22bd57..0dad45ce356b 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
@@ -37,7 +37,7 @@ public class MatrixRestore extends PaintOperation {
@Override
public String toString() {
- return "MatrixRestore;";
+ return "MatrixRestore";
}
public static class Companion implements CompanionOperation {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java
index d6c89e0d2c64..bbf41351a1c5 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java
@@ -15,68 +15,29 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
-import java.util.List;
-
-public class MatrixRotate extends PaintOperation {
- public static final Companion COMPANION = new Companion();
- float mRotate, mPivotX, mPivotY;
+public class MatrixRotate extends DrawBase3 {
+ public static final Companion COMPANION =
+ new Companion(Operations.MATRIX_ROTATE) {
+ @Override
+ public Operation construct(float rotate,
+ float pivotX,
+ float pivotY
+ ) {
+ return new MatrixRotate(rotate, pivotX, pivotY);
+ }
+ };
public MatrixRotate(float rotate, float pivotX, float pivotY) {
- mRotate = rotate;
- mPivotX = pivotX;
- mPivotY = pivotY;
- }
-
- @Override
- public void write(WireBuffer buffer) {
- COMPANION.apply(buffer, mRotate, mPivotX, mPivotY);
- }
-
- @Override
- public String toString() {
- return "DrawArc " + mRotate + ", " + mPivotX + ", " + mPivotY + ";";
- }
-
- public static class Companion implements CompanionOperation {
- private Companion() {
- }
-
- @Override
- public void read(WireBuffer buffer, List<Operation> operations) {
- float rotate = buffer.readFloat();
- float pivotX = buffer.readFloat();
- float pivotY = buffer.readFloat();
- MatrixRotate op = new MatrixRotate(rotate, pivotX, pivotY);
- operations.add(op);
- }
-
- @Override
- public String name() {
- return "Matrix";
- }
-
- @Override
- public int id() {
- return Operations.MATRIX_ROTATE;
- }
-
- public void apply(WireBuffer buffer, float rotate, float pivotX, float pivotY) {
- buffer.start(Operations.MATRIX_ROTATE);
- buffer.writeFloat(rotate);
- buffer.writeFloat(pivotX);
- buffer.writeFloat(pivotY);
- }
+ super(rotate, pivotX, pivotY);
+ mName = "MatrixRotate";
}
@Override
public void paint(PaintContext context) {
- context.matrixRotate(mRotate, mPivotX, mPivotY);
+ context.matrixRotate(mV1, mV2, mV3);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java
index 28aa68dd5884..04b940ba16c8 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java
@@ -15,74 +15,30 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
-import java.util.List;
-
-public class MatrixScale extends PaintOperation {
- public static final Companion COMPANION = new Companion();
- float mScaleX, mScaleY;
- float mCenterX, mCenterY;
+public class MatrixScale extends DrawBase4 {
+ public static final Companion COMPANION =
+ new Companion(Operations.MATRIX_SCALE) {
+ @Override
+ public Operation construct(float scaleX,
+ float scaleY,
+ float centerX,
+ float centerY
+ ) {
+ return new MatrixScale(scaleX, scaleY, centerX, centerY);
+ }
+ };
public MatrixScale(float scaleX, float scaleY, float centerX, float centerY) {
- mScaleX = scaleX;
- mScaleY = scaleY;
- mCenterX = centerX;
- mCenterY = centerY;
- }
-
- @Override
- public void write(WireBuffer buffer) {
- COMPANION.apply(buffer, mScaleX, mScaleY, mCenterX, mCenterY);
- }
-
- @Override
- public String toString() {
- return "MatrixScale " + mScaleY + ", " + mScaleY + ";";
- }
-
- public static class Companion implements CompanionOperation {
- private Companion() {
- }
-
- @Override
- public void read(WireBuffer buffer, List<Operation> operations) {
- float scaleX = buffer.readFloat();
- float scaleY = buffer.readFloat();
- float centerX = buffer.readFloat();
- float centerY = buffer.readFloat();
- MatrixScale op = new MatrixScale(scaleX, scaleY, centerX, centerY);
- operations.add(op);
- }
-
- @Override
- public String name() {
- return "Matrix";
- }
-
- @Override
- public int id() {
- return Operations.MATRIX_SCALE;
- }
-
- public void apply(WireBuffer buffer, float scaleX, float scaleY,
- float centerX, float centerY) {
- buffer.start(Operations.MATRIX_SCALE);
- buffer.writeFloat(scaleX);
- buffer.writeFloat(scaleY);
- buffer.writeFloat(centerX);
- buffer.writeFloat(centerY);
-
- }
+ super(scaleX, scaleY, centerX, centerY);
+ mName = "MatrixScale";
}
@Override
public void paint(PaintContext context) {
- context.mtrixScale(mScaleX, mScaleY, mCenterX, mCenterY);
+ context.matrixScale(mX1, mY1, mX2, mY2);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java
index 32987521e041..4f34e987e064 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java
@@ -15,65 +15,28 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
-import java.util.List;
-
-public class MatrixTranslate extends PaintOperation {
- public static final Companion COMPANION = new Companion();
- float mTranslateX, mTranslateY;
+public class MatrixTranslate extends DrawBase2 {
+ public static final Companion COMPANION =
+ new Companion(Operations.MATRIX_TRANSLATE) {
+ @Override
+ public Operation construct(float x1,
+ float y1
+ ) {
+ return new MatrixTranslate(x1, y1);
+ }
+ };
public MatrixTranslate(float translateX, float translateY) {
- mTranslateX = translateX;
- mTranslateY = translateY;
- }
-
- @Override
- public void write(WireBuffer buffer) {
- COMPANION.apply(buffer, mTranslateX, mTranslateY);
- }
-
- @Override
- public String toString() {
- return "DrawArc " + mTranslateY + ", " + mTranslateY + ";";
- }
-
- public static class Companion implements CompanionOperation {
- private Companion() {
- }
-
- @Override
- public void read(WireBuffer buffer, List<Operation> operations) {
- float translateX = buffer.readFloat();
- float translateY = buffer.readFloat();
- MatrixTranslate op = new MatrixTranslate(translateX, translateY);
- operations.add(op);
- }
-
- @Override
- public String name() {
- return "Matrix";
- }
-
- @Override
- public int id() {
- return Operations.MATRIX_TRANSLATE;
- }
-
- public void apply(WireBuffer buffer, float translateX, float translateY) {
- buffer.start(Operations.MATRIX_TRANSLATE);
- buffer.writeFloat(translateX);
- buffer.writeFloat(translateY);
- }
+ super(translateX, translateY);
+ mName = "MatrixTranslate";
}
@Override
public void paint(PaintContext context) {
- context.matrixTranslate(mTranslateX, mTranslateY);
+ context.matrixTranslate(mV1, mV2);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java
new file mode 100644
index 000000000000..0c5b286684d4
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Operation to deal with Text data
+ */
+public class NamedVariable implements Operation {
+ public int mVarId;
+ public String mVarName;
+ public int mVarType;
+ public static final Companion COMPANION = new Companion();
+ public static final int MAX_STRING_SIZE = 4000;
+
+ public NamedVariable(int varId, int varType, String name) {
+ this.mVarId = varId;
+ this.mVarType = varType;
+ this.mVarName = name;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mVarId, mVarType, mVarName);
+ }
+
+ @Override
+ public String toString() {
+ return "VariableName[" + mVarId + "] = \""
+ + Utils.trimString(mVarName, 10) + "\" type=" + mVarType;
+ }
+
+ public static class Companion implements CompanionOperation {
+ private Companion() {
+ }
+
+ @Override
+ public String name() {
+ return "TextData";
+ }
+
+ @Override
+ public int id() {
+ return Operations.DATA_TEXT;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ * @param buffer
+ * @param varId
+ * @param varType
+ * @param text
+ */
+ public void apply(WireBuffer buffer, int varId, int varType, String text) {
+ buffer.start(Operations.DATA_TEXT);
+ buffer.writeInt(varId);
+ buffer.writeInt(varType);
+ buffer.writeUTF8(text);
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ int varId = buffer.readInt();
+ int varType = buffer.readInt();
+ String text = buffer.readUTF8(MAX_STRING_SIZE);
+ operations.add(new NamedVariable(varId, varType, text));
+ }
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ context.loadVariableName(mVarName, mVarId, mVarType);
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+}
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 e5683ece7919..0807bcdcfebb 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
@@ -20,12 +20,14 @@ import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
import java.util.List;
-public class PaintData extends PaintOperation {
+public class PaintData extends PaintOperation implements VariableSupport {
public PaintBundle mPaintData = new PaintBundle();
public static final Companion COMPANION = new Companion();
public static final int MAX_STRING_SIZE = 4000;
@@ -34,6 +36,16 @@ public class PaintData extends PaintOperation {
}
@Override
+ public void updateVariables(RemoteContext context) {
+ mPaintData.updateVariables(context);
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ mPaintData.registerVars(context, this);
+ }
+
+ @Override
public void write(WireBuffer buffer) {
COMPANION.apply(buffer, mPaintData);
}
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 2646b27b1f51..e467e7b7f31e 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
@@ -18,27 +18,50 @@ package com.android.internal.widget.remotecompose.core.operations;
import com.android.internal.widget.remotecompose.core.CompanionOperation;
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.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
+import java.util.Arrays;
import java.util.List;
-public class PathData implements Operation {
+public class PathData implements Operation, VariableSupport {
public static final Companion COMPANION = new Companion();
int mInstanceId;
- float[] mRef;
float[] mFloatPath;
- float[] mRetFloats;
+ float[] mOutputPath;
PathData(int instanceId, float[] floatPath) {
mInstanceId = instanceId;
mFloatPath = floatPath;
+ mOutputPath = Arrays.copyOf(mFloatPath, mFloatPath.length);
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ for (int i = 0; i < mFloatPath.length; i++) {
+ float v = mFloatPath[i];
+ if (Utils.isVariable(v)) {
+ mOutputPath[i] = (Float.isNaN(v))
+ ? context.getFloat(Utils.idFromNan(v)) : v;
+ } else {
+ mOutputPath[i] = v;
+ }
+ }
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ for (int i = 0; i < mFloatPath.length; i++) {
+ if (Float.isNaN(mFloatPath[i])) {
+ context.listensTo(Utils.idFromNan(mFloatPath[i]), this);
+ }
+ }
}
@Override
public void write(WireBuffer buffer) {
- COMPANION.apply(buffer, mInstanceId, mFloatPath);
+ COMPANION.apply(buffer, mInstanceId, mOutputPath);
}
@Override
@@ -46,29 +69,35 @@ public class PathData implements Operation {
return pathString(mFloatPath);
}
- public float[] getFloatPath(PaintContext context) {
- float[] ret = mRetFloats; // Assume retFloats is declared elsewhere
- if (ret == null) {
- return mFloatPath; // Assume floatPath is declared elsewhere
- }
- float[] localRef = mRef; // Assume ref is of type Float[]
- if (localRef == null) {
- for (int i = 0; i < mFloatPath.length; i++) {
- ret[i] = mFloatPath[i];
- }
- } else {
- for (int i = 0; i < mFloatPath.length; i++) {
- float lr = localRef[i];
- if (Float.isNaN(lr)) {
- ret[i] = Utils.getActualValue(lr);
- } else {
- ret[i] = mFloatPath[i];
- }
- }
- }
- return ret;
+ @Override
+ public String toString() {
+ return "PathData[" + mInstanceId + "] = " + "\"" + deepToString(" ") + "\"";
}
+ /**
+ * public float[] getFloatPath(PaintContext context) {
+ * float[] ret = mRetFloats; // Assume retFloats is declared elsewhere
+ * if (ret == null) {
+ * return mFloatPath; // Assume floatPath is declared elsewhere
+ * }
+ * float[] localRef = mRef; // Assume ref is of type Float[]
+ * if (localRef == null) {
+ * for (int i = 0; i < mFloatPath.length; i++) {
+ * ret[i] = mFloatPath[i];
+ * }
+ * } else {
+ * for (int i = 0; i < mFloatPath.length; i++) {
+ * float lr = localRef[i];
+ * if (Float.isNaN(lr)) {
+ * ret[i] = Utils.getActualValue(lr);
+ * } else {
+ * ret[i] = mFloatPath[i];
+ * }
+ * }
+ * }
+ * return ret;
+ * }
+ */
public static final int MOVE = 10;
public static final int LINE = 11;
public static final int QUADRATIC = 12;
@@ -155,7 +184,7 @@ public class PathData implements Operation {
str.append(".");
break;
default:
- str.append("X");
+ str.append("[" + id + "]");
break;
}
} else {
@@ -170,7 +199,7 @@ public class PathData implements Operation {
@Override
public void apply(RemoteContext context) {
- context.loadPathData(mInstanceId, mFloatPath);
+ context.loadPathData(mInstanceId, mOutputPath);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
index 6d924eb70c50..997e8dc791ed 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
@@ -94,7 +94,6 @@ public class RootContentBehavior implements RemoteComposeOperation {
public static final int SCALE_CROP = 5;
public static final int SCALE_FILL_BOUNDS = 6;
-
public static final Companion COMPANION = new Companion();
/**
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java
index 64c7f3ef2d44..076b28edf981 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java
@@ -48,7 +48,7 @@ public class RootContentDescription implements RemoteComposeOperation {
@Override
public String toString() {
- return "ROOT_CONTENT_DESCRIPTION " + mContentDescription;
+ return "RootContentDescription " + mContentDescription;
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java
new file mode 100644
index 000000000000..8463ac576774
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Operation to deal with bitmap data
+ * On getting an Image during a draw call the bitmap is compressed and saved
+ * in playback the image is decompressed
+ */
+public class ShaderData implements Operation, VariableSupport {
+ int mShaderTextId; // the actual text of a shader
+ int mShaderID; // allows shaders to be referenced by number
+ HashMap<String, float[]> mUniformRawFloatMap = null;
+ HashMap<String, float[]> mUniformFloatMap = null;
+ HashMap<String, int[]> mUniformIntMap = null;
+ HashMap<String, Integer> mUniformBitmapMap = null;
+
+ public static final int MAX_IMAGE_DIMENSION = 8000;
+
+ public static final Companion COMPANION = new Companion();
+
+ public ShaderData(int shaderID,
+ int shaderTextId,
+ HashMap<String, float[]> floatMap,
+ HashMap<String, int[]> intMap,
+ HashMap<String, Integer> bitmapMap) {
+ mShaderID = shaderID;
+ mShaderTextId = shaderTextId;
+ if (floatMap != null) {
+ mUniformFloatMap = new HashMap<>();
+ mUniformRawFloatMap = new HashMap<>();
+
+ for (String name : floatMap.keySet()) {
+ mUniformRawFloatMap.put(name, floatMap.get(name));
+ mUniformFloatMap.put(name, floatMap.get(name));
+ }
+ }
+
+ if (intMap != null) {
+ mUniformIntMap = new HashMap<>();
+ for (String name : intMap.keySet()) {
+ mUniformIntMap.put(name, intMap.get(name));
+ }
+ }
+ if (bitmapMap != null) {
+ mUniformBitmapMap = new HashMap<>();
+ for (String name : bitmapMap.keySet()) {
+ mUniformBitmapMap.put(name, bitmapMap.get(name));
+ }
+ }
+
+ }
+
+ public int getShaderTextId() {
+ return mShaderTextId;
+ }
+
+ /**
+ * get names of all known floats
+ * @return
+ */
+ public String[] getUniformFloatNames() {
+ if (mUniformFloatMap == null) return new String[0];
+ return mUniformFloatMap.keySet().toArray(new String[0]);
+ }
+
+ /**
+ * Get float values associated with the name
+ * @param name
+ * @return
+ */
+ public float[] getUniformFloats(String name) {
+ return mUniformFloatMap.get(name);
+ }
+
+ /**
+ * get the name of all know uniform integers
+ * @return
+ */
+ public String[] getUniformIntegerNames() {
+ if (mUniformIntMap == null) return new String[0];
+ return mUniformIntMap.keySet().toArray(new String[0]);
+ }
+
+ /**
+ * Get Int value associated with the name
+ * @param name
+ * @return
+ */
+ public int[] getUniformInts(String name) {
+ return mUniformIntMap.get(name);
+ }
+
+ /**
+ * get list of uniform Bitmaps
+ * @return
+ */
+ public String[] getUniformBitmapNames() {
+ if (mUniformBitmapMap == null) return new String[0];
+ return mUniformBitmapMap.keySet().toArray(new String[0]);
+ }
+
+ /**
+ * Get a bitmap stored under that name
+ * @param name
+ * @return
+ */
+ public int getUniformBitmapId(String name) {
+ return mUniformBitmapMap.get(name);
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mShaderID, mShaderTextId,
+ mUniformFloatMap, mUniformIntMap, mUniformBitmapMap);
+ }
+
+ @Override
+ public String toString() {
+ return "SHADER DATA " + mShaderID;
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ for (String name : mUniformRawFloatMap.keySet()) {
+ float[] value = mUniformRawFloatMap.get(name);
+ float[] out = null;
+ for (int i = 0; i < value.length; i++) {
+ if (Float.isNaN(value[i])) {
+ if (out == null) { // need to copy
+ out = Arrays.copyOf(value, value.length);
+ }
+ out[i] = context.getFloat(Utils.idFromNan(value[i]));
+ }
+ }
+ mUniformFloatMap.put(name, out == null ? value : out);
+ }
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ for (String name : mUniformRawFloatMap.keySet()) {
+ float[] value = mUniformRawFloatMap.get(name);
+ for (int i = 0; i < value.length; i++) {
+ if (Float.isNaN(value[i])) {
+ context.listensTo(Utils.idFromNan(value[i]), this);
+ }
+ }
+ }
+ }
+
+ public static class Companion implements CompanionOperation {
+ private Companion() {
+ }
+
+ @Override
+ public String name() {
+ return "BitmapData";
+ }
+
+ @Override
+ public int id() {
+ return Operations.DATA_SHADER;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ * @param buffer
+ * @param shaderID
+ * @param shaderTextId
+ * @param floatMap
+ * @param intMap
+ * @param bitmapMap
+ */
+ public void apply(WireBuffer buffer, int shaderID, int shaderTextId,
+ HashMap<String, float[]> floatMap,
+ HashMap<String, int[]> intMap,
+ HashMap<String, Integer> bitmapMap) {
+ buffer.start(Operations.DATA_SHADER);
+ buffer.writeInt(shaderID);
+
+ buffer.writeInt(shaderTextId);
+ int floatSize = (floatMap == null) ? 0 : floatMap.size();
+ int intSize = (intMap == null) ? 0 : intMap.size();
+ int bitmapSize = (bitmapMap == null) ? 0 : bitmapMap.size();
+ int sizes = floatSize | (intSize << 8) | (bitmapSize << 16);
+ buffer.writeInt(sizes);
+
+ if (floatSize > 0) {
+
+ for (String name : floatMap.keySet()) {
+ buffer.writeUTF8(name);
+ float[] values = floatMap.get(name);
+ buffer.writeInt(values.length);
+
+ for (int i = 0; i < values.length; i++) {
+ buffer.writeFloat(values[i]);
+ }
+ }
+ }
+
+ if (intSize > 0) {
+ for (String name : intMap.keySet()) {
+ buffer.writeUTF8(name);
+ int[] values = intMap.get(name);
+ buffer.writeInt(values.length);
+ for (int i = 0; i < values.length; i++) {
+ buffer.writeInt(values[i]);
+ }
+ }
+ }
+ if (bitmapSize > 0) {
+ for (String name : bitmapMap.keySet()) {
+ buffer.writeUTF8(name);
+ int value = bitmapMap.get(name);
+ buffer.writeInt(value);
+ }
+ }
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ int shaderID = buffer.readInt();
+ int shaderTextId = buffer.readInt();
+ HashMap<String, float[]> floatMap = null;
+ HashMap<String, int[]> intMap = null;
+ HashMap<String, Integer> bitmapMap = null;
+
+ int sizes = buffer.readInt();
+
+ int floatMapSize = sizes & 0xFF;
+ if (floatMapSize > 0) {
+ floatMap = new HashMap<>();
+ for (int i = 0; i < floatMapSize; i++) {
+ String name = buffer.readUTF8();
+ int len = buffer.readInt();
+ float[] val = new float[len];
+
+ for (int j = 0; j < len; j++) {
+ val[j] = buffer.readFloat();
+ }
+
+ floatMap.put(name, val);
+ }
+ }
+ int intMapSize = (sizes >> 8) & 0xFF;
+
+ if (intMapSize > 0) {
+
+ intMap = new HashMap<>();
+ for (int i = 0; i < intMapSize; i++) {
+ String name = buffer.readUTF8();
+ int len = buffer.readInt();
+ int[] val = new int[len];
+ for (int j = 0; j < len; j++) {
+ val[j] = buffer.readInt();
+ }
+ intMap.put(name, val);
+ }
+ }
+ int bitmapMapSize = (sizes >> 16) & 0xFF;
+
+ if (bitmapMapSize > 0) {
+ bitmapMap = new HashMap<>();
+ for (int i = 0; i < bitmapMapSize; i++) {
+ String name = buffer.readUTF8();
+ int val = buffer.readInt();
+ bitmapMap.put(name, val);
+ }
+ }
+ operations.add(new ShaderData(shaderID, shaderTextId,
+ floatMap, intMap, bitmapMap));
+ }
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ context.loadShader(mShaderID, this);
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+}
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 5b622ae96d0b..ed1344975256 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
@@ -44,11 +44,13 @@ public class TextData implements Operation {
@Override
public String toString() {
- return "TEXT DATA " + mTextId + "\"" + mText + "\"";
+ return "TextData[" + mTextId + "] = \""
+ + Utils.trimString(mText, 10) + "\"";
}
public static class Companion implements CompanionOperation {
- private Companion() {}
+ private Companion() {
+ }
@Override
public String name() {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java
new file mode 100644
index 000000000000..65a39a1eba04
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringUtils;
+
+import java.util.List;
+
+/**
+ * Operation convert floats to text
+ * This command is structured [command][textID][before,after][flags]
+ * before and after define number of digits before and after the decimal point
+ */
+public class TextFromFloat implements Operation, VariableSupport {
+ public int mTextId;
+ public float mValue;
+ public float mOutValue;
+ public short mDigitsBefore;
+ public short mDigitsAfter;
+ public int mFlags;
+ public static final Companion COMPANION = new Companion();
+ public static final int MAX_STRING_SIZE = 4000;
+ char mPre = ' ';
+ char mAfter = ' ';
+ // Theses flags define what how to/if fill the space
+ public static final int PAD_AFTER_SPACE = 0; // pad past point with space
+ public static final int PAD_AFTER_NONE = 1; // do not pad past last digit
+ public static final int PAD_AFTER_ZERO = 3; // pad with 0 past last digit
+ public static final int PAD_PRE_SPACE = 0; // pad before number with spaces
+ public static final int PAD_PRE_NONE = 4; // pad before number with 0s
+ public static final int PAD_PRE_ZERO = 12; // do not pad before number
+
+ public TextFromFloat(int textId, float value, short digitsBefore,
+ short digitsAfter, int flags) {
+ this.mTextId = textId;
+ this.mValue = value;
+ this.mDigitsAfter = digitsAfter;
+ this.mDigitsBefore = digitsBefore;
+ this.mFlags = flags;
+ mOutValue = mValue;
+ switch (mFlags & 3) {
+ case PAD_AFTER_SPACE:
+ mAfter = ' ';
+ break;
+ case PAD_AFTER_NONE:
+ mAfter = 0;
+ break;
+ case PAD_AFTER_ZERO:
+ mAfter = '0';
+ break;
+ }
+ switch (mFlags & 12) {
+ case PAD_PRE_SPACE:
+ mPre = ' ';
+ break;
+ case PAD_PRE_NONE:
+ mPre = 0;
+ break;
+ case PAD_PRE_ZERO:
+ mPre = '0';
+ break;
+ }
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mTextId, mValue, mDigitsAfter, mDigitsBefore, mFlags);
+ }
+
+ @Override
+ public String toString() {
+ return "TextFromFloat[" + mTextId + "] = "
+ + Utils.floatToString(mValue) + " " + mDigitsBefore
+ + "." + mDigitsAfter + " " + mFlags;
+ }
+
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ if (Float.isNaN(mValue)) {
+ mOutValue = context.getFloat(Utils.idFromNan(mValue));
+ }
+
+ }
+
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ if (Float.isNaN(mValue)) {
+ context.listensTo(Utils.idFromNan(mValue), this);
+ }
+ }
+
+ public static class Companion implements CompanionOperation {
+ private Companion() {
+ }
+
+ @Override
+ public String name() {
+ return "TextData";
+ }
+
+ @Override
+ public int id() {
+ return Operations.TEXT_FROM_FLOAT;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ * @param buffer
+ * @param textId
+ * @param value
+ * @param digitsBefore
+ * @param digitsAfter
+ * @param flags
+ */
+ public void apply(WireBuffer buffer, int textId,
+ float value, short digitsBefore,
+ short digitsAfter, int flags) {
+ buffer.start(Operations.TEXT_FROM_FLOAT);
+ buffer.writeInt(textId);
+ buffer.writeFloat(value);
+ buffer.writeInt((digitsBefore << 16) | digitsAfter);
+ buffer.writeInt(flags);
+
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ int textId = buffer.readInt();
+ float value = buffer.readFloat();
+ int tmp = buffer.readInt();
+ short post = (short) (tmp & 0xFFFF);
+ short pre = (short) ((tmp >> 16) & 0xFFFF);
+
+ int flags = buffer.readInt();
+ operations.add(new TextFromFloat(textId, value, pre, post, flags));
+ }
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ float v = mOutValue;
+ String s = StringUtils.floatToString(v, mDigitsBefore,
+ mDigitsAfter, mPre, mAfter);
+ context.loadText(mTextId, s);
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java
new file mode 100644
index 000000000000..a0fc854f38f4
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Operation to deal with Text data
+ */
+public class TextMerge implements Operation {
+ public int mTextId;
+ public int mSrcId1;
+ public int mSrcId2;
+ public static final Companion COMPANION = new Companion();
+ public static final int MAX_STRING_SIZE = 4000;
+
+ public TextMerge(int textId, int srcId1, int srcId2) {
+ this.mTextId = textId;
+ this.mSrcId1 = srcId1;
+ this.mSrcId2 = srcId2;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mTextId, mSrcId1, mSrcId2);
+ }
+
+ @Override
+ public String toString() {
+ return "TextMerge[" + mTextId + "] = [" + mSrcId1 + " ] + [ " + mSrcId2 + "]";
+ }
+
+ public static class Companion implements CompanionOperation {
+ private Companion() {
+ }
+
+ @Override
+ public String name() {
+ return "TextData";
+ }
+
+ @Override
+ public int id() {
+ return Operations.TEXT_MERGE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ * @param buffer
+ * @param textId
+ * @param srcId1
+ * @param srcId2
+ */
+ public void apply(WireBuffer buffer, int textId, int srcId1, int srcId2) {
+ buffer.start(Operations.TEXT_MERGE);
+ buffer.writeInt(textId);
+ buffer.writeInt(srcId1);
+ buffer.writeInt(srcId2);
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ int textId = buffer.readInt();
+ int srcId1 = buffer.readInt();
+ int srcId2 = buffer.readInt();
+
+ operations.add(new TextMerge(textId, srcId1, srcId2));
+ }
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ String str1 = context.getText(mSrcId1);
+ String str2 = context.getText(mSrcId2);
+ context.loadText(mTextId, str1 + str2);
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
index 00e2f2058e89..fdc68601bf4d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
@@ -15,13 +15,16 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+/**
+ * Utilities to be used across all core operations
+ */
public class Utils {
public static float asNan(int v) {
return Float.intBitsToFloat(v | -0x800000);
}
public static int idFromNan(float value) {
- int b = Float.floatToRawIntBits(value);
+ int b = Float.floatToRawIntBits(value);
return b & 0xFFFFF;
}
@@ -29,13 +32,194 @@ public class Utils {
return 0;
}
- String getFloatString(float value) {
+ /**
+ * trim a string to n characters if needing to trim
+ * end in "..."
+ *
+ * @param str
+ * @param n
+ * @return
+ */
+ static String trimString(String str, int n) {
+ if (str.length() > n) {
+ str = str.substring(0, n - 3) + "...";
+ }
+ return str;
+ }
+
+ /**
+ * print the id and the value of a float
+ * @param idvalue
+ * @param value
+ * @return
+ */
+ public static String floatToString(float idvalue, float value) {
+ if (Float.isNaN(idvalue)) {
+ return "[" + idFromNan(idvalue) + "]" + floatToString(value);
+ }
+ return floatToString(value);
+ }
+
+ /**
+ * Convert float to string but render nan id in brackets [n]
+ * @param value
+ * @return
+ */
+ public static String floatToString(float value) {
if (Float.isNaN(value)) {
- int id = idFromNan(value);
- if (id > 0) {
- return "NaN(" + id + ")";
- }
+ return "[" + idFromNan(value) + "]";
}
- return "" + value;
+ return Float.toString(value);
+ }
+
+ /**
+ * Debugging util to print a message and include the file/line it came from
+ * @param str
+ */
+ public static void log(String str) {
+ StackTraceElement s = new Throwable().getStackTrace()[1];
+ System.out.println("(" + s.getFileName() + ":" + s.getLineNumber() + ")." + str);
+ }
+
+ /**
+ * Debugging util to print the stack
+ * @param str
+ * @param n
+ */
+ public static void logStack(String str, int n) {
+ StackTraceElement[] st = new Throwable().getStackTrace();
+ for (int i = 1; i < n + 1; i++) {
+ StackTraceElement s = st[i];
+ String space = new String(new char[i]).replace('\0', ' ');
+ System.out.println(space + "(" + s.getFileName()
+ + ":" + s.getLineNumber() + ")." + str);
+ }
+ }
+
+ /**
+ * Is a variable Allowed int calculation and references.
+ *
+ * @param v
+ * @return
+ */
+ public static boolean isVariable(float v) {
+ if (Float.isNaN(v)) {
+ int id = idFromNan(v);
+ return id > 40 || id < 10;
+ }
+ return false;
+ }
+
+ /**
+ * print a color in the familiar 0xAARRGGBB pattern
+ *
+ * @param color
+ * @return
+ */
+ public static String colorInt(int color) {
+ String str = "000000000000" + Integer.toHexString(color);
+ return "0x" + str.substring(str.length() - 8);
}
+
+ /**
+ * Interpolate two colors.
+ * gamma corrected colors are interpolated in the form c1 * (1-t) + c2 * t
+ *
+ * @param c1
+ * @param c2
+ * @param t
+ * @return
+ */
+ public static int interpolateColor(int c1, int c2, float t) {
+ if (Float.isNaN(t) || t == 0.0f) {
+ return c1;
+ } else if (t == 1.0f) {
+ return c2;
+ }
+ int a = 0xFF & (c1 >> 24);
+ int r = 0xFF & (c1 >> 16);
+ int g = 0xFF & (c1 >> 8);
+ int b = 0xFF & c1;
+ float f_r = (float) Math.pow(r / 255.0f, 2.2);
+ float f_g = (float) Math.pow(g / 255.0f, 2.2);
+ float f_b = (float) Math.pow(b / 255.0f, 2.2);
+ float c1fr = f_r;
+ float c1fg = f_g;
+ float c1fb = f_b;
+ float c1fa = a / 255f;
+
+ a = 0xFF & (c2 >> 24);
+ r = 0xFF & (c2 >> 16);
+ g = 0xFF & (c2 >> 8);
+ b = 0xFF & c2;
+ f_r = (float) Math.pow(r / 255.0f, 2.2);
+ f_g = (float) Math.pow(g / 255.0f, 2.2);
+ f_b = (float) Math.pow(b / 255.0f, 2.2);
+ float c2fr = f_r;
+ float c2fg = f_g;
+ float c2fb = f_b;
+ float c2fa = a / 255f;
+ f_r = c1fr + t * (c2fr - c1fr);
+ f_g = c1fg + t * (c2fg - c1fg);
+ f_b = c1fb + t * (c2fb - c1fb);
+ float f_a = c1fa + t * (c2fa - c1fa);
+
+ int outr = clamp((int) ((float) Math.pow(f_r, 1.0 / 2.2) * 255.0f));
+ int outg = clamp((int) ((float) Math.pow(f_g, 1.0 / 2.2) * 255.0f));
+ int outb = clamp((int) ((float) Math.pow(f_b, 1.0 / 2.2) * 255.0f));
+ int outa = clamp((int) (f_a * 255.0f));
+
+
+ return (outa << 24 | outr << 16 | outg << 8 | outb);
+ }
+
+ /**
+ * Efficient clamping function
+ *
+ * @param c
+ * @return number between 0 and 255
+ */
+ public static int clamp(int c) {
+ int n = 255;
+ c &= ~(c >> 31);
+ c -= n;
+ c &= (c >> 31);
+ c += n;
+ return c;
+ }
+
+ /**
+ * convert hue saturation and value to RGB
+ *
+ * @param hue 0..1
+ * @param saturation 0..1 0=on the gray scale
+ * @param value 0..1 0=black
+ * @return
+ */
+ public static int hsvToRgb(float hue, float saturation, float value) {
+ int h = (int) (hue * 6);
+ float f = hue * 6 - h;
+ int p = (int) (0.5f + 255 * value * (1 - saturation));
+ int q = (int) (0.5f + 255 * value * (1 - f * saturation));
+ int t = (int) (0.5f + 255 * value * (1 - (1 - f) * saturation));
+ int v = (int) (0.5f + 255 * value);
+ switch (h) {
+ case 0:
+ return 0XFF000000 | (v << 16) + (t << 8) + p;
+ case 1:
+ return 0XFF000000 | (q << 16) + (v << 8) + p;
+ case 2:
+ return 0XFF000000 | (p << 16) + (v << 8) + t;
+ case 3:
+ return 0XFF000000 | (p << 16) + (q << 8) + v;
+ case 4:
+ return 0XFF000000 | (t << 16) + (p << 8) + v;
+ case 5:
+ return 0XFF000000 | (v << 16) + (p << 8) + q;
+
+ }
+ return 0;
+ }
+
+
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
index 8abb0bfff338..a7d0ac6330f7 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
@@ -15,43 +15,60 @@
*/
package com.android.internal.widget.remotecompose.core.operations.paint;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.operations.Utils;
import java.util.Arrays;
+/**
+ * Paint Bundle represents a delta of changes to a paint object
+ */
public class PaintBundle {
int[] mArray = new int[200];
+ int[] mOutArray = null;
int mPos = 0;
- public void applyPaintChange(PaintChanges p) {
+ /**
+ * Apply changes to a PaintChanges interface
+ * @param paintContext
+ * @param p
+ */
+ public void applyPaintChange(PaintContext paintContext, PaintChanges p) {
int i = 0;
int mask = 0;
+ if (mOutArray == null) {
+ mOutArray = mArray;
+ }
while (i < mPos) {
- int cmd = mArray[i++];
+ int cmd = mOutArray[i++];
mask = mask | (1 << (cmd - 1));
switch (cmd & 0xFFFF) {
case TEXT_SIZE: {
- p.setTextSize(Float.intBitsToFloat(mArray[i++]));
+ p.setTextSize(Float.intBitsToFloat(mOutArray[i++]));
break;
}
case TYPEFACE:
int style = (cmd >> 16);
int weight = style & 0x3ff;
boolean italic = (style >> 10) > 0;
- int font_type = mArray[i++];
+ int font_type = mOutArray[i++];
p.setTypeFace(font_type, weight, italic);
break;
+ case COLOR_ID: // mOutArray should have already decoded it
case COLOR: {
- p.setColor(mArray[i++]);
+ p.setColor(mOutArray[i++]);
break;
}
case STROKE_WIDTH: {
- p.setStrokeWidth(Float.intBitsToFloat(mArray[i++]));
+ p.setStrokeWidth(Float.intBitsToFloat(mOutArray[i++]));
break;
}
case STROKE_MITER: {
- p.setStrokeMiter(Float.intBitsToFloat(mArray[i++]));
+ p.setStrokeMiter(Float.intBitsToFloat(mOutArray[i++]));
break;
}
case STROKE_CAP: {
@@ -63,6 +80,7 @@ public class PaintBundle {
break;
}
case SHADER: {
+ p.setShader(mOutArray[i++]);
break;
}
case STROKE_JOIN: {
@@ -81,17 +99,16 @@ public class PaintBundle {
p.setFilterBitmap(!((cmd >> 16) == 0));
break;
}
-
case GRADIENT: {
- i = callSetGradient(cmd, mArray, i, p);
+ i = callSetGradient(cmd, mOutArray, i, p);
break;
}
case COLOR_FILTER: {
- p.setColorFilter(mArray[i++], cmd >> 16);
+ p.setColorFilter(mOutArray[i++], cmd >> 16);
break;
}
case ALPHA: {
- p.setAlpha(Float.intBitsToFloat(mArray[i++]));
+ p.setAlpha(Float.intBitsToFloat(mOutArray[i++]));
break;
}
}
@@ -106,7 +123,6 @@ public class PaintBundle {
switch (id) {
case TEXT_SIZE:
return "TEXT_SIZE";
-
case COLOR:
return "COLOR";
case STROKE_WIDTH:
@@ -133,7 +149,6 @@ public class PaintBundle {
return "ALPHA";
case COLOR_FILTER:
return "COLOR_FILTER";
-
}
return "????" + id + "????";
}
@@ -154,6 +169,14 @@ public class PaintBundle {
return str + "]";
}
+ private static String asFloatStr(int value) {
+ float fValue = Float.intBitsToFloat(value);
+ if (Float.isNaN(fValue)) {
+ return "[" + Utils.idFromNan(fValue) + "]";
+ }
+ return Float.toString(fValue);
+ }
+
@Override
public String toString() {
StringBuilder ret = new StringBuilder("\n");
@@ -164,7 +187,8 @@ public class PaintBundle {
switch (type) {
case TEXT_SIZE: {
- ret.append(" TextSize(" + Float.intBitsToFloat(mArray[i++]));
+ ret.append(" TextSize("
+ + asFloatStr(mArray[i++]));
}
break;
@@ -181,14 +205,18 @@ public class PaintBundle {
ret.append(" Color(" + colorInt(mArray[i++]));
}
break;
+ case COLOR_ID: {
+ ret.append(" ColorId([" + mArray[i++] + "]");
+ }
+ break;
case STROKE_WIDTH: {
ret.append(" StrokeWidth("
- + (Float.intBitsToFloat(mArray[i++])));
+ + (asFloatStr(mArray[i++])));
}
break;
case STROKE_MITER: {
ret.append(" StrokeMiter("
- + (Float.intBitsToFloat(mArray[i++])));
+ + (asFloatStr(mArray[i++])));
}
break;
case STROKE_CAP: {
@@ -207,11 +235,12 @@ public class PaintBundle {
}
break;
case SHADER: {
+ ret.append(" Shader(" + mArray[i++]);
}
break;
case ALPHA: {
ret.append(" Alpha("
- + (Float.intBitsToFloat(mArray[i++])));
+ + (asFloatStr(mArray[i++])));
}
break;
case IMAGE_FILTER_QUALITY: {
@@ -244,7 +273,6 @@ public class PaintBundle {
return ret.toString();
}
-
int callPrintGradient(int cmd, int[] array, int i, StringBuilder p) {
int ret = i;
int type = (cmd >> 16);
@@ -258,26 +286,25 @@ public class PaintBundle {
colors = new int[len];
for (int j = 0; j < colors.length; j++) {
colors[j] = array[ret++];
-
}
}
len = array[ret++];
- float[] stops = null;
+ String[] stops = null;
if (len > 0) {
- stops = new float[len];
+ stops = new String[len];
for (int j = 0; j < stops.length; j++) {
- stops[j] = Float.intBitsToFloat(array[ret++]);
+ stops[j] = asFloatStr(array[ret++]);
}
}
p.append(" colors = " + colorInt(colors) + ",\n");
p.append(" stops = " + Arrays.toString(stops) + ",\n");
p.append(" start = ");
- p.append("[" + Float.intBitsToFloat(array[ret++]));
- p.append(", " + Float.intBitsToFloat(array[ret++]) + "],\n");
+ p.append("[" + asFloatStr(array[ret++]));
+ p.append(", " + asFloatStr(array[ret++]) + "],\n");
p.append(" end = ");
- p.append("[" + Float.intBitsToFloat(array[ret++]));
- p.append(", " + Float.intBitsToFloat(array[ret++]) + "],\n");
+ p.append("[" + asFloatStr(array[ret++]));
+ p.append(", " + asFloatStr(array[ret++]) + "],\n");
int tileMode = array[ret++];
p.append(" tileMode = " + tileMode + "\n ");
}
@@ -295,21 +322,21 @@ public class PaintBundle {
}
}
len = array[ret++];
- float[] stops = null;
+ String[] stops = null;
if (len > 0) {
- stops = new float[len];
+ stops = new String[len];
for (int j = 0; j < stops.length; j++) {
- stops[j] = Float.intBitsToFloat(array[ret++]);
+ stops[j] = asFloatStr(array[ret++]);
}
}
p.append(" colors = " + colorInt(colors) + ",\n");
p.append(" stops = " + Arrays.toString(stops) + ",\n");
p.append(" center = ");
- p.append("[" + Float.intBitsToFloat(array[ret++]));
- p.append(", " + Float.intBitsToFloat(array[ret++]) + "],\n");
+ p.append("[" + asFloatStr(array[ret++]));
+ p.append(", " + asFloatStr(array[ret++]) + "],\n");
p.append(" radius =");
- p.append(" " + Float.intBitsToFloat(array[ret++]) + ",\n");
+ p.append(" " + asFloatStr(array[ret++]) + ",\n");
int tileMode = array[ret++];
p.append(" tileMode = " + tileMode + "\n ");
}
@@ -327,20 +354,19 @@ public class PaintBundle {
}
}
len = array[ret++];
- float[] stops = null;
+ String[] stops = null;
if (len > 0) {
- stops = new float[len];
+ stops = new String[len];
for (int j = 0; j < stops.length; j++) {
- stops[j] = Float.intBitsToFloat(array[ret++]);
+ stops[j] = asFloatStr(array[ret++]);
}
}
-
p.append(" colors = " + colorInt(colors) + ",\n");
p.append(" stops = " + Arrays.toString(stops) + ",\n");
p.append(" center = ");
- p.append("[" + Float.intBitsToFloat(array[ret++]));
- p.append(", " + Float.intBitsToFloat(array[ret++]) + "],\n ");
-
+ p.append("[" + asFloatStr(array[ret++]));
+ p.append(", "
+ + asFloatStr(array[ret++]) + "],\n ");
}
break;
default: {
@@ -376,7 +402,6 @@ public class PaintBundle {
return ret;
}
-
switch (gradientType) {
case LINEAR_GRADIENT: {
@@ -433,7 +458,7 @@ public class PaintBundle {
public static final int COLOR = 4; // int
public static final int STROKE_WIDTH = 5; // float
public static final int STROKE_MITER = 6;
- public static final int STROKE_CAP = 7; // int
+ public static final int STROKE_CAP = 7; // int
public static final int STYLE = 8; // int
public static final int SHADER = 9; // int
public static final int IMAGE_FILTER_QUALITY = 10; // int
@@ -445,7 +470,7 @@ public class PaintBundle {
public static final int TYPEFACE = 16;
public static final int FILTER_BITMAP = 17;
public static final int BLEND_MODE = 18;
-
+ public static final int COLOR_ID = 19; // int
public static final int BLEND_MODE_CLEAR = 0;
public static final int BLEND_MODE_SRC = 1;
@@ -634,8 +659,8 @@ public class PaintBundle {
/**
* @param fontType 0 = default 1 = sans serif 2 = serif 3 = monospace
- * @param weight 100-1000
- * @param italic tur
+ * @param weight 100-1000
+ * @param italic tur
*/
public void setTextStyle(int fontType, int weight, boolean italic) {
int style = (weight & 0x3FF) | (italic ? 2048 : 0); // pack the weight and italic
@@ -658,6 +683,10 @@ public class PaintBundle {
mPos++;
}
+ /**
+ * Set the Color based on Color
+ * @param color
+ */
public void setColor(int color) {
mArray[mPos] = COLOR;
mPos++;
@@ -666,6 +695,18 @@ public class PaintBundle {
}
/**
+ * Set the Color based on ID
+ * @param color
+ */
+ public void setColorId(int color) {
+ mArray[mPos] = COLOR_ID;
+ mPos++;
+ mArray[mPos] = color;
+ mPos++;
+ }
+
+
+ /**
* Set the paint's Cap.
*
* @param cap set the paint's line cap style, used whenever the paint's
@@ -676,16 +717,29 @@ public class PaintBundle {
mPos++;
}
+ /**
+ * Set the style STROKE and/or FILL
+ * @param style
+ */
public void setStyle(int style) {
mArray[mPos] = STYLE | (style << 16);
mPos++;
}
- public void setShader(int shader, String shaderString) {
- mArray[mPos] = SHADER | (shader << 16);
+ /**
+ * Set the shader id to use
+ * @param shaderId
+ */
+ public void setShader(int shaderId) {
+ mArray[mPos] = SHADER;
+ mPos++;
+ mArray[mPos] = shaderId;
mPos++;
}
+ /**
+ * Set the Alpha value
+ */
public void setAlpha(float alpha) {
mArray[mPos] = ALPHA;
mPos++;
@@ -729,7 +783,6 @@ public class PaintBundle {
* destination pixels
* (content of the render target).
*
- *
* @param blendmode The blend mode to be installed in the paint
*/
public void setBlendMode(int blendmode) {
@@ -825,5 +878,216 @@ public class PaintBundle {
return "null";
}
-}
+ /**
+ * Check all the floats for Nan(id) floats and call listenTo
+ * @param context
+ * @param support
+ */
+ public void registerVars(RemoteContext context, VariableSupport support) {
+ int i = 0;
+ while (i < mPos) {
+ int cmd = mArray[i++];
+ int type = cmd & 0xFFFF;
+ switch (type) {
+ case STROKE_MITER:
+ case STROKE_WIDTH:
+ case ALPHA:
+ case TEXT_SIZE:
+ float v = Float.intBitsToFloat(mArray[i++]);
+ if (Float.isNaN(v)) {
+ context.listensTo(Utils.idFromNan(v), support);
+ }
+ break;
+ case COLOR_ID:
+ context.listensTo(mArray[i++], support);
+ break;
+ case COLOR:
+
+ case TYPEFACE:
+ case SHADER:
+ case COLOR_FILTER:
+ i++;
+ break;
+ case STROKE_JOIN:
+ case FILTER_BITMAP:
+ case STROKE_CAP:
+ case STYLE:
+ case IMAGE_FILTER_QUALITY:
+ case BLEND_MODE:
+ case ANTI_ALIAS:
+ break;
+
+ case GRADIENT: {
+ // TODO gradients should be handled correctly
+ i = callPrintGradient(cmd, mArray, i, new StringBuilder());
+ }
+ }
+ }
+ }
+
+ /**
+ * Update variables if any are float ids
+ * @param context
+ */
+ public void updateVariables(RemoteContext context) {
+ if (mOutArray == null) {
+ mOutArray = Arrays.copyOf(mArray, mArray.length);
+ } else {
+ System.arraycopy(mArray, 0, mOutArray, 0, mArray.length);
+ }
+ int i = 0;
+ while (i < mPos) {
+ int cmd = mArray[i++];
+ int type = cmd & 0xFFFF;
+ switch (type) {
+ case STROKE_MITER:
+ case STROKE_WIDTH:
+ case ALPHA:
+ case TEXT_SIZE:
+ mOutArray[i] = fixFloatVar(mArray[i], context);
+ i++;
+ break;
+ case COLOR_ID:
+ mOutArray[i] = fixColor(mArray[i], context);
+ i++;
+ break;
+ case COLOR:
+ case TYPEFACE:
+ case SHADER:
+ case COLOR_FILTER:
+ i++;
+ break;
+ case STROKE_JOIN:
+ case FILTER_BITMAP:
+ case STROKE_CAP:
+ case STYLE:
+ case IMAGE_FILTER_QUALITY:
+ case BLEND_MODE:
+ case ANTI_ALIAS:
+ break;
+
+ case GRADIENT: {
+ // TODO gradients should be handled correctly
+ i = updateFloatsInGradient(cmd, mOutArray, mArray, i, context);
+ }
+ }
+ }
+ }
+
+ private int fixFloatVar(int val, RemoteContext context) {
+ float v = Float.intBitsToFloat(val);
+ if (Float.isNaN(v)) {
+ int id = Utils.idFromNan(v);
+ return Float.floatToRawIntBits(context.getFloat(id));
+ }
+ return val;
+ }
+
+ private int fixColor(int colorId, RemoteContext context) {
+ int n = context.getColor(colorId);
+ return n;
+ }
+
+ int updateFloatsInGradient(int cmd, int[] out, int[] array,
+ int i,
+ RemoteContext context) {
+ int ret = i;
+ int type = (cmd >> 16);
+ switch (type) {
+ case 0: {
+ int len = array[ret++];
+ if (len > 0) {
+ for (int j = 0; j < len; j++) {
+ ret++;
+ }
+ }
+ len = array[ret++];
+
+ if (len > 0) {
+ for (int j = 0; j < len; j++) {
+ out[ret] = fixFloatVar(array[ret], context);
+ ret++;
+ }
+ }
+
+ out[ret] = fixFloatVar(array[ret], context);
+ ret++;
+ out[ret] = fixFloatVar(array[ret], context);
+ ret++;
+
+ // end
+ out[ret] = fixFloatVar(array[ret], context);
+ ret++;
+ out[ret] = fixFloatVar(array[ret], context);
+ ret++;
+ ret++; // tileMode
+ }
+
+ break;
+ case 1: {
+ // RadialGradient
+ int len = array[ret++];
+ if (len > 0) {
+ for (int j = 0; j < len; j++) {
+ ret++;
+ }
+ }
+ len = array[ret++];
+ if (len > 0) {
+ for (int j = 0; j < len; j++) {
+ out[ret] = fixFloatVar(array[ret], context);
+ ret++;
+ }
+ }
+
+
+ // center
+ out[ret] = fixFloatVar(array[ret], context);
+ ret++;
+ out[ret] = fixFloatVar(array[ret], context);
+ ret++;
+ // radius
+ out[ret] = fixFloatVar(array[ret], context);
+ ret++;
+ ret++; // tileMode
+
+ }
+
+ break;
+ case 2: {
+ // SweepGradient
+ int len = array[ret++];
+ int[] colors = null;
+ if (len > 0) {
+ colors = new int[len];
+ for (int j = 0; j < colors.length; j++) {
+ colors[j] = array[ret++];
+
+ }
+ }
+ len = array[ret++];
+ float[] stops = null;
+ if (len > 0) {
+ stops = new float[len];
+ for (int j = 0; j < stops.length; j++) {
+ out[ret] = fixFloatVar(array[ret], context);
+ ret++;
+ }
+ }
+
+ // center
+ out[ret] = fixFloatVar(array[ret], context);
+ ret++;
+ out[ret] = fixFloatVar(array[ret], context);
+ ret++;
+ }
+ break;
+ default: {
+ System.err.println("gradient type unknown");
+ }
+ }
+
+ return ret;
+ }
+} \ No newline at end of file
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java
index 994bf6d7e327..28fe63a03c67 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java
@@ -27,7 +27,6 @@ public class PaintChangeAdapter implements PaintChanges {
}
-
@Override
public void setStrokeWidth(float width) {
@@ -49,7 +48,7 @@ public class PaintChangeAdapter implements PaintChanges {
}
@Override
- public void setShader(int shader, String shaderString) {
+ public void setShader(int shader) {
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java
index 87e58ac35930..d5dc3889add3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java
@@ -15,9 +15,14 @@
*/
package com.android.internal.widget.remotecompose.core.operations.paint;
+/**
+ * Interface to a paint object
+ * For more details see Android Paint
+ */
public interface PaintChanges {
-
+ // MASK to be set/cleared
+ int CLEAR_TEXT_SIZE = 1 << (PaintBundle.TEXT_SIZE - 1);
int CLEAR_TEXT_STYLE = 1 << (PaintBundle.TYPEFACE - 1);
int CLEAR_COLOR = 1 << (PaintBundle.COLOR - 1);
int CLEAR_STROKE_WIDTH = 1 << (PaintBundle.STROKE_WIDTH - 1);
@@ -32,21 +37,101 @@ public interface PaintChanges {
int CLEAR_COLOR_FILTER = 1 << (PaintBundle.COLOR_FILTER - 1);
int VALID_BITS = 0x1FFF; // only the first 13 bit are valid now
-
+ /**
+ * Set the size of text
+ * @param size
+ */
void setTextSize(float size);
+
+ /**
+ * Set the width of lines
+ * @param width
+ */
void setStrokeWidth(float width);
+
+ /**
+ * Set the color to use
+ * @param color
+ */
void setColor(int color);
+
+ /**
+ * Set the Stroke Cap
+ * @param cap
+ */
void setStrokeCap(int cap);
+
+ /**
+ * Set the Stroke style FILL and/or STROKE
+ * @param style
+ */
void setStyle(int style);
- void setShader(int shader, String shaderString);
+
+ /**
+ * Set the id of the shader to use
+ * @param shader
+ */
+ void setShader(int shader);
+
+ /**
+ * Set the way image is interpolated
+ * @param quality
+ */
void setImageFilterQuality(int quality);
+
+ /**
+ * Set the alpha to draw under
+ * @param a
+ */
void setAlpha(float a);
+
+ /**
+ * Set the Stroke Miter
+ * @param miter
+ */
void setStrokeMiter(float miter);
+
+ /**
+ * Set the Stroke Join
+ * @param join
+ */
void setStrokeJoin(int join);
+
+ /**
+ * Should bitmaps be interpolated
+ * @param filter
+ */
void setFilterBitmap(boolean filter);
+
+ /**
+ * Set the blend mode can be porterduff + others
+ * @param mode
+ */
void setBlendMode(int mode);
+
+ /**
+ * Set the AntiAlias. Typically true
+ * Set to off when you need pixilated look (e.g. QR codes)
+ * @param aa
+ */
void setAntiAlias(boolean aa);
+
+ /**
+ * Clear some sub set of the settings
+ * @param mask
+ */
void clear(long mask);
+
+ /**
+ * Set a linear gradient fill
+ * @param colorsArray
+ * @param stopsArray
+ * @param startX
+ * @param startY
+ * @param endX
+ * @param endY
+ * @param tileMode
+ */
void setLinearGradient(
int[] colorsArray,
float[] stopsArray,
@@ -57,6 +142,15 @@ public interface PaintChanges {
int tileMode
);
+ /**
+ * Set a radial gradient fill
+ * @param colorsArray
+ * @param stopsArray
+ * @param centerX
+ * @param centerY
+ * @param radius
+ * @param tileMode
+ */
void setRadialGradient(
int[] colorsArray,
float[] stopsArray,
@@ -66,6 +160,13 @@ public interface PaintChanges {
int tileMode
);
+ /**
+ * Set a sweep gradient fill
+ * @param colorsArray
+ * @param stopsArray
+ * @param centerX
+ * @param centerY
+ */
void setSweepGradient(
int[] colorsArray,
float[] stopsArray,
@@ -73,9 +174,19 @@ public interface PaintChanges {
float centerY
);
-
+ /**
+ * Set Color filter mod
+ * @param color
+ * @param mode
+ */
void setColorFilter(int color, int mode);
+ /**
+ * Set TypeFace 0,1,2
+ * TODO above should point to a string to be decoded
+ * @param fontType
+ * @param weight
+ * @param italic
+ */
void setTypeFace(int fontType, int weight, boolean italic);
-}
-
+} \ No newline at end of file
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
new file mode 100644
index 000000000000..616048d424ec
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
@@ -0,0 +1,452 @@
+/*
+ * 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.utilities;
+
+/**
+ * high performance floating point expression evaluator used in animation
+ */
+public class AnimatedFloatExpression {
+ static IntMap<String> sNames = new IntMap<>();
+ public static final int OFFSET = 0x100;
+ public static final float ADD = asNan(OFFSET + 1);
+ public static final float SUB = asNan(OFFSET + 2);
+ public static final float MUL = asNan(OFFSET + 3);
+ public static final float DIV = asNan(OFFSET + 4);
+ public static final float MOD = asNan(OFFSET + 5);
+ public static final float MIN = asNan(OFFSET + 6);
+ public static final float MAX = asNan(OFFSET + 7);
+ public static final float POW = asNan(OFFSET + 8);
+ public static final float SQRT = asNan(OFFSET + 9);
+ public static final float ABS = asNan(OFFSET + 10);
+ public static final float SIGN = asNan(OFFSET + 11);
+ public static final float COPY_SIGN = asNan(OFFSET + 12);
+ public static final float EXP = asNan(OFFSET + 13);
+ public static final float FLOOR = asNan(OFFSET + 14);
+ public static final float LOG = asNan(OFFSET + 15);
+ public static final float LN = asNan(OFFSET + 16);
+ public static final float ROUND = asNan(OFFSET + 17);
+ public static final float SIN = asNan(OFFSET + 18);
+ public static final float COS = asNan(OFFSET + 19);
+ public static final float TAN = asNan(OFFSET + 20);
+ public static final float ASIN = asNan(OFFSET + 21);
+ public static final float ACOS = asNan(OFFSET + 22);
+
+ public static final float ATAN = asNan(OFFSET + 23);
+
+ public static final float ATAN2 = asNan(OFFSET + 24);
+ public static final float MAD = asNan(OFFSET + 25);
+ public static final float IFELSE = asNan(OFFSET + 26);
+
+ public static final float CLAMP = asNan(OFFSET + 27);
+ public static final float CBRT = asNan(OFFSET + 28);
+ public static final float DEG = asNan(OFFSET + 29);
+ public static final float RAD = asNan(OFFSET + 30);
+ public static final float CEIL = asNan(OFFSET + 31);
+
+
+ public static final float LAST_OP = 31;
+
+
+ public static final float VAR1 = asNan(OFFSET + 27);
+ public static final float VAR2 = asNan(OFFSET + 28);
+
+ // TODO CLAMP, CBRT, DEG, RAD, EXPM1, CEIL, FLOOR
+ private static final float FP_PI = (float) Math.PI;
+ private static final float FP_TO_RAD = 57.29577951f; // 180/PI
+ private static final float FP_TO_DEG = 0.01745329252f; // 180/PI
+
+ float[] mStack;
+ float[] mLocalStack = new float[128];
+ float[] mVar;
+
+ /**
+ * is float a math operator
+ * @param v
+ * @return
+ */
+ public static boolean isMathOperator(float v) {
+ if (Float.isNaN(v)) {
+ int pos = fromNaN(v);
+ return pos > OFFSET && pos <= OFFSET + LAST_OP;
+ }
+ return false;
+ }
+
+ interface Op {
+ int eval(int sp);
+ }
+
+ /**
+ * Evaluate a float expression
+ * @param exp
+ * @param var
+ * @return
+ */
+ public float eval(float[] exp, float... var) {
+ mStack = exp;
+ mVar = var;
+ int sp = -1;
+ for (int i = 0; i < mStack.length; i++) {
+ float v = mStack[i];
+ if (Float.isNaN(v)) {
+ sp = mOps[fromNaN(v) - OFFSET].eval(sp);
+ } else {
+ mStack[++sp] = v;
+ }
+ }
+ return mStack[sp];
+ }
+
+ /**
+ * Evaluate a float expression
+ * @param exp
+ * @param len
+ * @param var
+ * @return
+ */
+ public float eval(float[] exp, int len, float... var) {
+ System.arraycopy(exp, 0, mLocalStack, 0, len);
+ mStack = mLocalStack;
+ mVar = var;
+ int sp = -1;
+ for (int i = 0; i < len; i++) {
+ float v = mStack[i];
+ if (Float.isNaN(v)) {
+ sp = mOps[fromNaN(v) - OFFSET].eval(sp);
+ } else {
+ mStack[++sp] = v;
+ }
+ }
+ return mStack[sp];
+ }
+
+ /**
+ * Evaluate a float expression
+ * @param exp
+ * @param var
+ * @return
+ */
+ public float evalDB(float[] exp, float... var) {
+ mStack = exp;
+ mVar = var;
+ int sp = -1;
+ for (float v : exp) {
+ if (Float.isNaN(v)) {
+ System.out.print(" " + sNames.get((fromNaN(v) - OFFSET)));
+ sp = mOps[fromNaN(v) - OFFSET].eval(sp);
+ } else {
+ System.out.print(" " + v);
+ mStack[++sp] = v;
+ }
+ }
+ return mStack[sp];
+ }
+
+ Op[] mOps = {
+ null,
+ (sp) -> { // ADD
+ mStack[sp - 1] = mStack[sp - 1] + mStack[sp];
+ return sp - 1;
+ },
+ (sp) -> { // SUB
+ mStack[sp - 1] = mStack[sp - 1] - mStack[sp];
+ return sp - 1;
+ },
+ (sp) -> { // MUL
+ mStack[sp - 1] = mStack[sp - 1] * mStack[sp];
+ return sp - 1;
+ },
+ (sp) -> { // DIV
+ mStack[sp - 1] = mStack[sp - 1] / mStack[sp];
+ return sp - 1;
+ },
+ (sp) -> { // MOD
+ mStack[sp - 1] = mStack[sp - 1] % mStack[sp];
+ return sp - 1;
+ },
+ (sp) -> { // MIN
+ mStack[sp - 1] = (float) Math.min(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+ },
+ (sp) -> { // MAX
+ mStack[sp - 1] = (float) Math.max(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+ },
+ (sp) -> { // POW
+ mStack[sp - 1] = (float) Math.pow(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+ },
+ (sp) -> { // SQRT
+ mStack[sp] = (float) Math.sqrt(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // ABS
+ mStack[sp] = (float) Math.abs(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // SIGN
+ mStack[sp] = (float) Math.signum(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // copySign
+ mStack[sp - 1] = (float) Math.copySign(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+ },
+ (sp) -> { // EXP
+ mStack[sp] = (float) Math.exp(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // FLOOR
+ mStack[sp] = (float) Math.floor(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // LOG
+ mStack[sp] = (float) Math.log10(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // LN
+ mStack[sp] = (float) Math.log(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // ROUND
+ mStack[sp] = (float) Math.round(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // SIN
+ mStack[sp] = (float) Math.sin(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // COS
+ mStack[sp] = (float) Math.cos(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // TAN
+ mStack[sp] = (float) Math.tan(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // ASIN
+ mStack[sp] = (float) Math.asin(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // ACOS
+ mStack[sp] = (float) Math.acos(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // ATAN
+ mStack[sp] = (float) Math.atan(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // ATAN2
+ mStack[sp - 1] = (float) Math.atan2(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+ },
+ (sp) -> { // MAD
+ mStack[sp - 2] = mStack[sp] + mStack[sp - 1] * mStack[sp - 2];
+ return sp - 2;
+ },
+ (sp) -> { // Ternary conditional
+ mStack[sp - 2] = (mStack[sp] > 0)
+ ? mStack[sp - 1] : mStack[sp - 2];
+ return sp - 2;
+ },
+ (sp) -> { // CLAMP(min,max, val)
+ mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]),
+ mStack[sp - 1]);
+ return sp - 2;
+ },
+ (sp) -> { // CBRT cuberoot
+ mStack[sp] = (float) Math.pow(mStack[sp], 1 / 3.);
+ return sp;
+ },
+ (sp) -> { // DEG
+ mStack[sp] = mStack[sp] * FP_TO_RAD;
+ return sp;
+ },
+ (sp) -> { // RAD
+ mStack[sp] = mStack[sp] * FP_TO_DEG;
+ return sp;
+ },
+ (sp) -> { // CEIL
+ mStack[sp] = (float) Math.ceil(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // first var =
+ mStack[sp] = mVar[0];
+ return sp;
+ },
+ (sp) -> { // second var y?
+ mStack[sp] = mVar[1];
+ return sp;
+ },
+ (sp) -> { // 3rd var z?
+ mStack[sp] = mVar[2];
+ return sp;
+ },
+ };
+
+ static {
+ int k = 0;
+ sNames.put(k++, "NOP");
+ sNames.put(k++, "+");
+ sNames.put(k++, "-");
+ sNames.put(k++, "*");
+ sNames.put(k++, "/");
+ sNames.put(k++, "%");
+ sNames.put(k++, "min");
+ sNames.put(k++, "max");
+ sNames.put(k++, "pow");
+ sNames.put(k++, "sqrt");
+ sNames.put(k++, "abs");
+ sNames.put(k++, "sign");
+ sNames.put(k++, "copySign");
+ sNames.put(k++, "exp");
+ sNames.put(k++, "floor");
+ sNames.put(k++, "log");
+ sNames.put(k++, "ln");
+ sNames.put(k++, "round");
+ sNames.put(k++, "sin");
+ sNames.put(k++, "cos");
+ sNames.put(k++, "tan");
+ sNames.put(k++, "asin");
+ sNames.put(k++, "acos");
+ sNames.put(k++, "atan");
+ sNames.put(k++, "atan2");
+ sNames.put(k++, "mad");
+ sNames.put(k++, "ifElse");
+ sNames.put(k++, "clamp");
+ sNames.put(k++, "cbrt");
+ sNames.put(k++, "deg");
+ sNames.put(k++, "rad");
+ sNames.put(k++, "ceil");
+ sNames.put(k++, "a[0]");
+ sNames.put(k++, "a[1]");
+ sNames.put(k++, "a[2]");
+ }
+
+ /**
+ * given a float command return its math name (e.g sin, cos etc.)
+ * @param f
+ * @return
+ */
+ public static String toMathName(float f) {
+ int id = fromNaN(f) - OFFSET;
+ return sNames.get(id);
+ }
+
+ /**
+ * Convert an expression encoded as an array of floats int ot a string
+ * @param exp
+ * @param labels
+ * @return
+ */
+ public static String toString(float[] exp, String[] labels) {
+ StringBuilder s = new StringBuilder();
+ for (int i = 0; i < exp.length; i++) {
+ float v = exp[i];
+ if (Float.isNaN(v)) {
+ if (isMathOperator(v)) {
+ s.append(toMathName(v));
+ } else {
+ s.append("[");
+ s.append(fromNaN(v));
+ s.append("]");
+ }
+ } else {
+ if (labels[i] != null) {
+ s.append(labels[i]);
+ }
+ s.append(v);
+ }
+ s.append(" ");
+ }
+ return s.toString();
+ }
+
+ static String toString(float[] exp, int sp) {
+ String[] str = new String[exp.length];
+ if (Float.isNaN(exp[sp])) {
+ int id = fromNaN(exp[sp]) - OFFSET;
+ switch (NO_OF_OPS[id]) {
+ case -1:
+ return "nop";
+ case 1:
+ return sNames.get(id) + "(" + toString(exp, sp + 1) + ") ";
+ case 2:
+ if (infix(id)) {
+ return "(" + toString(exp, sp + 1)
+ + sNames.get(id) + " "
+ + toString(exp, sp + 2) + ") ";
+ } else {
+ return sNames.get(id) + "("
+ + toString(exp, sp + 1) + ", "
+ + toString(exp, sp + 2) + ")";
+ }
+ case 3:
+ if (infix(id)) {
+ return "((" + toString(exp, sp + 1) + ") ? "
+ + toString(exp, sp + 2) + ":"
+ + toString(exp, sp + 3) + ")";
+ } else {
+ return sNames.get(id)
+ + "(" + toString(exp, sp + 1)
+ + ", " + toString(exp, sp + 2)
+ + ", " + toString(exp, sp + 3) + ")";
+ }
+ }
+ }
+ return Float.toString(exp[sp]);
+ }
+
+ static final int[] NO_OF_OPS = {
+ -1, // no op
+ 2, 2, 2, 2, 2, // + - * / %
+ 2, 2, 2, // min max, power
+ 1, 1, 1, 1, 1, 1, 1, 1, //sqrt,abs,CopySign,exp,floor,log,ln
+ 1, 1, 1, 1, 1, 1, 1, 2, // round,sin,cos,tan,asin,acos,atan,atan2
+ 3, 3, 3, 1, 1, 1, 1,
+ 0, 0, 0 // mad, ?:,
+ // a[0],a[1],a[2]
+ };
+
+ /**
+ * to be used by parser to determine if command is infix
+ * @param n
+ * @return
+ */
+ static boolean infix(int n) {
+ return ((n < 6) || (n == 25) || (n == 26));
+ }
+
+ /**
+ * Convert an id into a NaN object
+ * @param v
+ * @return
+ */
+ public static float asNan(int v) {
+ return Float.intBitsToFloat(v | -0x800000);
+ }
+
+ /**
+ * Get ID from a NaN float
+ * @param v
+ * @return
+ */
+ public static int fromNaN(float v) {
+ int b = Float.floatToRawIntBits(v);
+ return b & 0xFFFFF;
+ }
+
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java
new file mode 100644
index 000000000000..0ea28a8bb900
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities;
+
+/**
+ * These are tools to use long Color as variables
+ * long colors are stored a 0xXXXXXXXX XXXXXX??
+ * in SRGB the colors are stored 0xAARRGGBB,00000000
+ * SRGB color sapce is color space 0
+ * Our Color will use color float with a
+ * Current android supports
+ * SRGB, LINEAR_SRGB, EXTENDED_SRGB, LINEAR_EXTENDED_SRGB, BT709, BT2020,
+ * DCI_P3, DISPLAY_P3, NTSC_1953, SMPTE_C, ADOBE_RGB, PRO_PHOTO_RGB, ACES,
+ * ACESCG, CIE_XYZ, CIE_LAB, BT2020_HLG, BT2020_PQ 0..17 respectively
+ *
+ * Our color space will be 62 (MAX_ID-1). (0x3E)
+ * Storing the default value in SRGB format and having the
+ * id of the color between the ARGB values and the 62 i.e.
+ * 0xAARRGGBB 00 00 00 3E
+ *
+ */
+public class ColorUtils {
+ public static int RC_COLOR = 62;
+
+ long packRCColor(int defaultARGB, int id) {
+ long l = defaultARGB;
+ return (l << 32) | id << 8 | RC_COLOR;
+ }
+
+ boolean isRCColor(long color) {
+ return ((color & 0x3F) == 62);
+ }
+
+ int getID(long color) {
+ if (isRCColor(color)) {
+ return (int) ((color & 0xFFFFFF00) >> 8);
+ }
+ return -1;
+ }
+
+ /**
+ * get default color from long color
+ * @param color
+ * @return
+ */
+ public int getDefaultColor(long color) {
+ if (isRCColor(color)) {
+ return (int) (color >> 32);
+ }
+ if (((color & 0xFF) == 0)) {
+ return (int) (color >> 32);
+ }
+ return 0;
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
index 8051ef1ab37c..0512fa6be710 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
@@ -50,7 +50,6 @@ public class IntMap<T> {
return insert(key, value);
}
-
public T get(int key) {
int index = findKey(key);
if (index == -1) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java
new file mode 100644
index 000000000000..f4cd504d650f
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java
@@ -0,0 +1,75 @@
+/*
+ * 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.utilities;
+
+import com.android.internal.widget.remotecompose.core.operations.Utils;
+
+/**
+ * This defines the major id maps and ranges used by remote compose
+ * Generally ids ranging from 0 ... FFF (4095) are for ids
+ * 0x1000-0x1100 are used for path operations in PathData
+ * 0x1100-0x1200 are used for math operations in Animated float
+ * 0x
+ */
+public class NanMap {
+
+ public static final int MOVE = 0x1000;
+ public static final int LINE = 0x1001;
+ public static final int QUADRATIC = 0x1002;
+ public static final int CONIC = 0x1003;
+ public static final int CUBIC = 0x1004;
+ public static final int CLOSE = 0x1005;
+ public static final int DONE = 0x1006;
+ 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);
+ public static final float CONIC_NAN = Utils.asNan(CONIC);
+ 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 ADD = asNan(0x1100);
+ public static final float SUB = asNan(0x1101);
+ public static final float MUL = asNan(0x1102);
+ public static final float DIV = asNan(0x1103);
+ public static final float MOD = asNan(0x1104);
+ public static final float MIN = asNan(0x1105);
+ public static final float MAX = asNan(0x1106);
+ public static final float POW = asNan(0x1107);
+
+
+ /**
+ * Get ID from Nan float
+ * @param v
+ * @return
+ */
+ public static int fromNaN(float v) {
+ int b = Float.floatToRawIntBits(v);
+ return b & 0xFFFFF;
+ }
+
+ /**
+ * Given id return as a Nan float
+ * @param v
+ * @return
+ */
+ public static float asNan(int v) {
+ return Float.intBitsToFloat(v | 0xFF800000);
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
new file mode 100644
index 000000000000..8dd5405ddf12
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities;
+
+import java.util.Arrays;
+
+/**
+ * Utilities for string manipulation
+ */
+public class StringUtils {
+ /**
+ * Converts a float into a string.
+ * Providing a defined number of characters before and after the
+ * decimal point.
+ *
+ * @param value The value to convert to string
+ * @param beforeDecimalPoint digits before the decimal point
+ * @param afterDecimalPoint digits after the decimal point
+ * @param pre character to pad width 0 = no pad typically ' ' or '0'
+ * @param post character to pad width 0 = no pad typically ' ' or '0'
+ * @return
+ */
+ public static String floatToString(float value,
+ int beforeDecimalPoint,
+ int afterDecimalPoint,
+ char pre, char post) {
+
+ int integerPart = (int) value;
+ float fractionalPart = value % 1;
+
+ // Convert integer part to string and pad with spaces
+ String integerPartString = String.valueOf(integerPart);
+ int iLen = integerPartString.length();
+ if (iLen < beforeDecimalPoint) {
+ int spacesToPad = beforeDecimalPoint - iLen;
+ if (pre != 0) {
+ char[] pad = new char[spacesToPad];
+ Arrays.fill(pad, pre);
+ integerPartString = new String(pad) + integerPartString;
+ }
+
+
+ } else if (iLen > beforeDecimalPoint) {
+ integerPartString = integerPartString.substring(iLen - beforeDecimalPoint);
+ }
+ if (afterDecimalPoint == 0) {
+ return integerPartString;
+ }
+ // Convert fractional part to string and pad with zeros
+
+ for (int i = 0; i < afterDecimalPoint; i++) {
+ fractionalPart *= 10;
+ }
+
+ fractionalPart = Math.round(fractionalPart);
+
+ for (int i = 0; i < afterDecimalPoint; i++) {
+ fractionalPart *= .1;
+ }
+
+ String fact = Float.toString(fractionalPart);
+ fact = fact.substring(2, Math.min(fact.length(), afterDecimalPoint + 2));
+ int trim = fact.length();
+ for (int i = fact.length() - 1; i >= 0; i--) {
+ if (fact.charAt(i) != '0') {
+ break;
+ }
+ trim--;
+ }
+ if (trim != fact.length()) {
+ fact = fact.substring(0, trim);
+ }
+ int len = fact.length();
+ if (post != 0 && len < afterDecimalPoint) {
+ char[] c = new char[afterDecimalPoint - len];
+ Arrays.fill(c, post);
+ fact = fact + new String(c);
+ }
+
+ return integerPartString + "." + fact;
+ }
+
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java
new file mode 100644
index 000000000000..c3cd5ae9c79d
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java
@@ -0,0 +1,67 @@
+/*
+ * 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.utilities.easing;
+
+/**
+ * Provide a specific bouncing easing function
+ */
+public class BounceCurve extends Easing {
+ private static final float N1 = 7.5625f;
+ private static final float D1 = 2.75f;
+
+ BounceCurve(int type) {
+ mType = type;
+ }
+
+ @Override
+ public float get(float x) {
+ float t = x;
+ if (t < 0) {
+ return 0f;
+ }
+ if (t < 1 / D1) {
+ return 1 / (1 + 1 / D1) * (N1 * t * t + t);
+ } else if (t < 2 / D1) {
+ t -= 1.5f / D1;
+ return N1 * t * t + 0.75f;
+ } else if (t < 2.5 / D1) {
+ t -= 2.25f / D1;
+ return N1 * t * t + 0.9375f;
+ } else if (t <= 1) {
+ t -= 2.625f / D1;
+ return N1 * t * t + 0.984375f;
+ }
+ return 1f;
+ }
+
+ @Override
+ public float getDiff(float x) {
+ if (x < 0) {
+ return 0f;
+ }
+ if (x < 1 / D1) {
+ return 2 * N1 * x / (1 + 1 / D1) + 1 / (1 + 1 / D1);
+ } else if (x < 2 / D1) {
+ return 2 * N1 * (x - 1.5f / D1);
+ } else if (x < 2.5 / D1) {
+ return 2 * N1 * (x - 2.25f / D1);
+ } else if (x <= 1) {
+ return 2 * N1 * (x - 2.625f / D1);
+ }
+ return 0f;
+ }
+
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java
new file mode 100644
index 000000000000..fd1ee036e475
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+
+class CubicEasing extends Easing {
+ float mType = 0;
+ float mX1 = 0f;
+ float mY1 = 0f;
+ float mX2 = 0f;
+ float mY2 = 0f;
+
+ private static final float[] STANDARD = {0.4f, 0.0f, 0.2f, 1f};
+ private static final float[] ACCELERATE = {0.4f, 0.05f, 0.8f, 0.7f};
+ private static final float[] DECELERATE = {0.0f, 0.0f, 0.2f, 0.95f};
+ private static final float[] LINEAR = {1f, 1f, 0f, 0f};
+ private static final float[] ANTICIPATE = {0.36f, 0f, 0.66f, -0.56f};
+ private static final float[] OVERSHOOT = {0.34f, 1.56f, 0.64f, 1f};
+
+ CubicEasing(int type) {
+ mType = type;
+ config(type);
+ }
+
+ CubicEasing(float x1, float y1, float x2, float y2) {
+ setup(x1, y1, x2, y2);
+ }
+
+ public void config(int type) {
+
+ switch (type) {
+ case CUBIC_STANDARD:
+ setup(STANDARD);
+ break;
+ case CUBIC_ACCELERATE:
+ setup(ACCELERATE);
+ break;
+ case CUBIC_DECELERATE:
+ setup(DECELERATE);
+ break;
+ case CUBIC_LINEAR:
+ setup(LINEAR);
+ break;
+ case CUBIC_ANTICIPATE:
+ setup(ANTICIPATE);
+ break;
+ case CUBIC_OVERSHOOT:
+ setup(OVERSHOOT);
+ break;
+ }
+ mType = type;
+ }
+
+ void setup(float[] values) {
+ setup(values[0], values[1], values[2], values[3]);
+ }
+
+ void setup(float x1, float y1, float x2, float y2) {
+ mX1 = x1;
+ mY1 = y1;
+ mX2 = x2;
+ mY2 = y2;
+ }
+
+ private float getX(float t) {
+ float t1 = 1 - t;
+ // no need for because start at 0,0 float f0 = (1 - t) * (1 - t) * (1 - t)
+ float f1 = 3 * t1 * t1 * t;
+ float f2 = 3 * t1 * t * t;
+ float f3 = t * t * t;
+ return mX1 * f1 + mX2 * f2 + f3;
+ }
+
+ private float getY(float t) {
+ float t1 = 1 - t;
+ // no need for testing because start at 0,0 float f0 = (1 - t) * (1 - t) * (1 - t)
+ float f1 = 3 * t1 * t1 * t;
+ float f2 = 3 * t1 * t * t;
+ float f3 = t * t * t;
+ return mY1 * f1 + mY2 * f2 + f3;
+ }
+
+ private float getDiffX(float t) {
+ float t1 = 1 - t;
+ return 3 * t1 * t1 * mX1 + 6 * t1 * t * (mX2 - mX1) + 3 * t * t * (1 - mX2);
+ }
+
+ private float getDiffY(float t) {
+ float t1 = 1 - t;
+ return 3 * t1 * t1 * mY1 + 6 * t1 * t * (mY2 - mY1) + 3 * t * t * (1 - mY2);
+ }
+
+ /**
+ * binary search for the region and linear interpolate the answer
+ */
+ public float getDiff(float x) {
+ float t = 0.5f;
+ float range = 0.5f;
+ while (range > D_ERROR) {
+ float tx = getX(t);
+ range *= 0.5;
+ if (tx < x) {
+ t += range;
+ } else {
+ t -= range;
+ }
+ }
+ float x1 = getX(t - range);
+ float x2 = getX(t + range);
+ float y1 = getY(t - range);
+ float y2 = getY(t + range);
+ return (y2 - y1) / (x2 - x1);
+ }
+
+ /**
+ * binary search for the region and linear interpolate the answer
+ */
+ public float get(float x) {
+ if (x <= 0.0f) {
+ return 0f;
+ }
+ if (x >= 1.0f) {
+ return 1.0f;
+ }
+ float t = 0.5f;
+ float range = 0.5f;
+ while (range > ERROR) {
+ float tx = getX(t);
+ range *= 0.5f;
+ if (tx < x) {
+ t += range;
+ } else {
+ t -= range;
+ }
+ }
+ float x1 = getX(t - range);
+ float x2 = getX(t + range);
+ float y1 = getY(t - range);
+ float y2 = getY(t + range);
+ return (y2 - y1) * (x - x1) / (x2 - x1) + y1;
+ }
+
+ private static final float ERROR = 0.01f;
+ private static final float D_ERROR = 0.0001f;
+}
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
new file mode 100644
index 000000000000..4ed955069d78
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+
+/**
+ * The standard interface to Easing functions
+ */
+public abstract class Easing {
+ int mType;
+ /**
+ * get the value at point x
+ */
+ public abstract float get(float x);
+
+ /**
+ * get the slope of the easing function at at x
+ */
+ public abstract float getDiff(float x);
+
+ public int getType() {
+ return mType;
+ }
+
+ public static final int CUBIC_STANDARD = 1;
+ public static final int CUBIC_ACCELERATE = 2;
+ public static final int CUBIC_DECELERATE = 3;
+ public static final int CUBIC_LINEAR = 4;
+ public static final int CUBIC_ANTICIPATE = 5;
+ public static final int CUBIC_OVERSHOOT = 6;
+ public static final int CUBIC_CUSTOM = 11;
+ public static final int SPLINE_CUSTOM = 12;
+ public static final int EASE_OUT_BOUNCE = 13;
+ public static final int EASE_OUT_ELASTIC = 14;
+
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java
new file mode 100644
index 000000000000..e26958302e3c
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+
+/**
+ * Provide a bouncing Easing function
+ */
+public class ElasticOutCurve extends Easing {
+ private static final float F_PI = (float) Math.PI;
+ private static final float C4 = 2 * F_PI / 3;
+ private static final float TWENTY_PI = 20 * F_PI;
+ private static final float LOG_8 = (float) Math.log(8.0f);
+
+ @Override
+ public float get(float x) {
+ if (x <= 0) {
+ return 0.0f;
+ }
+ if (x >= 1) {
+ return 1.0f;
+ } else
+ return (float) (Math.pow(2.0f, -10 * x)
+ * Math.sin((x * 10 - 0.75f) * C4) + 1);
+ }
+
+ @Override
+ public float getDiff(float x) {
+ if (x < 0 || x > 1) {
+ return 0.0f;
+ } else
+ return (float) ((5 * Math.pow(2.0f, (1 - 10 * x))
+ * (LOG_8 * Math.cos(TWENTY_PI * x / 3) + 2
+ * F_PI * Math.sin(TWENTY_PI * x / 3))
+ / 3));
+ }
+}
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
new file mode 100644
index 000000000000..4f484de1045e
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+
+/**
+ * Support Animation of the FloatExpression
+ */
+public class FloatAnimation extends Easing {
+ float[] mSpec;
+ // mSpec[0] = duration
+ // int(mSpec[1]) = num_of_param << 16 | type
+ // mSpec[2..1+num_of_param] params
+ // mSpec[2+num_of_param] starting Value
+ Easing mEasingCurve;
+ private int mType = CUBIC_STANDARD;
+ private float mDuration = 1;
+ private float mWrap = Float.NaN;
+ private float mInitialValue = Float.NaN;
+ private float mTargetValue = Float.NaN;
+ private float mScale = 1;
+ float mOffset = 0;
+
+ @Override
+ public String toString() {
+
+ String str = "type " + mType;
+ if (!Float.isNaN(mInitialValue)) {
+ str += " " + mInitialValue;
+ }
+ if (!Float.isNaN(mTargetValue)) {
+ str += " -> " + mTargetValue;
+ }
+ if (!Float.isNaN(mWrap)) {
+ str += " % " + mWrap;
+ }
+
+ return str;
+ }
+
+ public FloatAnimation() {
+ }
+
+ public FloatAnimation(float... description) {
+ setAnimationDescription(description);
+ }
+
+ public FloatAnimation(int type,
+ float duration,
+ float[] description,
+ float initialValue,
+ float wrap) {
+ setAnimationDescription(packToFloatArray(duration,
+ type, description, initialValue, wrap));
+ }
+
+ /**
+ * packs spec into a float array
+ *
+ * @param duration
+ * @param type
+ * @param spec
+ * @param initialValue
+ * @return
+ */
+ public static float[] packToFloatArray(float duration,
+ int type,
+ float[] spec,
+ float initialValue,
+ float wrap) {
+ int count = 0;
+
+ if (!Float.isNaN(initialValue)) {
+ count++;
+ }
+ if (spec != null) {
+ count++;
+ }
+ if (spec != null || type != CUBIC_STANDARD) {
+ count++;
+ count += (spec == null) ? 0 : spec.length;
+ }
+ if (duration != 1 || count > 0) {
+ count++;
+ }
+ if (!Float.isNaN(initialValue)) {
+ count++;
+ }
+ if (!Float.isNaN(wrap)) {
+ count++;
+ }
+ float[] ret = new float[count];
+ int pos = 0;
+ int specLen = (spec == null) ? 0 : spec.length;
+
+ if (ret.length > 0) {
+ ret[pos++] = duration;
+
+ }
+ if (ret.length > 1) {
+ int wrapBit = (Float.isNaN(wrap)) ? 0 : 1;
+ int initBit = (Float.isNaN(initialValue)) ? 0 : 2;
+ int bits = type | ((wrapBit | initBit) << 8);
+ ret[pos++] = Float.intBitsToFloat(specLen << 16 | bits);
+ }
+
+ if (specLen > 0) {
+ System.arraycopy(spec, 0, ret, pos, spec.length);
+ pos += spec.length;
+ }
+ if (!Float.isNaN(initialValue)) {
+ ret[pos++] = initialValue;
+ }
+ if (!Float.isNaN(wrap)) {
+ ret[pos] = wrap;
+ }
+ return ret;
+ }
+
+ /**
+ * Create an animation based on a float encoding of the animation
+ * @param description
+ */
+ public void setAnimationDescription(float[] description) {
+ mSpec = description;
+ mDuration = (mSpec.length == 0) ? 1 : mSpec[0];
+ int len = 0;
+ if (mSpec.length > 1) {
+ int num_type = Float.floatToRawIntBits(mSpec[1]);
+ mType = num_type & 0xFF;
+ boolean wrap = ((num_type >> 8) & 0x1) > 0;
+ boolean init = ((num_type >> 8) & 0x2) > 0;
+ len = (num_type >> 16) & 0xFFFF;
+ int off = 2 + len;
+ if (init) {
+ mInitialValue = mSpec[off++];
+ }
+ if (wrap) {
+ mWrap = mSpec[off];
+ }
+ }
+ create(mType, description, 2, len);
+ }
+
+ private void create(int type, float[] params, int offset, int len) {
+ switch (type) {
+ case CUBIC_STANDARD:
+ case CUBIC_ACCELERATE:
+ case CUBIC_DECELERATE:
+ case CUBIC_LINEAR:
+ case CUBIC_ANTICIPATE:
+ case CUBIC_OVERSHOOT:
+ mEasingCurve = new CubicEasing(type);
+ break;
+ case CUBIC_CUSTOM:
+ mEasingCurve = new CubicEasing(params[offset + 0],
+ params[offset + 1],
+ params[offset + 2],
+ params[offset + 3]
+ );
+ break;
+ case EASE_OUT_BOUNCE:
+ mEasingCurve = new BounceCurve(type);
+ break;
+ case EASE_OUT_ELASTIC:
+ mEasingCurve = new ElasticOutCurve();
+ break;
+ case SPLINE_CUSTOM:
+ mEasingCurve = new StepCurve(params, offset, len);
+ break;
+ }
+ }
+
+ /**
+ * Get the duration the interpolate is to take
+ * @return duration in seconds
+ */
+ public float getDuration() {
+ return mDuration;
+ }
+
+ /**
+ * Set the initial Value
+ * @param value
+ */
+ public void setInitialValue(float value) {
+
+ if (Float.isNaN(mWrap)) {
+ mInitialValue = value;
+ } else {
+ mInitialValue = value % mWrap;
+ }
+ setScaleOffset();
+ }
+
+ /**
+ * Set the target value to interpolate to
+ * @param value
+ */
+ public void setTargetValue(float value) {
+ if (Float.isNaN(mWrap)) {
+ mTargetValue = value;
+ } else {
+ if (Math.abs((value % mWrap) + mWrap - mInitialValue)
+ < Math.abs((value % mWrap) - mInitialValue)) {
+ mTargetValue = (value % mWrap) + mWrap;
+
+ } else {
+ mTargetValue = value % mWrap;
+ }
+ }
+ setScaleOffset();
+ }
+
+ public float getTargetValue() {
+ return mTargetValue;
+ }
+
+ private void setScaleOffset() {
+ if (!Float.isNaN(mInitialValue) && !Float.isNaN(mTargetValue)) {
+ mScale = (mTargetValue - mInitialValue);
+ mOffset = mInitialValue;
+ } else {
+ mScale = 1;
+ mOffset = 0;
+ }
+ }
+
+ /**
+ * get the value at time t in seconds since start
+ */
+ public float get(float t) {
+ return mEasingCurve.get(t / mDuration)
+ * (mTargetValue - mInitialValue) + mInitialValue;
+ }
+
+ /**
+ * get the slope of the easing function at at x
+ */
+ public float getDiff(float t) {
+ return mEasingCurve.getDiff(t / mDuration) * (mTargetValue - mInitialValue);
+ }
+
+ 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
new file mode 100644
index 000000000000..693deafc5b00
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java
@@ -0,0 +1,81 @@
+/*
+ * 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.utilities.easing;
+
+/**
+ * Provides and interface to create easing functions
+ */
+public class GeneralEasing extends Easing{
+ float[] mEasingData = new float[0];
+ Easing mEasingCurve = new CubicEasing(CUBIC_STANDARD);
+
+ /**
+ * Set the curve based on the float encoding of it
+ * @param data
+ */
+ public void setCurveSpecification(float[] data) {
+ mEasingData = data;
+ createEngine();
+ }
+
+ public float[] getCurveSpecification() {
+ return mEasingData;
+ }
+
+ void createEngine() {
+ int type = Float.floatToRawIntBits(mEasingData[0]);
+ switch (type) {
+ case CUBIC_STANDARD:
+ case CUBIC_ACCELERATE:
+ case CUBIC_DECELERATE:
+ case CUBIC_LINEAR:
+ case CUBIC_ANTICIPATE:
+ case CUBIC_OVERSHOOT:
+ mEasingCurve = new CubicEasing(type);
+ break;
+ case CUBIC_CUSTOM:
+ mEasingCurve = new CubicEasing(mEasingData[1],
+ mEasingData[2],
+ mEasingData[3],
+ mEasingData[5]
+ );
+ break;
+ case EASE_OUT_BOUNCE:
+ mEasingCurve = new BounceCurve(type);
+ break;
+ }
+ }
+
+ /**
+ * get the value at point x
+ */
+ public float get(float x) {
+ return mEasingCurve.get(x);
+ }
+
+ /**
+ * get the slope of the easing function at at x
+ */
+ public float getDiff(float x) {
+ return mEasingCurve.getDiff(x);
+ }
+
+ public int getType() {
+ return mEasingCurve.getType();
+ }
+
+
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java
new file mode 100644
index 000000000000..23930b92a282
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+
+import java.util.Arrays;
+
+/**
+ * This performs a spline interpolation in multiple dimensions
+ *
+ *
+ */
+public class MonotonicCurveFit {
+ private static final String TAG = "MonotonicCurveFit";
+ private double[] mT;
+ private double[][] mY;
+ private double[][] mTangent;
+ private boolean mExtrapolate = true;
+ double[] mSlopeTemp;
+
+ /**
+ * create a collection of curves
+ * @param time the point along the curve
+ * @param y the parameter at those points
+ */
+ public MonotonicCurveFit(double[] time, double[][] y) {
+ final int n = time.length;
+ final int dim = y[0].length;
+ mSlopeTemp = new double[dim];
+ double[][] slope = new double[n - 1][dim]; // could optimize this out
+ double[][] tangent = new double[n][dim];
+ for (int j = 0; j < dim; j++) {
+ for (int i = 0; i < n - 1; i++) {
+ double dt = time[i + 1] - time[i];
+ slope[i][j] = (y[i + 1][j] - y[i][j]) / dt;
+ if (i == 0) {
+ tangent[i][j] = slope[i][j];
+ } else {
+ tangent[i][j] = (slope[i - 1][j] + slope[i][j]) * 0.5f;
+ }
+ }
+ tangent[n - 1][j] = slope[n - 2][j];
+ }
+
+ for (int i = 0; i < n - 1; i++) {
+ for (int j = 0; j < dim; j++) {
+ if (slope[i][j] == 0.) {
+ tangent[i][j] = 0.;
+ tangent[i + 1][j] = 0.;
+ } else {
+ double a = tangent[i][j] / slope[i][j];
+ double b = tangent[i + 1][j] / slope[i][j];
+ double h = Math.hypot(a, b);
+ if (h > 9.0) {
+ double t = 3. / h;
+ tangent[i][j] = t * a * slope[i][j];
+ tangent[i + 1][j] = t * b * slope[i][j];
+ }
+ }
+ }
+ }
+ mT = time;
+ mY = y;
+ mTangent = tangent;
+ }
+
+ /**
+ * Get the position of all curves at time t
+ * @param t
+ * @param v
+ */
+ public void getPos(double t, double[] v) {
+ final int n = mT.length;
+ final int dim = mY[0].length;
+ if (mExtrapolate) {
+ if (t <= mT[0]) {
+ getSlope(mT[0], mSlopeTemp);
+ for (int j = 0; j < dim; j++) {
+ v[j] = mY[0][j] + (t - mT[0]) * mSlopeTemp[j];
+ }
+ return;
+ }
+ if (t >= mT[n - 1]) {
+ getSlope(mT[n - 1], mSlopeTemp);
+ for (int j = 0; j < dim; j++) {
+ v[j] = mY[n - 1][j] + (t - mT[n - 1]) * mSlopeTemp[j];
+ }
+ return;
+ }
+ } else {
+ if (t <= mT[0]) {
+ for (int j = 0; j < dim; j++) {
+ v[j] = mY[0][j];
+ }
+ return;
+ }
+ if (t >= mT[n - 1]) {
+ for (int j = 0; j < dim; j++) {
+ v[j] = mY[n - 1][j];
+ }
+ return;
+ }
+ }
+
+ for (int i = 0; i < n - 1; i++) {
+ if (t == mT[i]) {
+ for (int j = 0; j < dim; j++) {
+ v[j] = mY[i][j];
+ }
+ }
+ if (t < mT[i + 1]) {
+ double h = mT[i + 1] - mT[i];
+ double x = (t - mT[i]) / h;
+ for (int j = 0; j < dim; j++) {
+ double y1 = mY[i][j];
+ double y2 = mY[i + 1][j];
+ double t1 = mTangent[i][j];
+ double t2 = mTangent[i + 1][j];
+ v[j] = interpolate(h, x, y1, y2, t1, t2);
+ }
+ return;
+ }
+ }
+ }
+
+ /**
+ * Get the position of all curves at time t
+ * @param t
+ * @param v
+ */
+ public void getPos(double t, float[] v) {
+ final int n = mT.length;
+ final int dim = mY[0].length;
+ if (mExtrapolate) {
+ if (t <= mT[0]) {
+ getSlope(mT[0], mSlopeTemp);
+ for (int j = 0; j < dim; j++) {
+ v[j] = (float) (mY[0][j] + (t - mT[0]) * mSlopeTemp[j]);
+ }
+ return;
+ }
+ if (t >= mT[n - 1]) {
+ getSlope(mT[n - 1], mSlopeTemp);
+ for (int j = 0; j < dim; j++) {
+ v[j] = (float) (mY[n - 1][j] + (t - mT[n - 1]) * mSlopeTemp[j]);
+ }
+ return;
+ }
+ } else {
+ if (t <= mT[0]) {
+ for (int j = 0; j < dim; j++) {
+ v[j] = (float) mY[0][j];
+ }
+ return;
+ }
+ if (t >= mT[n - 1]) {
+ for (int j = 0; j < dim; j++) {
+ v[j] = (float) mY[n - 1][j];
+ }
+ return;
+ }
+ }
+
+ for (int i = 0; i < n - 1; i++) {
+ if (t == mT[i]) {
+ for (int j = 0; j < dim; j++) {
+ v[j] = (float) mY[i][j];
+ }
+ }
+ if (t < mT[i + 1]) {
+ double h = mT[i + 1] - mT[i];
+ double x = (t - mT[i]) / h;
+ for (int j = 0; j < dim; j++) {
+ double y1 = mY[i][j];
+ double y2 = mY[i + 1][j];
+ double t1 = mTangent[i][j];
+ double t2 = mTangent[i + 1][j];
+ v[j] = (float) interpolate(h, x, y1, y2, t1, t2);
+ }
+ return;
+ }
+ }
+ }
+
+ /**
+ * Get the position of the jth curve at time t
+ * @param t
+ * @param j
+ * @return
+ */
+ public double getPos(double t, int j) {
+ final int n = mT.length;
+ if (mExtrapolate) {
+ if (t <= mT[0]) {
+ return mY[0][j] + (t - mT[0]) * getSlope(mT[0], j);
+ }
+ if (t >= mT[n - 1]) {
+ return mY[n - 1][j] + (t - mT[n - 1]) * getSlope(mT[n - 1], j);
+ }
+ } else {
+ if (t <= mT[0]) {
+ return mY[0][j];
+ }
+ if (t >= mT[n - 1]) {
+ return mY[n - 1][j];
+ }
+ }
+
+ for (int i = 0; i < n - 1; i++) {
+ if (t == mT[i]) {
+ return mY[i][j];
+ }
+ if (t < mT[i + 1]) {
+ double h = mT[i + 1] - mT[i];
+ double x = (t - mT[i]) / h;
+ double y1 = mY[i][j];
+ double y2 = mY[i + 1][j];
+ double t1 = mTangent[i][j];
+ double t2 = mTangent[i + 1][j];
+ return interpolate(h, x, y1, y2, t1, t2);
+
+ }
+ }
+ return 0; // should never reach here
+ }
+
+ /**
+ * Get the slope of all the curves at position t
+ * @param t
+ * @param v
+ */
+ public void getSlope(double t, double[] v) {
+ final int n = mT.length;
+ int dim = mY[0].length;
+ if (t <= mT[0]) {
+ t = mT[0];
+ } else if (t >= mT[n - 1]) {
+ t = mT[n - 1];
+ }
+
+ for (int i = 0; i < n - 1; i++) {
+ if (t <= mT[i + 1]) {
+ double h = mT[i + 1] - mT[i];
+ double x = (t - mT[i]) / h;
+ for (int j = 0; j < dim; j++) {
+ double y1 = mY[i][j];
+ double y2 = mY[i + 1][j];
+ double t1 = mTangent[i][j];
+ double t2 = mTangent[i + 1][j];
+ v[j] = diff(h, x, y1, y2, t1, t2) / h;
+ }
+ break;
+ }
+ }
+ return;
+ }
+
+ /**
+ * Get the slope of the j curve at position t
+ * @param t
+ * @param j
+ * @return
+ */
+ public double getSlope(double t, int j) {
+ final int n = mT.length;
+
+ if (t < mT[0]) {
+ t = mT[0];
+ } else if (t >= mT[n - 1]) {
+ t = mT[n - 1];
+ }
+ for (int i = 0; i < n - 1; i++) {
+ if (t <= mT[i + 1]) {
+ double h = mT[i + 1] - mT[i];
+ double x = (t - mT[i]) / h;
+ double y1 = mY[i][j];
+ double y2 = mY[i + 1][j];
+ double t1 = mTangent[i][j];
+ double t2 = mTangent[i + 1][j];
+ return diff(h, x, y1, y2, t1, t2) / h;
+ }
+ }
+ return 0; // should never reach here
+ }
+
+ public double[] getTimePoints() {
+ return mT;
+ }
+
+ /**
+ * Cubic Hermite spline
+ */
+ private static double interpolate(double h,
+ double x,
+ double y1,
+ double y2,
+ double t1,
+ double t2) {
+ double x2 = x * x;
+ double x3 = x2 * x;
+ return -2 * x3 * y2 + 3 * x2 * y2 + 2 * x3 * y1 - 3 * x2 * y1 + y1
+ + h * t2 * x3 + h * t1 * x3 - h * t2 * x2 - 2 * h * t1 * x2
+ + h * t1 * x;
+ }
+
+ /**
+ * Cubic Hermite spline slope differentiated
+ */
+ private static double diff(double h, double x, double y1, double y2, double t1, double t2) {
+ double x2 = x * x;
+ return -6 * x2 * y2 + 6 * x * y2 + 6 * x2 * y1 - 6 * x * y1 + 3 * h * t2 * x2
+ + 3 * h * t1 * x2 - 2 * h * t2 * x - 4 * h * t1 * x + h * t1;
+ }
+
+ /**
+ * This builds a monotonic spline to be used as a wave function
+ */
+ public static MonotonicCurveFit buildWave(String configString) {
+ // done this way for efficiency
+ String str = configString;
+ double[] values = new double[str.length() / 2];
+ int start = configString.indexOf('(') + 1;
+ int off1 = configString.indexOf(',', start);
+ int count = 0;
+ while (off1 != -1) {
+ String tmp = configString.substring(start, off1).trim();
+ values[count++] = Double.parseDouble(tmp);
+ off1 = configString.indexOf(',', start = off1 + 1);
+ }
+ off1 = configString.indexOf(')', start);
+ String tmp = configString.substring(start, off1).trim();
+ values[count++] = Double.parseDouble(tmp);
+
+ return buildWave(Arrays.copyOf(values, count));
+ }
+
+ private static MonotonicCurveFit buildWave(double[] values) {
+ int length = values.length * 3 - 2;
+ int len = values.length - 1;
+ double gap = 1.0 / len;
+ double[][] points = new double[length][1];
+ double[] time = new double[length];
+ for (int i = 0; i < values.length; i++) {
+ double v = values[i];
+ points[i + len][0] = v;
+ time[i + len] = i * gap;
+ if (i > 0) {
+ points[i + len * 2][0] = v + 1;
+ time[i + len * 2] = i * gap + 1;
+
+ points[i - 1][0] = v - 1 - gap;
+ time[i - 1] = i * gap + -1 - gap;
+ }
+ }
+
+ return new MonotonicCurveFit(time, points);
+ }
+}
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
new file mode 100644
index 000000000000..6ed6548405d2
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+
+
+/**
+ * This class translates a series of floating point values into a continuous
+ * curve for use in an easing function including quantize functions
+ * it is used with the "spline(0,0.3,0.3,0.5,...0.9,1)" it should start at 0 and end at one 1
+ */
+public class StepCurve extends Easing {
+ private static final boolean DEBUG = false;
+ MonotonicCurveFit mCurveFit;
+
+ public StepCurve(float[] params, int offset, int len) {
+ mCurveFit = genSpline(params, offset, len);
+ }
+
+ private static MonotonicCurveFit genSpline(float[] values, int off, int arrayLen) {
+ int length = arrayLen * 3 - 2;
+ int len = arrayLen - 1;
+ double gap = 1.0 / len;
+ double[][] points = new double[length][1];
+ double[] time = new double[length];
+ for (int i = 0; i < arrayLen; i++) {
+ double v = values[i + off];
+ points[i + len][0] = v;
+ time[i + len] = i * gap;
+ if (i > 0) {
+ points[i + len * 2][0] = v + 1;
+ time[i + len * 2] = i * gap + 1;
+
+ points[i - 1][0] = v - 1 - gap;
+ time[i - 1] = i * gap + -1 - gap;
+ }
+ }
+
+ MonotonicCurveFit ms = new MonotonicCurveFit(time, points);
+
+ return ms;
+ }
+
+ @Override
+ public float getDiff(float x) {
+ return (float) mCurveFit.getSlope(x, 0);
+ }
+
+
+ @Override
+ public float get(float x) {
+ return (float) mCurveFit.getPos(x, 0);
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
index bcda27a40f64..cb0d62ed12e0 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
@@ -79,6 +79,13 @@ public class RemoteComposeDocument {
}
/**
+ * The delay in milliseconds to next repaint -1 = not needed 0 = asap
+ * @return delay in milliseconds to next repaint or -1
+ */
+ public int needsRepaint() {
+ return mDocument.needsRepaint();
+ }
+ /**
* Returns true if the document can be displayed given this version of the player
*
* @param majorVersion the max major version supported by the player
@@ -89,5 +96,11 @@ public class RemoteComposeDocument {
return mDocument.canBeDisplayed(majorVersion, minorVersion, capabilities);
}
+ @Override
+ public String toString() {
+ return "Document{\n"
+ + mDocument
+ + '}';
+ }
}
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 7423a1679d36..cc1f3ddcc093 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
@@ -16,7 +16,6 @@
package com.android.internal.widget.remotecompose.player;
import android.content.Context;
-import android.graphics.Color;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ViewGroup;
@@ -98,7 +97,6 @@ public class RemoteComposePlayer extends FrameLayout {
LayoutParams.MATCH_PARENT);
HorizontalScrollView horizontalScrollView =
new HorizontalScrollView(getContext());
- horizontalScrollView.setBackgroundColor(Color.TRANSPARENT);
horizontalScrollView.setFillViewport(true);
horizontalScrollView.addView(mInner, layoutParamsInner);
LayoutParams layoutParams = new LayoutParams(
@@ -115,7 +113,6 @@ public class RemoteComposePlayer extends FrameLayout {
LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT);
ScrollView scrollView = new ScrollView(getContext());
- scrollView.setBackgroundColor(Color.TRANSPARENT);
scrollView.setFillViewport(true);
scrollView.addView(mInner, layoutParamsInner);
LayoutParams layoutParams = new LayoutParams(
@@ -139,9 +136,7 @@ public class RemoteComposePlayer extends FrameLayout {
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
- setBackgroundColor(Color.TRANSPARENT);
mInner = new RemoteComposeCanvas(context, attrs, defStyleAttr);
- mInner.setBackgroundColor(Color.TRANSPARENT);
addView(mInner, layoutParams);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
index d0d6e6982a16..ecb68bb21fb3 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
@@ -26,6 +26,7 @@ import android.graphics.PorterDuffColorFilter;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.RuntimeShader;
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.graphics.Typeface;
@@ -33,6 +34,8 @@ import android.graphics.Typeface;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.operations.ClipPath;
+import com.android.internal.widget.remotecompose.core.operations.ShaderData;
+import com.android.internal.widget.remotecompose.core.operations.Utils;
import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
import com.android.internal.widget.remotecompose.core.operations.paint.PaintChanges;
@@ -43,6 +46,7 @@ import com.android.internal.widget.remotecompose.core.operations.paint.PaintChan
public class AndroidPaintContext extends PaintContext {
Paint mPaint = new Paint();
Canvas mCanvas;
+ Rect mTmpRect = new Rect(); // use in calculation of bounds
public AndroidPaintContext(RemoteContext context, Canvas canvas) {
super(context);
@@ -177,6 +181,22 @@ public class AndroidPaintContext extends PaintContext {
}
@Override
+ public void getTextBounds(int textId, int start, int end, boolean monospace, float[] bounds) {
+ String str = getText(textId);
+ if (end == -1) {
+ end = str.length();
+ }
+
+ mPaint.getTextBounds(str, start, end, mTmpRect);
+
+ bounds[0] = mTmpRect.left;
+ bounds[1] = mTmpRect.top;
+ bounds[2] = monospace ? (mPaint.measureText(str, start, end) - mTmpRect.left)
+ : mTmpRect.right;
+ bounds[3] = mTmpRect.bottom;
+ }
+
+ @Override
public void drawTextRun(int textID,
int start,
int end,
@@ -185,7 +205,16 @@ public class AndroidPaintContext extends PaintContext {
float x,
float y,
boolean rtl) {
- String textToPaint = getText(textID).substring(start, end);
+
+ String textToPaint = getText(textID);
+ if (end == -1) {
+ if (start != 0) {
+ textToPaint = textToPaint.substring(start);
+ }
+ } else {
+ textToPaint = textToPaint.substring(start, end);
+ }
+
mCanvas.drawText(textToPaint, x, y, mPaint);
}
@@ -308,7 +337,7 @@ public class AndroidPaintContext extends PaintContext {
@Override
public void applyPaint(PaintBundle mPaintData) {
- mPaintData.applyPaintChange(new PaintChanges() {
+ mPaintData.applyPaintChange((PaintContext) this, new PaintChanges() {
@Override
public void setTextSize(float size) {
mPaint.setTextSize(size);
@@ -361,10 +390,8 @@ public class AndroidPaintContext extends PaintContext {
}
}
-
}
-
@Override
public void setStrokeWidth(float width) {
mPaint.setStrokeWidth(width);
@@ -386,13 +413,37 @@ public class AndroidPaintContext extends PaintContext {
}
@Override
- public void setShader(int shader, String shaderString) {
-
+ public void setShader(int shaderId) {
+ // TODO this stuff should check the shader creation
+ if (shaderId == 0) {
+ mPaint.setShader(null);
+ return;
+ }
+ ShaderData data = getShaderData(shaderId);
+ RuntimeShader shader = new RuntimeShader(getText(data.getShaderTextId()));
+ String[] names = data.getUniformFloatNames();
+ for (int i = 0; i < names.length; i++) {
+ String name = names[i];
+ float[] val = data.getUniformFloats(name);
+ shader.setFloatUniform(name, val);
+ }
+ names = data.getUniformIntegerNames();
+ for (int i = 0; i < names.length; i++) {
+ String name = names[i];
+ int[] val = data.getUniformInts(name);
+ shader.setIntUniform(name, val);
+ }
+ names = data.getUniformBitmapNames();
+ for (int i = 0; i < names.length; i++) {
+ String name = names[i];
+ int val = data.getUniformBitmapId(name);
+ }
+ mPaint.setShader(shader);
}
@Override
public void setImageFilterQuality(int quality) {
- System.out.println(">>>>>>>>>>>> ");
+ Utils.log(" quality =" + quality);
}
@Override
@@ -420,7 +471,6 @@ public class AndroidPaintContext extends PaintContext {
mPaint.setFilterBitmap(filter);
}
-
@Override
public void setAntiAlias(boolean aa) {
mPaint.setAntiAlias(aa);
@@ -437,7 +487,6 @@ public class AndroidPaintContext extends PaintContext {
case PaintBundle.COLOR_FILTER:
mPaint.setColorFilter(null);
- System.out.println(">>>>>>>>>>>>> CLEAR!!!!");
break;
}
}
@@ -446,12 +495,11 @@ public class AndroidPaintContext extends PaintContext {
}
}
- Shader.TileMode[] mTilesModes = new Shader.TileMode[]{
+ Shader.TileMode[] mTileModes = new Shader.TileMode[]{
Shader.TileMode.CLAMP,
Shader.TileMode.REPEAT,
Shader.TileMode.MIRROR};
-
@Override
public void setLinearGradient(int[] colors,
float[] stops,
@@ -463,7 +511,7 @@ public class AndroidPaintContext extends PaintContext {
mPaint.setShader(new LinearGradient(startX,
startY,
endX,
- endY, colors, stops, mTilesModes[tileMode]));
+ endY, colors, stops, mTileModes[tileMode]));
}
@@ -475,7 +523,7 @@ public class AndroidPaintContext extends PaintContext {
float radius,
int tileMode) {
mPaint.setShader(new RadialGradient(centerX, centerY, radius,
- colors, stops, mTilesModes[tileMode]));
+ colors, stops, mTileModes[tileMode]));
}
@Override
@@ -490,7 +538,6 @@ public class AndroidPaintContext extends PaintContext {
@Override
public void setColorFilter(int color, int mode) {
PorterDuff.Mode pmode = origamiToPorterDuffMode(mode);
- System.out.println("setting color filter to " + pmode.name());
if (pmode != null) {
mPaint.setColorFilter(
new PorterDuffColorFilter(color, pmode));
@@ -500,10 +547,10 @@ public class AndroidPaintContext extends PaintContext {
}
@Override
- public void mtrixScale(float scaleX,
- float scaleY,
- float centerX,
- float centerY) {
+ public void matrixScale(float scaleX,
+ float scaleY,
+ float centerX,
+ float centerY) {
if (Float.isNaN(centerX)) {
mCanvas.scale(scaleX, scaleY);
} else {
@@ -556,6 +603,11 @@ public class AndroidPaintContext extends PaintContext {
}
}
+ @Override
+ public void reset() {
+ mPaint.reset();
+ }
+
private Path getPath(int path1Id,
int path2Id,
float tween,
@@ -599,5 +651,9 @@ public class AndroidPaintContext extends PaintContext {
private String getText(int id) {
return (String) mContext.mRemoteComposeState.getFromId(id);
}
+
+ private ShaderData getShaderData(int id) {
+ return (ShaderData) mContext.mRemoteComposeState.getFromId(id);
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
index 270e96f11942..6e4893bc0ee6 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
@@ -20,10 +20,15 @@ import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
+import com.android.internal.widget.remotecompose.core.operations.ShaderData;
+
+import java.util.HashMap;
/**
* An implementation of Context for Android.
- *
+ * <p>
* This is used to play the RemoteCompose operations on Android.
*/
class AndroidRemoteContext extends RemoteContext {
@@ -33,6 +38,7 @@ class AndroidRemoteContext extends RemoteContext {
mPaintContext = new AndroidPaintContext(this, canvas);
} else {
// need to make sure to update the canvas for the current one
+ mPaintContext.reset();
((AndroidPaintContext) mPaintContext).setCanvas(canvas);
}
mWidth = canvas.getWidth();
@@ -50,13 +56,32 @@ class AndroidRemoteContext extends RemoteContext {
}
}
+ static class VarName {
+ String mName;
+ int mId;
+ int mType;
+
+ VarName(String name, int id, int type) {
+ mName = name;
+ mId = id;
+ mType = type;
+ }
+ }
+
+ HashMap<String, VarName> mVarNameHashMap = new HashMap<>();
+
+ @Override
+ public void loadVariableName(String varName, int varId, int varType) {
+ mVarNameHashMap.put(varName, new VarName(varName, varId, varType));
+ }
+
/**
* Decode a byte array into an image and cache it using the given imageId
*
- * @oaram imageId the id of the image
- * @param width with of image to be loaded
+ * @param width with of image to be loaded
* @param height height of image to be loaded
* @param bitmap a byte array containing the image information
+ * @oaram imageId the id of the image
*/
@Override
public void loadBitmap(int imageId, int width, int height, byte[] bitmap) {
@@ -70,14 +95,66 @@ class AndroidRemoteContext extends RemoteContext {
public void loadText(int id, String text) {
if (!mRemoteComposeState.containsId(id)) {
mRemoteComposeState.cache(id, text);
+ } else {
+ mRemoteComposeState.update(id, text);
}
}
+ @Override
+ public String getText(int id) {
+ return (String) mRemoteComposeState.getFromId(id);
+ }
+
+ @Override
+ public void loadFloat(int id, float value) {
+ mRemoteComposeState.updateFloat(id, value);
+ }
+
+
+ @Override
+ public void loadColor(int id, int color) {
+ mRemoteComposeState.updateColor(id, color);
+ }
+
+ @Override
+ public void loadAnimatedFloat(int id, FloatExpression animatedFloat) {
+ mRemoteComposeState.cache(id, animatedFloat);
+ }
+
+ @Override
+ public void loadShader(int id, ShaderData value) {
+ mRemoteComposeState.cache(id, value);
+ }
+
+ @Override
+ public float getFloat(int id) {
+ return (float) mRemoteComposeState.getFloat(id);
+ }
+
+ @Override
+ public int getColor(int id) {
+ return mRemoteComposeState.getColor(id);
+ }
+
+ @Override
+ public void listensTo(int id, VariableSupport variableSupport) {
+ mRemoteComposeState.listenToVar(id, variableSupport);
+ }
+
+ @Override
+ public int updateOps() {
+ return mRemoteComposeState.getOpsToUpdate(this);
+ }
+
+ @Override
+ public ShaderData getShader(int id) {
+ return (ShaderData) mRemoteComposeState.getFromId(id);
+ }
+
///////////////////////////////////////////////////////////////////////////////////////////////
// Click handling
///////////////////////////////////////////////////////////////////////////////////////////////
-
@Override
public void addClickArea(int id,
int contentDescriptionId,
@@ -87,7 +164,7 @@ class AndroidRemoteContext extends RemoteContext {
float bottom,
int metadataId) {
String contentDescription = (String) mRemoteComposeState.getFromId(contentDescriptionId);
- String metadata = (String) mRemoteComposeState.getFromId(metadataId);
+ String metadata = (String) mRemoteComposeState.getFromId(metadataId);
mDocument.addClickArea(id, contentDescription, left, top, right, bottom, metadata);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java b/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java
index 672dae32d8e0..329178abe8b5 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java
@@ -20,7 +20,6 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.View;
-
/**
* Implementation for the click handling
*/
@@ -40,7 +39,6 @@ class ClickAreaView extends View {
setContentDescription(contentDescription);
}
-
public void setDebug(boolean value) {
if (mDebug != value) {
mDebug = value;
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 a3bb73e22c59..97d23c84b5bd 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
@@ -85,6 +85,7 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
mDocument.initializeContext(mARContext);
setContentDescription(mDocument.getDocument().getContentDescription());
requestLayout();
+ invalidate();
}
AndroidRemoteContext mARContext = new AndroidRemoteContext();
@@ -119,8 +120,7 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
removeAllViews();
}
-
- public interface ClickCallbacks {
+ public interface ClickCallbacks {
void click(int id, String metadata);
}
@@ -213,6 +213,9 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
setMeasuredDimension(w, h);
}
+ private int mCount;
+ private long mTime = System.nanoTime();
+
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
@@ -224,6 +227,17 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
mARContext.mWidth = getWidth();
mARContext.mHeight = getHeight();
mDocument.paint(mARContext, mTheme);
+ if (mDebug) {
+ mCount++;
+ if (System.nanoTime() - mTime > 1000000000L) {
+ System.out.println(" count " + mCount + " fps");
+ mCount = 0;
+ mTime = System.nanoTime();
+ }
+ }
+ if (mDocument.needsRepaint() > 0) {
+ invalidate();
+ }
}
}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c6d4f3ad771d..4dfe000659ff 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4840,7 +4840,7 @@
See android.credentials.CredentialManager
-->
<string name="config_defaultCredentialManagerHybridService" translatable="false"></string>
-
+
<!-- The component name, flattened to a string, for the system's credential manager
autofill service. This service allows interceding autofill requests and routing
them to credential manager.
@@ -6503,6 +6503,9 @@
<string-array name="config_sharedLibrariesLoadedAfterApp" translatable="false">
</string-array>
+ <!-- the number of the max cached processes in the system. -->
+ <integer name="config_customizedMaxCachedProcesses">32</integer>
+
<!-- Whether this device should support taking app snapshots on closure -->
<bool name="config_disableTaskSnapshots">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4442b5e4c044..cc74d023ee5e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -5067,6 +5067,8 @@
code and resources provided by applications. -->
<java-symbol type="array" name="config_sharedLibrariesLoadedAfterApp" />
+ <java-symbol type="integer" name="config_customizedMaxCachedProcesses" />
+
<java-symbol type="color" name="overview_background"/>
<java-symbol type="bool" name="config_disableTaskSnapshots" />
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
index d92d24d9e22d..94c281fa9fac 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
@@ -685,46 +685,53 @@ class DividerPresenter implements View.OnTouchListener {
? taskBounds.width() - mProperties.mDividerWidthPx
: taskBounds.height() - mProperties.mDividerWidthPx;
- if (isDraggingToFullscreenAllowed(mProperties.mDividerAttributes)) {
- final float displayDensity = getDisplayDensity();
- return dividerPositionWithDraggingToFullscreenAllowed(
- dividerPosition,
- minPosition,
- maxPosition,
- fullyExpandedPosition,
- velocity,
- displayDensity);
- }
- return Math.clamp(dividerPosition, minPosition, maxPosition);
+ final float displayDensity = getDisplayDensity();
+ final boolean isDraggingToFullscreenAllowed =
+ isDraggingToFullscreenAllowed(mProperties.mDividerAttributes);
+ return dividerPositionWithPositionOptions(
+ dividerPosition,
+ minPosition,
+ maxPosition,
+ fullyExpandedPosition,
+ velocity,
+ displayDensity,
+ isDraggingToFullscreenAllowed);
}
/**
- * Returns the divider position given a set of position options. A snap algorithm is used to
- * adjust the ending position to either fully expand one container or move the divider back to
- * the specified min/max ratio depending on the dragging velocity.
+ * Returns the divider position given a set of position options. A snap algorithm can adjust
+ * the ending position to either fully expand one container or move the divider back to
+ * the specified min/max ratio depending on the dragging velocity and if dragging to fullscreen
+ * is allowed.
*/
@VisibleForTesting
- static int dividerPositionWithDraggingToFullscreenAllowed(int dividerPosition, int minPosition,
- int maxPosition, int fullyExpandedPosition, float velocity, float displayDensity) {
- final float minDismissVelocityPxPerSecond =
- MIN_DISMISS_VELOCITY_DP_PER_SECOND * displayDensity;
+ static int dividerPositionWithPositionOptions(int dividerPosition, int minPosition,
+ int maxPosition, int fullyExpandedPosition, float velocity, float displayDensity,
+ boolean isDraggingToFullscreenAllowed) {
+ if (isDraggingToFullscreenAllowed) {
+ final float minDismissVelocityPxPerSecond =
+ MIN_DISMISS_VELOCITY_DP_PER_SECOND * displayDensity;
+ if (dividerPosition < minPosition && velocity < -minDismissVelocityPxPerSecond) {
+ return 0;
+ }
+ if (dividerPosition > maxPosition && velocity > minDismissVelocityPxPerSecond) {
+ return fullyExpandedPosition;
+ }
+ }
final float minFlingVelocityPxPerSecond =
MIN_FLING_VELOCITY_DP_PER_SECOND * displayDensity;
- if (dividerPosition < minPosition && velocity < -minDismissVelocityPxPerSecond) {
- return 0;
+ if (Math.abs(velocity) >= minFlingVelocityPxPerSecond) {
+ return dividerPositionForFling(
+ dividerPosition, minPosition, maxPosition, velocity);
}
- if (dividerPosition > maxPosition && velocity > minDismissVelocityPxPerSecond) {
- return fullyExpandedPosition;
- }
- if (Math.abs(velocity) < minFlingVelocityPxPerSecond) {
- if (dividerPosition >= minPosition && dividerPosition <= maxPosition) {
- return dividerPosition;
- }
- final int[] snapPositions = {0, minPosition, maxPosition, fullyExpandedPosition};
- return snap(dividerPosition, snapPositions);
+ if (dividerPosition >= minPosition && dividerPosition <= maxPosition) {
+ return dividerPosition;
}
- return dividerPositionForFling(
- dividerPosition, minPosition, maxPosition, velocity);
+ return snap(
+ dividerPosition,
+ isDraggingToFullscreenAllowed
+ ? new int[] {0, minPosition, maxPosition, fullyExpandedPosition}
+ : new int[] {minPosition, maxPosition});
}
/**
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
index af3c4dac5d67..4f51815ed05d 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
@@ -660,108 +660,241 @@ public class DividerPresenterTest {
// Divider position is less than minPosition and the velocity is enough to be dismissed
assertEquals(
0, // Closed position
- DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+ DividerPresenter.dividerPositionWithPositionOptions(
10 /* dividerPosition */,
30 /* minPosition */,
900 /* maxPosition */,
1200 /* fullyExpandedPosition */,
-dismissVelocity,
- displayDensity));
+ displayDensity,
+ true /* isDraggingToFullscreenAllowed */));
// Divider position is greater than maxPosition and the velocity is enough to be dismissed
assertEquals(
1200, // Fully expanded position
- DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+ DividerPresenter.dividerPositionWithPositionOptions(
1000 /* dividerPosition */,
30 /* minPosition */,
900 /* maxPosition */,
1200 /* fullyExpandedPosition */,
dismissVelocity,
- displayDensity));
+ displayDensity,
+ true /* isDraggingToFullscreenAllowed */));
// Divider position is returned when the velocity is not fast enough for fling and is in
// between minPosition and maxPosition
assertEquals(
500, // dividerPosition is not snapped
- DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+ DividerPresenter.dividerPositionWithPositionOptions(
500 /* dividerPosition */,
30 /* minPosition */,
900 /* maxPosition */,
1200 /* fullyExpandedPosition */,
nonFlingVelocity,
- displayDensity));
+ displayDensity,
+ true /* isDraggingToFullscreenAllowed */));
// Divider position is snapped when the velocity is not fast enough for fling and larger
// than maxPosition
assertEquals(
900, // Closest position is maxPosition
- DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+ DividerPresenter.dividerPositionWithPositionOptions(
950 /* dividerPosition */,
30 /* minPosition */,
900 /* maxPosition */,
1200 /* fullyExpandedPosition */,
nonFlingVelocity,
- displayDensity));
+ displayDensity,
+ true /* isDraggingToFullscreenAllowed */));
// Divider position is snapped when the velocity is not fast enough for fling and smaller
// than minPosition
assertEquals(
30, // Closest position is minPosition
- DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+ DividerPresenter.dividerPositionWithPositionOptions(
20 /* dividerPosition */,
30 /* minPosition */,
900 /* maxPosition */,
1200 /* fullyExpandedPosition */,
nonFlingVelocity,
- displayDensity));
+ displayDensity,
+ true /* isDraggingToFullscreenAllowed */));
// Divider position is in the closed to maxPosition bounds and the velocity is enough for
// backward fling
assertEquals(
2000, // maxPosition
- DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+ DividerPresenter.dividerPositionWithPositionOptions(
2200 /* dividerPosition */,
1000 /* minPosition */,
2000 /* maxPosition */,
2500 /* fullyExpandedPosition */,
-flingVelocity,
- displayDensity));
+ displayDensity,
+ true /* isDraggingToFullscreenAllowed */));
// Divider position is not in the closed to maxPosition bounds and the velocity is enough
// for backward fling
assertEquals(
1000, // minPosition
- DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+ DividerPresenter.dividerPositionWithPositionOptions(
1200 /* dividerPosition */,
1000 /* minPosition */,
2000 /* maxPosition */,
2500 /* fullyExpandedPosition */,
-flingVelocity,
- displayDensity));
+ displayDensity,
+ true /* isDraggingToFullscreenAllowed */));
// Divider position is in the closed to minPosition bounds and the velocity is enough for
// forward fling
assertEquals(
1000, // minPosition
- DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+ DividerPresenter.dividerPositionWithPositionOptions(
500 /* dividerPosition */,
1000 /* minPosition */,
2000 /* maxPosition */,
2500 /* fullyExpandedPosition */,
flingVelocity,
- displayDensity));
+ displayDensity,
+ true /* isDraggingToFullscreenAllowed */));
// Divider position is not in the closed to minPosition bounds and the velocity is enough
// for forward fling
assertEquals(
2000, // maxPosition
- DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+ DividerPresenter.dividerPositionWithPositionOptions(
1200 /* dividerPosition */,
1000 /* minPosition */,
2000 /* maxPosition */,
2500 /* fullyExpandedPosition */,
flingVelocity,
- displayDensity));
+ displayDensity,
+ true /* isDraggingToFullscreenAllowed */));
+ }
+
+ @Test
+ public void testDividerPositionWithDraggingToFullscreenNotAllowed() {
+ final float displayDensity = 600F;
+ final float nonFlingVelocity = MIN_FLING_VELOCITY_DP_PER_SECOND * displayDensity - 10f;
+ final float flingVelocity = MIN_FLING_VELOCITY_DP_PER_SECOND * displayDensity + 10f;
+
+ // Divider position is returned when the velocity is not fast enough for fling and is in
+ // between minPosition and maxPosition
+ assertEquals(
+ 500, // dividerPosition is not snapped
+ DividerPresenter.dividerPositionWithPositionOptions(
+ 500 /* dividerPosition */,
+ 30 /* minPosition */,
+ 900 /* maxPosition */,
+ 1200 /* fullyExpandedPosition */,
+ nonFlingVelocity,
+ displayDensity,
+ false /* isDraggingToFullscreenAllowed */));
+
+ // Divider position is snapped when the velocity is not fast enough for fling and larger
+ // than maxPosition
+ assertEquals(
+ 900, // Closest position is maxPosition
+ DividerPresenter.dividerPositionWithPositionOptions(
+ 950 /* dividerPosition */,
+ 30 /* minPosition */,
+ 900 /* maxPosition */,
+ 1200 /* fullyExpandedPosition */,
+ nonFlingVelocity,
+ displayDensity,
+ false /* isDraggingToFullscreenAllowed */));
+
+ // Divider position is snapped when the velocity is not fast enough for fling and smaller
+ // than minPosition
+ assertEquals(
+ 30, // Closest position is minPosition
+ DividerPresenter.dividerPositionWithPositionOptions(
+ 20 /* dividerPosition */,
+ 30 /* minPosition */,
+ 900 /* maxPosition */,
+ 1200 /* fullyExpandedPosition */,
+ nonFlingVelocity,
+ displayDensity,
+ false /* isDraggingToFullscreenAllowed */));
+
+ // Divider position is snapped when the velocity is not fast enough for fling and at the
+ // closed position
+ assertEquals(
+ 30, // Closest position is minPosition
+ DividerPresenter.dividerPositionWithPositionOptions(
+ 0 /* dividerPosition */,
+ 30 /* minPosition */,
+ 900 /* maxPosition */,
+ 1200 /* fullyExpandedPosition */,
+ nonFlingVelocity,
+ displayDensity,
+ false /* isDraggingToFullscreenAllowed */));
+
+ // Divider position is snapped when the velocity is not fast enough for fling and at the
+ // fully expanded position
+ assertEquals(
+ 900, // Closest position is maxPosition
+ DividerPresenter.dividerPositionWithPositionOptions(
+ 1200 /* dividerPosition */,
+ 30 /* minPosition */,
+ 900 /* maxPosition */,
+ 1200 /* fullyExpandedPosition */,
+ nonFlingVelocity,
+ displayDensity,
+ false /* isDraggingToFullscreenAllowed */));
+
+ // Divider position is in the closed to maxPosition bounds and the velocity is enough for
+ // backward fling
+ assertEquals(
+ 2000, // maxPosition
+ DividerPresenter.dividerPositionWithPositionOptions(
+ 2200 /* dividerPosition */,
+ 1000 /* minPosition */,
+ 2000 /* maxPosition */,
+ 2500 /* fullyExpandedPosition */,
+ -flingVelocity,
+ displayDensity,
+ false /* isDraggingToFullscreenAllowed */));
+
+ // Divider position is not in the closed to maxPosition bounds and the velocity is enough
+ // for backward fling
+ assertEquals(
+ 1000, // minPosition
+ DividerPresenter.dividerPositionWithPositionOptions(
+ 1200 /* dividerPosition */,
+ 1000 /* minPosition */,
+ 2000 /* maxPosition */,
+ 2500 /* fullyExpandedPosition */,
+ -flingVelocity,
+ displayDensity,
+ false /* isDraggingToFullscreenAllowed */));
+
+ // Divider position is in the closed to minPosition bounds and the velocity is enough for
+ // forward fling
+ assertEquals(
+ 1000, // minPosition
+ DividerPresenter.dividerPositionWithPositionOptions(
+ 500 /* dividerPosition */,
+ 1000 /* minPosition */,
+ 2000 /* maxPosition */,
+ 2500 /* fullyExpandedPosition */,
+ flingVelocity,
+ displayDensity,
+ false /* isDraggingToFullscreenAllowed */));
+
+ // Divider position is not in the closed to minPosition bounds and the velocity is enough
+ // for forward fling
+ assertEquals(
+ 2000, // maxPosition
+ DividerPresenter.dividerPositionWithPositionOptions(
+ 1200 /* dividerPosition */,
+ 1000 /* minPosition */,
+ 2000 /* maxPosition */,
+ 2500 /* fullyExpandedPosition */,
+ flingVelocity,
+ displayDensity,
+ false /* isDraggingToFullscreenAllowed */));
}
private TaskFragmentContainer createMockTaskFragmentContainer(
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
index 0efdbdc9376c..327e2059557c 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
@@ -456,5 +456,7 @@ class BubbleStackViewTest {
override fun isStackExpanded(): Boolean = false
override fun isShowingAsBubbleBar(): Boolean = false
+
+ override fun hideCurrentInputMethod() {}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 4a1da4daf1ad..644907361cd7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -592,11 +592,12 @@ public class BubbleController implements ConfigurationChangeListener,
* Hides the current input method, wherever it may be focused, via InputMethodManagerInternal.
*/
void hideCurrentInputMethod() {
+ mBubblePositioner.setImeVisible(false /* visible */, 0 /* height */);
int displayId = mWindowManager.getDefaultDisplay().getDisplayId();
try {
mBarService.hideCurrentInputMethodForBubbles(displayId);
} catch (RemoteException e) {
- e.printStackTrace();
+ Log.e(TAG, "Failed to hide IME", e);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
index b0d3cc4a5d5c..3d9bf032c1b0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
@@ -29,6 +29,7 @@ interface BubbleExpandedViewManager {
fun setAppBubbleTaskId(key: String, taskId: Int)
fun isStackExpanded(): Boolean
fun isShowingAsBubbleBar(): Boolean
+ fun hideCurrentInputMethod()
companion object {
/**
@@ -73,6 +74,10 @@ interface BubbleExpandedViewManager {
override fun isStackExpanded(): Boolean = controller.isStackExpanded
override fun isShowingAsBubbleBar(): Boolean = controller.isShowingAsBubbleBar
+
+ override fun hideCurrentInputMethod() {
+ controller.hideCurrentInputMethod()
+ }
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index fac9bf6e2a4b..ed904e2ff766 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -2324,7 +2324,6 @@ public class BubbleStackView extends FrameLayout
* not.
*/
void hideCurrentInputMethod() {
- mPositioner.setImeVisible(false, 0);
mManager.hideCurrentInputMethod();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index 271fb9abce6a..a7da07d013c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
@@ -82,6 +82,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
private static final int INVALID_TASK_ID = -1;
private BubbleExpandedViewManager mManager;
+ private BubblePositioner mPositioner;
private boolean mIsOverflow;
private BubbleTaskViewHelper mBubbleTaskViewHelper;
private BubbleBarMenuViewController mMenuViewController;
@@ -160,6 +161,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
boolean isOverflow,
@Nullable BubbleTaskView bubbleTaskView) {
mManager = expandedViewManager;
+ mPositioner = positioner;
mIsOverflow = isOverflow;
if (mIsOverflow) {
@@ -290,15 +292,27 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
}
/**
- * Hides the current modal menu view or collapses the bubble stack.
- * Called from {@link BubbleBarLayerView}
+ * Hides the current modal menu if it is visible
+ * @return {@code true} if menu was visible and is hidden
*/
- public void hideMenuOrCollapse() {
+ public boolean hideMenuIfVisible() {
if (mMenuViewController.isMenuVisible()) {
- mMenuViewController.hideMenu(/* animated = */ true);
- } else {
- mManager.collapseStack();
+ mMenuViewController.hideMenu(true /* animated */);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Hides the IME if it is visible
+ * @return {@code true} if IME was visible
+ */
+ public boolean hideImeIfVisible() {
+ if (mPositioner.isImeVisible()) {
+ mManager.hideCurrentInputMethod();
+ return true;
}
+ return false;
}
/** Updates the bubble shown in the expanded view. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index 123cc7e9d488..1d51d83da8e1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -132,7 +132,7 @@ public class BubbleBarLayerView extends FrameLayout
}
});
- setOnClickListener(view -> hideMenuOrCollapse());
+ setOnClickListener(view -> hideMenuOrImeOrCollapse());
}
@Override
@@ -217,7 +217,7 @@ public class BubbleBarLayerView extends FrameLayout
@Override
public void onBackPressed() {
- hideMenuOrCollapse();
+ hideMenuOrImeOrCollapse();
}
});
@@ -344,15 +344,23 @@ public class BubbleBarLayerView extends FrameLayout
addView(mDismissView);
}
- /** Hides the current modal education/menu view, expanded view or collapses the bubble stack */
- private void hideMenuOrCollapse() {
+ /** Hides the current modal education/menu view, IME or collapses the expanded view */
+ private void hideMenuOrImeOrCollapse() {
if (mEducationViewController.isEducationVisible()) {
mEducationViewController.hideEducation(/* animated = */ true);
- } else if (isExpanded() && mExpandedView != null) {
- mExpandedView.hideMenuOrCollapse();
- } else {
- mBubbleController.collapseStack();
+ return;
+ }
+ if (isExpanded() && mExpandedView != null) {
+ boolean menuHidden = mExpandedView.hideMenuIfVisible();
+ if (menuHidden) {
+ return;
+ }
+ boolean imeHidden = mExpandedView.hideImeIfVisible();
+ if (imeHidden) {
+ return;
+ }
}
+ mBubbleController.collapseStack();
}
/** Updates the expanded view size and position. */
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index efbf8da6d17c..eeb4853afadc 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -52,6 +52,7 @@ interface IMediaRouterService {
// Methods for MediaRouter2
List<MediaRoute2Info> getSystemRoutes(String callerPackageName, boolean isProxyRouter);
RoutingSessionInfo getSystemSessionInfo();
+ boolean showMediaOutputSwitcherWithRouter2(String packageName);
void registerRouter2(IMediaRouter2 router, String packageName);
void unregisterRouter2(IMediaRouter2 router);
@@ -97,5 +98,5 @@ interface IMediaRouterService {
void setSessionVolumeWithManager(IMediaRouter2Manager manager, int requestId,
String sessionId, int volume);
void releaseSessionWithManager(IMediaRouter2Manager manager, int requestId, String sessionId);
- boolean showMediaOutputSwitcher(String packageName);
+ boolean showMediaOutputSwitcherWithProxyRouter(IMediaRouter2Manager manager);
}
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 5672cd54e369..0667bfda7596 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -2666,8 +2666,11 @@ public final class MediaRouter2 {
@Override
public boolean showSystemOutputSwitcher() {
- throw new UnsupportedOperationException(
- "Cannot show system output switcher from a privileged router.");
+ try {
+ return mMediaRouterService.showMediaOutputSwitcherWithProxyRouter(mClient);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
}
/** Gets the list of all discovered routes. */
@@ -3539,7 +3542,7 @@ public final class MediaRouter2 {
public boolean showSystemOutputSwitcher() {
synchronized (mLock) {
try {
- return mMediaRouterService.showMediaOutputSwitcher(mImpl.getPackageName());
+ return mMediaRouterService.showMediaOutputSwitcherWithRouter2(mPackageName);
} catch (RemoteException ex) {
ex.rethrowFromSystemServer();
}
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 38ad560d1bf0..5a4d3ce5661b 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -494,6 +494,26 @@
<item>show_deuteranomaly</item>
</string-array>
+ <!-- Titles for app process limit preference. [CHAR LIMIT=35] -->
+ <string-array name="app_process_limit_entries">
+ <item>Standard limit</item>
+ <item>No background processes</item>
+ <item>At most 1 process</item>
+ <item>At most 2 processes</item>
+ <item>At most 3 processes</item>
+ <item>At most 4 processes</item>
+ </string-array>
+
+ <!-- Values for app process limit preference. -->
+ <string-array name="app_process_limit_values" translatable="false" >
+ <item>-1</item>
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ <item>4</item>
+ </string-array>
+
<!-- USB configuration names for Developer Settings.
This can be overridden by devices with additional USB configurations. -->
<string-array name="usb_configuration_titles">
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 927aa6930ec3..363045ec1d83 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -975,6 +975,9 @@
<string name="immediately_destroy_activities_summary">Destroy every activity as soon as
the user leaves it</string>
+ <!-- UI debug setting: limit number of running background processes [CHAR LIMIT=25] -->
+ <string name="app_process_limit_title">Background process limit</string>
+
<!-- UI debug setting: show all ANRs? [CHAR LIMIT=25] -->
<string name="show_all_anrs">Show background ANRs</string>
<!-- UI debug setting: show all ANRs summary [CHAR LIMIT=100] -->
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index c32938497147..a1f8f1b32f77 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -1,6 +1,6 @@
package com.android.systemui.communal.ui.compose
-import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
@@ -20,20 +20,18 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.composed
import androidx.compose.ui.draw.alpha
+import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.CommunalSwipeDetector
@@ -217,6 +215,7 @@ private fun SceneScope.CommunalScene(
CommunalBackgroundType.DEFAULT -> DefaultBackground(colors = colors)
CommunalBackgroundType.STATIC_GRADIENT -> StaticLinearGradient()
CommunalBackgroundType.ANIMATED -> AnimatedLinearGradient()
+ CommunalBackgroundType.NONE -> BackgroundTopScrim()
}
}
with(content) { Content(modifier = modifier) }
@@ -252,7 +251,8 @@ private fun BoxScope.AnimatedLinearGradient() {
val colors = LocalAndroidColorScheme.current
Box(
Modifier.matchParentSize()
- .animatedGradientBackground(colors = listOf(colors.primary, colors.primaryContainer))
+ .background(colors.primary)
+ .animatedRadialGradientBackground(colors.primary, colors.primaryContainer)
)
BackgroundTopScrim()
}
@@ -265,29 +265,76 @@ private fun BoxScope.BackgroundTopScrim() {
Box(Modifier.matchParentSize().alpha(0.34f).background(scrimOnTopColor))
}
-/** Modifier which sets the background of a composable to an animated gradient */
+/** The duration to use for the gradient background animation. */
+private const val ANIMATION_DURATION_MS = 10_000
+
+/** The offset to use in order to place the center of each gradient offscreen. */
+private val ANIMATION_OFFSCREEN_OFFSET = 128.dp
+
+/** Modifier which creates two radial gradients that animate up and down. */
@Composable
-private fun Modifier.animatedGradientBackground(colors: List<Color>): Modifier = composed {
- var size by remember { mutableStateOf(IntSize.Zero) }
- val transition = rememberInfiniteTransition(label = "scrim background")
- val startOffsetX by
- transition.animateFloat(
- initialValue = -size.width.toFloat(),
- targetValue = size.width.toFloat(),
+fun Modifier.animatedRadialGradientBackground(toColor: Color, fromColor: Color): Modifier {
+ val density = LocalDensity.current
+ val infiniteTransition = rememberInfiniteTransition(label = "radial gradient transition")
+ val centerFraction by
+ infiniteTransition.animateFloat(
+ initialValue = 0f,
+ targetValue = 1f,
animationSpec =
infiniteRepeatable(
- animation = tween(durationMillis = 5_000, easing = LinearEasing),
- repeatMode = RepeatMode.Reverse,
+ animation =
+ tween(
+ durationMillis = ANIMATION_DURATION_MS,
+ easing = CubicBezierEasing(0.33f, 0f, 0.67f, 1f),
+ ),
+ repeatMode = RepeatMode.Reverse
),
- label = "scrim start offset"
+ label = "radial gradient center fraction"
)
- background(
+
+ // Offset to place the center of the gradients offscreen. This is applied to both the
+ // x and y coordinates.
+ val offsetPx = remember(density) { with(density) { ANIMATION_OFFSCREEN_OFFSET.toPx() } }
+
+ return drawBehind {
+ val gradientRadius = (size.width / 2) + offsetPx
+ val totalHeight = size.height + 2 * offsetPx
+
+ val leftCenter =
+ Offset(
+ x = -offsetPx,
+ y = totalHeight * centerFraction - offsetPx,
+ )
+ val rightCenter =
+ Offset(
+ x = offsetPx + size.width,
+ y = totalHeight * (1f - centerFraction) - offsetPx,
+ )
+
+ // Right gradient
+ drawCircle(
brush =
- Brush.linearGradient(
- colors = colors,
- start = Offset(startOffsetX, 0f),
- end = Offset(startOffsetX + size.width.toFloat(), size.height.toFloat()),
- )
+ Brush.radialGradient(
+ colors = listOf(fromColor, toColor),
+ center = rightCenter,
+ radius = gradientRadius
+ ),
+ center = rightCenter,
+ radius = gradientRadius,
+ blendMode = BlendMode.SrcAtop,
)
- .onGloballyPositioned { size = it.size }
+
+ // Left gradient
+ drawCircle(
+ brush =
+ Brush.radialGradient(
+ colors = listOf(fromColor, toColor),
+ center = leftCenter,
+ radius = gradientRadius
+ ),
+ center = leftCenter,
+ radius = gradientRadius,
+ blendMode = BlendMode.SrcAtop,
+ )
+ }
}
diff --git a/packages/SystemUI/res/layout/auth_container_view.xml b/packages/SystemUI/res/layout/auth_container_view.xml
index 2a1ce1fc5ec6..cc5a27d60ec1 100644
--- a/packages/SystemUI/res/layout/auth_container_view.xml
+++ b/packages/SystemUI/res/layout/auth_container_view.xml
@@ -28,9 +28,9 @@
<View
android:id="@+id/panel"
+ style="@style/AuthNonCredentialPanelStyle"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="?androidprv:attr/materialColorSurfaceContainer"
android:elevation="@dimen/biometric_dialog_elevation"/>
<ScrollView
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 1e0adec4e84f..73b7586f1210 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -338,8 +338,11 @@
<item name="android:textSize">16sp</item>
</style>
- <style name="AuthCredentialPanelStyle">
+ <style name="AuthNonCredentialPanelStyle">
<item name="android:background">?androidprv:attr/materialColorSurfaceBright</item>
+ </style>
+
+ <style name="AuthCredentialPanelStyle" parent="AuthNonCredentialPanelStyle">
<item name="android:clickable">true</item>
<item name="android:clipToOutline">true</item>
<item name="android:importantForAccessibility">no</item>
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 177aad9a40fc..430ff0716ee0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -515,7 +515,9 @@ public class AuthContainerView extends LinearLayout
} else {
throw new IllegalStateException("Unknown credential type: " + credentialType);
}
- mCredentialView = factory.inflate(layoutResourceId, null, false);
+ // TODO(b/288175645): Once AuthContainerView is removed, set 0dp in credential view xml
+ // files with the corresponding left/right or top/bottom constraints being set to "parent".
+ mCredentialView = factory.inflate(layoutResourceId, mLayout, false);
// The background is used for detecting taps / cancelling authentication. Since the
// credential view is full-screen and should not be canceled from background taps,
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt
index 8b816db53970..4eaba065e078 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt
@@ -21,4 +21,5 @@ enum class CommunalBackgroundType(val value: Int) {
DEFAULT(0),
STATIC_GRADIENT(1),
ANIMATED(2),
+ NONE(3),
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
index d09b9f68ea60..5d541260b05f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
@@ -38,6 +38,7 @@ constructor(
) : KeyguardQuickAffordanceConfig {
override val key: String = BuiltInKeyguardQuickAffordanceKeys.GLANCEABLE_HUB
+
override fun pickerName(): String = "Glanceable hub"
override val pickerIconResourceId = R.drawable.ic_widgets
@@ -52,6 +53,10 @@ constructor(
}
}
+ override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState {
+ return KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice
+ }
+
override fun onTriggered(
expandable: Expandable?
): KeyguardQuickAffordanceConfig.OnTriggeredResult {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
index 8e985e11732f..37dffd1955d6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
@@ -107,6 +107,7 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
@@ -371,6 +372,7 @@ class MediaDataProcessor(
.onStart { emit(Unit) }
.map { allowMediaRecommendations() }
.distinctUntilChanged()
+ .flowOn(backgroundDispatcher)
// only track the most recent emission
.collectLatest {
allowMediaRecommendations = it
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 63a183d506f8..959300629cd7 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -272,8 +272,10 @@ class UiAutomationManager {
mMainHandler.post(() -> {
try {
final IAccessibilityServiceClient serviceInterface;
+ final UiAutomationService uiAutomationService;
synchronized (mLock) {
serviceInterface = mServiceInterface;
+ uiAutomationService = mUiAutomationService;
if (serviceInterface == null) {
mService = null;
} else {
@@ -283,8 +285,8 @@ class UiAutomationManager {
}
// If the serviceInterface is null, the UiAutomation has been shut down on
// another thread.
- if (serviceInterface != null) {
- mUiAutomationService.addWindowTokensForAllDisplays();
+ if (serviceInterface != null && uiAutomationService != null) {
+ uiAutomationService.addWindowTokensForAllDisplays();
if (mTrace.isA11yTracingEnabledForTypes(
AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
mTrace.logTrace("UiAutomationService.connectServiceUnknownThread",
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 95c0e0ee5272..26aa0535d43e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -169,6 +169,7 @@ final class ActivityManagerConstants extends ContentObserver {
*/
static final String KEY_ENABLE_NEW_OOMADJ = "enable_new_oom_adj";
+ private static final int DEFAULT_MAX_CACHED_PROCESSES = 1024;
private static final boolean DEFAULT_PRIORITIZE_ALARM_BROADCASTS = true;
private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000;
private static final long DEFAULT_FGSERVICE_MIN_REPORT_TIME = 3*1000;
@@ -293,7 +294,12 @@ final class ActivityManagerConstants extends ContentObserver {
private static final long DEFAULT_SERVICE_BACKGROUND_TIMEOUT = DEFAULT_SERVICE_TIMEOUT * 10;
/**
- * Maximum number of phantom processes.
+ * Maximum number of cached processes.
+ */
+ private static final String KEY_MAX_CACHED_PROCESSES = "max_cached_processes";
+
+ /**
+ * Maximum number of cached processes.
*/
private static final String KEY_MAX_PHANTOM_PROCESSES = "max_phantom_processes";
@@ -440,6 +446,9 @@ final class ActivityManagerConstants extends ContentObserver {
volatile int mProcStateDebugSetProcStateDelay = 0;
volatile int mProcStateDebugSetUidStateDelay = 0;
+ // Maximum number of cached processes we will allow.
+ public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
+
// This is the amount of time we allow an app to settle after it goes into the background,
// before we start restricting what it can do.
public long BACKGROUND_SETTLE_TIME = DEFAULT_BACKGROUND_SETTLE_TIME;
@@ -848,6 +857,24 @@ final class ActivityManagerConstants extends ContentObserver {
private ContentResolver mResolver;
private final KeyValueListParser mParser = new KeyValueListParser(',');
+ private int mOverrideMaxCachedProcesses = -1;
+ private final int mCustomizedMaxCachedProcesses;
+
+ // The maximum number of cached processes we will keep around before killing them.
+ // NOTE: this constant is *only* a control to not let us go too crazy with
+ // keeping around processes on devices with large amounts of RAM. For devices that
+ // are tighter on RAM, the out of memory killer is responsible for killing background
+ // processes as RAM is needed, and we should *never* be relying on this limit to
+ // kill them. Also note that this limit only applies to cached background processes;
+ // we have no limit on the number of service, visible, foreground, or other such
+ // processes and the number of those processes does not count against the cached
+ // process limit. This will be initialized in the constructor.
+ public int CUR_MAX_CACHED_PROCESSES;
+
+ // The maximum number of empty app processes we will let sit around. This will be
+ // initialized in the constructor.
+ public int CUR_MAX_EMPTY_PROCESSES;
+
/** @see #mNoKillCachedProcessesUntilBootCompleted */
private static final String KEY_NO_KILL_CACHED_PROCESSES_UNTIL_BOOT_COMPLETED =
"no_kill_cached_processes_until_boot_completed";
@@ -879,6 +906,15 @@ final class ActivityManagerConstants extends ContentObserver {
volatile long mNoKillCachedProcessesPostBootCompletedDurationMillis =
DEFAULT_NO_KILL_CACHED_PROCESSES_POST_BOOT_COMPLETED_DURATION_MILLIS;
+ // The number of empty apps at which we don't consider it necessary to do
+ // memory trimming.
+ public int CUR_TRIM_EMPTY_PROCESSES = computeEmptyProcessLimit(MAX_CACHED_PROCESSES) / 2;
+
+ // The number of cached at which we don't consider it necessary to do
+ // memory trimming.
+ public int CUR_TRIM_CACHED_PROCESSES =
+ (MAX_CACHED_PROCESSES - computeEmptyProcessLimit(MAX_CACHED_PROCESSES)) / 3;
+
/** @see #mNoKillCachedProcessesUntilBootCompleted */
private static final String KEY_MAX_EMPTY_TIME_MILLIS =
"max_empty_time_millis";
@@ -1129,6 +1165,9 @@ final class ActivityManagerConstants extends ContentObserver {
return;
}
switch (name) {
+ case KEY_MAX_CACHED_PROCESSES:
+ updateMaxCachedProcesses();
+ break;
case KEY_DEFAULT_BACKGROUND_ACTIVITY_STARTS_ENABLED:
updateBackgroundActivityStarts();
break;
@@ -1378,7 +1417,16 @@ final class ActivityManagerConstants extends ContentObserver {
context.getResources().getStringArray(
com.android.internal.R.array.config_keep_warming_services))
.map(ComponentName::unflattenFromString).collect(Collectors.toSet()));
-
+ mCustomizedMaxCachedProcesses = context.getResources().getInteger(
+ com.android.internal.R.integer.config_customizedMaxCachedProcesses);
+ CUR_MAX_CACHED_PROCESSES = mCustomizedMaxCachedProcesses;
+ CUR_MAX_EMPTY_PROCESSES = computeEmptyProcessLimit(CUR_MAX_CACHED_PROCESSES);
+
+ final int rawMaxEmptyProcesses = computeEmptyProcessLimit(
+ Integer.min(CUR_MAX_CACHED_PROCESSES, MAX_CACHED_PROCESSES));
+ CUR_TRIM_EMPTY_PROCESSES = rawMaxEmptyProcesses / 2;
+ CUR_TRIM_CACHED_PROCESSES = (Integer.min(CUR_MAX_CACHED_PROCESSES, MAX_CACHED_PROCESSES)
+ - rawMaxEmptyProcesses) / 3;
loadNativeBootDeviceConfigConstants();
mDefaultDisableAppProfilerPssProfiling = context.getResources().getBoolean(
R.bool.config_am_disablePssProfiling);
@@ -1433,6 +1481,19 @@ final class ActivityManagerConstants extends ContentObserver {
DEFAULT_ENABLE_NEW_OOM_ADJ);
}
+ public void setOverrideMaxCachedProcesses(int value) {
+ mOverrideMaxCachedProcesses = value;
+ updateMaxCachedProcesses();
+ }
+
+ public int getOverrideMaxCachedProcesses() {
+ return mOverrideMaxCachedProcesses;
+ }
+
+ public static int computeEmptyProcessLimit(int totalProcessLimit) {
+ return totalProcessLimit/2;
+ }
+
@Override
public void onChange(boolean selfChange, Uri uri) {
if (uri == null) return;
@@ -1933,6 +1994,29 @@ final class ActivityManagerConstants extends ContentObserver {
mSystemServerAutomaticHeapDumpPackageName);
}
+ private void updateMaxCachedProcesses() {
+ String maxCachedProcessesFlag = DeviceConfig.getProperty(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_MAX_CACHED_PROCESSES);
+ try {
+ CUR_MAX_CACHED_PROCESSES = mOverrideMaxCachedProcesses < 0
+ ? (TextUtils.isEmpty(maxCachedProcessesFlag)
+ ? mCustomizedMaxCachedProcesses : Integer.parseInt(maxCachedProcessesFlag))
+ : mOverrideMaxCachedProcesses;
+ } catch (NumberFormatException e) {
+ // Bad flag value from Phenotype, revert to default.
+ Slog.e(TAG,
+ "Unable to parse flag for max_cached_processes: " + maxCachedProcessesFlag, e);
+ CUR_MAX_CACHED_PROCESSES = mCustomizedMaxCachedProcesses;
+ }
+ CUR_MAX_EMPTY_PROCESSES = computeEmptyProcessLimit(CUR_MAX_CACHED_PROCESSES);
+
+ final int rawMaxEmptyProcesses = computeEmptyProcessLimit(
+ Integer.min(CUR_MAX_CACHED_PROCESSES, MAX_CACHED_PROCESSES));
+ CUR_TRIM_EMPTY_PROCESSES = rawMaxEmptyProcesses / 2;
+ CUR_TRIM_CACHED_PROCESSES = (Integer.min(CUR_MAX_CACHED_PROCESSES, MAX_CACHED_PROCESSES)
+ - rawMaxEmptyProcesses) / 3;
+ }
+
private void updateProactiveKillsEnabled() {
PROACTIVE_KILLS_ENABLED = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -2191,6 +2275,8 @@ final class ActivityManagerConstants extends ContentObserver {
pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) "
+ Settings.Global.ACTIVITY_MANAGER_CONSTANTS + ":");
+ pw.print(" "); pw.print(KEY_MAX_CACHED_PROCESSES); pw.print("=");
+ pw.println(MAX_CACHED_PROCESSES);
pw.print(" "); pw.print(KEY_BACKGROUND_SETTLE_TIME); pw.print("=");
pw.println(BACKGROUND_SETTLE_TIME);
pw.print(" "); pw.print(KEY_FGSERVICE_MIN_SHOWN_TIME); pw.print("=");
@@ -2391,6 +2477,14 @@ final class ActivityManagerConstants extends ContentObserver {
pw.print("="); pw.println(MAX_PREVIOUS_TIME);
pw.println();
+ if (mOverrideMaxCachedProcesses >= 0) {
+ pw.print(" mOverrideMaxCachedProcesses="); pw.println(mOverrideMaxCachedProcesses);
+ }
+ pw.print(" mCustomizedMaxCachedProcesses="); pw.println(mCustomizedMaxCachedProcesses);
+ pw.print(" CUR_MAX_CACHED_PROCESSES="); pw.println(CUR_MAX_CACHED_PROCESSES);
+ pw.print(" CUR_MAX_EMPTY_PROCESSES="); pw.println(CUR_MAX_EMPTY_PROCESSES);
+ pw.print(" CUR_TRIM_EMPTY_PROCESSES="); pw.println(CUR_TRIM_EMPTY_PROCESSES);
+ pw.print(" CUR_TRIM_CACHED_PROCESSES="); pw.println(CUR_TRIM_CACHED_PROCESSES);
pw.print(" OOMADJ_UPDATE_QUICK="); pw.println(OOMADJ_UPDATE_QUICK);
pw.print(" ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION=");
pw.println(mEnableWaitForFinishAttachApplication);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e0ec17124318..2a724cdce456 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5846,13 +5846,19 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public void setProcessLimit(int max) {
- // Process limits are deprecated since b/253908413
+ enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT,
+ "setProcessLimit()");
+ synchronized (this) {
+ mConstants.setOverrideMaxCachedProcesses(max);
+ trimApplicationsLocked(true, OOM_ADJ_REASON_PROCESS_END);
+ }
}
@Override
public int getProcessLimit() {
- // Process limits are deprecated since b/253908413
- return Integer.MAX_VALUE;
+ synchronized (this) {
+ return mConstants.getOverrideMaxCachedProcesses();
+ }
}
void importanceTokenDied(ImportanceToken token) {
@@ -10201,6 +10207,19 @@ public class ActivityManagerService extends IActivityManager.Stub
addStartInfoTimestampInternal(key, timestampNs, userId, callingUid);
}
+ @Override
+ public void reportStartInfoViewTimestamps(long renderThreadDrawStartTimeNs,
+ long framePresentedTimeNs) {
+ int callingUid = Binder.getCallingUid();
+ int userId = UserHandle.getUserId(callingUid);
+ addStartInfoTimestampInternal(
+ ApplicationStartInfo.START_TIMESTAMP_INITIAL_RENDERTHREAD_FRAME,
+ renderThreadDrawStartTimeNs, userId, callingUid);
+ addStartInfoTimestampInternal(
+ ApplicationStartInfo.START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE,
+ framePresentedTimeNs, userId, callingUid);
+ }
+
private void addStartInfoTimestampInternal(int key, long timestampNs, int userId, int uid) {
mProcessList.getAppStartInfoTracker().addTimestampToStart(
Settings.getPackageNameForUid(mContext, uid),
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 3ed60fcae5e4..dda48adbf732 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -507,7 +507,8 @@ public class AppProfiler {
final int lruSize = mService.mProcessList.getLruSizeLOSP();
if (mCachedAppFrozenDurations == null
|| mCachedAppFrozenDurations.length < lruSize) {
- mCachedAppFrozenDurations = new long[lruSize];
+ mCachedAppFrozenDurations = new long[Math.max(
+ lruSize, mService.mConstants.CUR_MAX_CACHED_PROCESSES)];
}
mService.mProcessList.forEachLruProcessesLOSP(true, app -> {
if (app.mOptRecord.isFrozen()) {
@@ -1369,13 +1370,18 @@ public class AppProfiler {
// are managing to keep around is less than half the maximum we desire;
// if we are keeping a good number around, we'll let them use whatever
// memory they want.
- final int numCachedAndEmpty = numCached + numEmpty;
- if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
- memFactor = ADJ_MEM_FACTOR_CRITICAL;
- } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {
- memFactor = ADJ_MEM_FACTOR_LOW;
+ if (numCached <= mService.mConstants.CUR_TRIM_CACHED_PROCESSES
+ && numEmpty <= mService.mConstants.CUR_TRIM_EMPTY_PROCESSES) {
+ final int numCachedAndEmpty = numCached + numEmpty;
+ if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
+ memFactor = ADJ_MEM_FACTOR_CRITICAL;
+ } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {
+ memFactor = ADJ_MEM_FACTOR_LOW;
+ } else {
+ memFactor = ADJ_MEM_FACTOR_MODERATE;
+ }
} else {
- memFactor = ADJ_MEM_FACTOR_MODERATE;
+ memFactor = ADJ_MEM_FACTOR_NORMAL;
}
}
// We always allow the memory level to go up (better). We only allow it to go
diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java
index a8227fa8e38b..dc6e2fa39b65 100644
--- a/services/core/java/com/android/server/am/AppStartInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java
@@ -1128,21 +1128,8 @@ public final class AppStartInfoTracker {
// Records are sorted newest to oldest, grab record at index 0.
ApplicationStartInfo startInfo = mInfos.get(0);
- int startupState = startInfo.getStartupState();
- // If startup state is error then don't accept any further timestamps.
- if (startupState == ApplicationStartInfo.STARTUP_STATE_ERROR) {
- if (DEBUG) Slog.d(TAG, "Startup state is error, not accepting new timestamps.");
- return;
- }
-
- // If startup state is first frame drawn then only accept fully drawn timestamp.
- if (startupState == ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN
- && key != ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN) {
- if (DEBUG) {
- Slog.d(TAG, "Startup state is first frame drawn and timestamp is not fully "
- + "drawn, not accepting new timestamps.");
- }
+ if (!isAddTimestampAllowed(startInfo, key, timestampNs)) {
return;
}
@@ -1155,6 +1142,55 @@ public final class AppStartInfoTracker {
}
}
+ private boolean isAddTimestampAllowed(ApplicationStartInfo startInfo, int key,
+ long timestampNs) {
+ int startupState = startInfo.getStartupState();
+
+ // If startup state is error then don't accept any further timestamps.
+ if (startupState == ApplicationStartInfo.STARTUP_STATE_ERROR) {
+ if (DEBUG) Slog.d(TAG, "Startup state is error, not accepting new timestamps.");
+ return false;
+ }
+
+ Map<Integer, Long> timestamps = startInfo.getStartupTimestamps();
+
+ if (startupState == ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN) {
+ switch (key) {
+ case ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN:
+ // Allowed, continue to confirm it's not already added.
+ break;
+ case ApplicationStartInfo.START_TIMESTAMP_INITIAL_RENDERTHREAD_FRAME:
+ Long firstFrameTimeNs = timestamps
+ .get(ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME);
+ if (firstFrameTimeNs == null) {
+ // This should never happen. State can't be first frame drawn if first
+ // frame timestamp was not provided.
+ return false;
+ }
+
+ if (timestampNs > firstFrameTimeNs) {
+ // Initial renderthread frame has to occur before first frame.
+ return false;
+ }
+
+ // Allowed, continue to confirm it's not already added.
+ break;
+ case ApplicationStartInfo.START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE:
+ // Allowed, continue to confirm it's not already added.
+ break;
+ default:
+ return false;
+ }
+ }
+
+ if (timestamps.get(key) != null) {
+ // Timestamp should not occur more than once for a given start.
+ return false;
+ }
+
+ return true;
+ }
+
@GuardedBy("mLock")
void dumpLocked(PrintWriter pw, String prefix, SimpleDateFormat sdf) {
if (mMonitoringModeEnabled) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index d642b02e23ea..29e0f7ae6f01 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -122,12 +122,14 @@ import com.android.server.net.BaseNetworkObserver;
import com.android.server.pm.UserManagerInternal;
import com.android.server.power.optimization.Flags;
import com.android.server.power.stats.AggregatedPowerStatsConfig;
+import com.android.server.power.stats.AudioPowerStatsProcessor;
import com.android.server.power.stats.BatteryExternalStatsWorker;
import com.android.server.power.stats.BatteryStatsDumpHelperImpl;
import com.android.server.power.stats.BatteryStatsImpl;
import com.android.server.power.stats.BatteryUsageStatsProvider;
import com.android.server.power.stats.BluetoothPowerStatsProcessor;
import com.android.server.power.stats.CpuPowerStatsProcessor;
+import com.android.server.power.stats.FlashlightPowerStatsProcessor;
import com.android.server.power.stats.MobileRadioPowerStatsProcessor;
import com.android.server.power.stats.PhoneCallPowerStatsProcessor;
import com.android.server.power.stats.PowerStatsAggregator;
@@ -136,6 +138,7 @@ import com.android.server.power.stats.PowerStatsScheduler;
import com.android.server.power.stats.PowerStatsStore;
import com.android.server.power.stats.PowerStatsUidResolver;
import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
+import com.android.server.power.stats.VideoPowerStatsProcessor;
import com.android.server.power.stats.WifiPowerStatsProcessor;
import com.android.server.power.stats.wakeups.CpuWakeupStats;
@@ -194,7 +197,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
private final BatteryUsageStatsProvider mBatteryUsageStatsProvider;
private final AtomicFile mConfigFile;
private final BatteryStats.BatteryStatsDumpHelper mDumpHelper;
- private final PowerStatsUidResolver mPowerStatsUidResolver;
+ private final PowerStatsUidResolver mPowerStatsUidResolver = new PowerStatsUidResolver();
private final AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
private volatile boolean mMonitorEnabled = true;
@@ -422,7 +425,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub
setPowerStatsThrottlePeriods(batteryStatsConfigBuilder, context.getResources().getString(
com.android.internal.R.string.config_powerStatsThrottlePeriods));
mBatteryStatsConfig = batteryStatsConfigBuilder.build();
- mPowerStatsUidResolver = new PowerStatsUidResolver();
mStats = new BatteryStatsImpl(mBatteryStatsConfig, Clock.SYSTEM_CLOCK, mMonotonicClock,
systemDir, mHandler, this, this, mUserManagerUserInfoProvider, mPowerProfile,
mCpuScalingPolicies, mPowerStatsUidResolver);
@@ -516,6 +518,42 @@ public final class BatteryStatsService extends IBatteryStats.Stub
AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
.setProcessor(
new BluetoothPowerStatsProcessor(mPowerProfile));
+
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_AUDIO)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+ .setProcessor(
+ new AudioPowerStatsProcessor(mPowerProfile,
+ mPowerStatsUidResolver));
+
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_VIDEO)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+ .setProcessor(
+ new VideoPowerStatsProcessor(mPowerProfile,
+ mPowerStatsUidResolver));
+
+ config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)
+ .trackDeviceStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN)
+ .trackUidStates(
+ AggregatedPowerStatsConfig.STATE_POWER,
+ AggregatedPowerStatsConfig.STATE_SCREEN,
+ AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+ .setProcessor(
+ new FlashlightPowerStatsProcessor(mPowerProfile,
+ mPowerStatsUidResolver));
return config;
}
@@ -583,6 +621,24 @@ public final class BatteryStatsService extends IBatteryStats.Stub
BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
Flags.streamlinedConnectivityBatteryStats());
+ mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_AUDIO,
+ Flags.streamlinedMiscBatteryStats());
+ mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+ BatteryConsumer.POWER_COMPONENT_AUDIO,
+ Flags.streamlinedMiscBatteryStats());
+
+ mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_VIDEO,
+ Flags.streamlinedMiscBatteryStats());
+ mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+ BatteryConsumer.POWER_COMPONENT_VIDEO,
+ Flags.streamlinedMiscBatteryStats());
+
+ mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT,
+ Flags.streamlinedMiscBatteryStats());
+ mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+ BatteryConsumer.POWER_COMPONENT_FLASHLIGHT,
+ Flags.streamlinedMiscBatteryStats());
+
mWorker.systemServicesReady();
mStats.systemServicesReady(mContext);
mCpuWakeupStats.systemServicesReady();
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 105e201add52..ab34dd4477fd 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -470,7 +470,7 @@ public class OomAdjuster {
return true;
});
mTmpUidRecords = new ActiveUids(service, false);
- mTmpQueue = new ArrayDeque<ProcessRecord>();
+ mTmpQueue = new ArrayDeque<ProcessRecord>(mConstants.CUR_MAX_CACHED_PROCESSES << 1);
mNumSlots = ((CACHED_APP_MAX_ADJ - CACHED_APP_MIN_ADJ + 1) >> 1)
/ CACHED_APP_IMPORTANCE_LEVELS;
}
@@ -1079,11 +1079,23 @@ public class OomAdjuster {
int curEmptyAdj = CACHED_APP_MIN_ADJ + CACHED_APP_IMPORTANCE_LEVELS;
int nextEmptyAdj = curEmptyAdj + (CACHED_APP_IMPORTANCE_LEVELS * 2);
+ final int emptyProcessLimit = mConstants.CUR_MAX_EMPTY_PROCESSES;
+ final int cachedProcessLimit = mConstants.CUR_MAX_CACHED_PROCESSES
+ - emptyProcessLimit;
// Let's determine how many processes we have running vs.
// how many slots we have for background processes; we may want
// to put multiple processes in a slot of there are enough of
// them.
int numEmptyProcs = numLru - mNumNonCachedProcs - mNumCachedHiddenProcs;
+ if (numEmptyProcs > cachedProcessLimit) {
+ // If there are more empty processes than our limit on cached
+ // processes, then use the cached process limit for the factor.
+ // This ensures that the really old empty processes get pushed
+ // down to the bottom, so if we are running low on memory we will
+ // have a better chance at keeping around more cached processes
+ // instead of a gazillion empty processes.
+ numEmptyProcs = cachedProcessLimit;
+ }
int cachedFactor = (mNumCachedHiddenProcs > 0
? (mNumCachedHiddenProcs + mNumSlots - 1) : 1)
/ mNumSlots;
@@ -1205,6 +1217,17 @@ public class OomAdjuster {
ArrayList<ProcessRecord> lruList = mProcessList.getLruProcessesLOSP();
final int numLru = lruList.size();
+ final boolean doKillExcessiveProcesses = shouldKillExcessiveProcesses(now);
+ if (!doKillExcessiveProcesses) {
+ if (mNextNoKillDebugMessageTime < now) {
+ Slog.d(TAG, "Not killing cached processes"); // STOPSHIP Remove it b/222365734
+ mNextNoKillDebugMessageTime = now + 5000; // Every 5 seconds
+ }
+ }
+ final int emptyProcessLimit = doKillExcessiveProcesses
+ ? mConstants.CUR_MAX_EMPTY_PROCESSES : Integer.MAX_VALUE;
+ final int cachedProcessLimit = doKillExcessiveProcesses
+ ? (mConstants.CUR_MAX_CACHED_PROCESSES - emptyProcessLimit) : Integer.MAX_VALUE;
int lastCachedGroup = 0;
int lastCachedGroupUid = 0;
int numCached = 0;
@@ -1256,14 +1279,36 @@ public class OomAdjuster {
} else {
lastCachedGroupUid = lastCachedGroup = 0;
}
- if (proactiveKillsEnabled) {
+ if ((numCached - numCachedExtraGroup) > cachedProcessLimit) {
+ app.killLocked("cached #" + numCached,
+ "too many cached",
+ ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_TOO_MANY_CACHED,
+ true);
+ } else if (proactiveKillsEnabled) {
lruCachedApp = app;
}
break;
case PROCESS_STATE_CACHED_EMPTY:
- numEmpty++;
- if (proactiveKillsEnabled) {
- lruCachedApp = app;
+ if (numEmpty > mConstants.CUR_TRIM_EMPTY_PROCESSES
+ && app.getLastActivityTime() < oldTime) {
+ app.killLocked("empty for " + ((now
+ - app.getLastActivityTime()) / 1000) + "s",
+ "empty for too long",
+ ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_TRIM_EMPTY,
+ true);
+ } else {
+ numEmpty++;
+ if (numEmpty > emptyProcessLimit) {
+ app.killLocked("empty #" + numEmpty,
+ "too many empty",
+ ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_TOO_MANY_EMPTY,
+ true);
+ } else if (proactiveKillsEnabled) {
+ lruCachedApp = app;
+ }
}
break;
default:
@@ -1304,6 +1349,7 @@ public class OomAdjuster {
}
if (proactiveKillsEnabled // Proactive kills enabled?
+ && doKillExcessiveProcesses // Should kill excessive processes?
&& freeSwapPercent < lowSwapThresholdPercent // Swap below threshold?
&& lruCachedApp != null // If no cached app, let LMKD decide
// If swap is non-decreasing, give reclaim a chance to catch up
@@ -1498,6 +1544,25 @@ public class OomAdjuster {
}
}
+ /**
+ * Return true if we should kill excessive cached/empty processes.
+ */
+ private boolean shouldKillExcessiveProcesses(long nowUptime) {
+ final long lastUserUnlockingUptime = mService.mUserController.getLastUserUnlockingUptime();
+
+ if (lastUserUnlockingUptime == 0) {
+ // No users have been unlocked.
+ return !mConstants.mNoKillCachedProcessesUntilBootCompleted;
+ }
+ final long noKillCachedProcessesPostBootCompletedDurationMillis =
+ mConstants.mNoKillCachedProcessesPostBootCompletedDurationMillis;
+ if ((lastUserUnlockingUptime + noKillCachedProcessesPostBootCompletedDurationMillis)
+ > nowUptime) {
+ return false;
+ }
+ return true;
+ }
+
protected final ComputeOomAdjWindowCallback mTmpComputeOomAdjWindowCallback =
new ComputeOomAdjWindowCallback();
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index c03497e629f0..ba7d3b8c76d2 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -70,6 +70,7 @@ import com.android.internal.util.function.pooled.PooledLambda;
import com.android.media.flags.Flags;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;
+import com.android.server.statusbar.StatusBarManagerInternal;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
@@ -118,6 +119,7 @@ class MediaRouter2ServiceImpl {
private final UserManagerInternal mUserManagerInternal;
private final Object mLock = new Object();
private final AppOpsManager mAppOpsManager;
+ private final StatusBarManagerInternal mStatusBarManagerInternal;
final AtomicInteger mNextRouterOrManagerId = new AtomicInteger(1);
final ActivityManager mActivityManager;
final PowerManager mPowerManager;
@@ -188,6 +190,7 @@ class MediaRouter2ServiceImpl {
mPowerManager = mContext.getSystemService(PowerManager.class);
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
+ mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class);
IntentFilter screenOnOffIntentFilter = new IntentFilter();
screenOnOffIntentFilter.addAction(ACTION_SCREEN_ON);
@@ -260,6 +263,17 @@ class MediaRouter2ServiceImpl {
}
}
+ @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS)
+ public boolean showMediaOutputSwitcherWithRouter2(@NonNull String packageName) {
+ UserHandle userHandle = Binder.getCallingUserHandle();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return showOutputSwitcher(packageName, userHandle);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
public void registerRouter2(@NonNull IMediaRouter2 router, @NonNull String packageName) {
Objects.requireNonNull(router, "router must not be null");
if (TextUtils.isEmpty(packageName)) {
@@ -778,6 +792,31 @@ class MediaRouter2ServiceImpl {
}
}
+ @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS)
+ public boolean showMediaOutputSwitcherWithProxyRouter(
+ @NonNull IMediaRouter2Manager proxyRouter) {
+ Objects.requireNonNull(proxyRouter, "Proxy router must not be null");
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ final IBinder binder = proxyRouter.asBinder();
+ ManagerRecord proxyRouterRecord = mAllManagerRecords.get(binder);
+
+ if (proxyRouterRecord.mTargetPackageName == null) {
+ throw new UnsupportedOperationException(
+ "Only proxy routers can show the Output Switcher.");
+ }
+
+ return showOutputSwitcher(
+ proxyRouterRecord.mTargetPackageName,
+ UserHandle.of(proxyRouterRecord.mUserRecord.mUserId));
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
// End of methods that implement MediaRouter2Manager operations.
// Start of methods that implements operations for both MediaRouter2 and MediaRouter2Manager.
@@ -934,6 +973,19 @@ class MediaRouter2ServiceImpl {
}
}
+ @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS)
+ private boolean showOutputSwitcher(
+ @NonNull String packageName, @NonNull UserHandle userHandle) {
+ if (mActivityManager.getPackageImportance(packageName) > IMPORTANCE_FOREGROUND) {
+ Slog.w(TAG, "showMediaOutputSwitcher only works when called from foreground");
+ return false;
+ }
+ synchronized (mLock) {
+ mStatusBarManagerInternal.showMediaOutputSwitcher(packageName, userHandle);
+ }
+ return true;
+ }
+
// End of methods that implements operations for both MediaRouter2 and MediaRouter2Manager.
public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 192ac6287884..1188a0764051 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -16,7 +16,6 @@
package com.android.server.media;
-import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import android.Manifest;
import android.annotation.NonNull;
@@ -75,7 +74,6 @@ import com.android.media.flags.Flags;
import com.android.server.LocalServices;
import com.android.server.Watchdog;
import com.android.server.pm.UserManagerInternal;
-import com.android.server.statusbar.StatusBarManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -266,32 +264,6 @@ public final class MediaRouterService extends IMediaRouterService.Stub
// Binder call
@Override
- public boolean showMediaOutputSwitcher(String packageName) {
- int uid = Binder.getCallingUid();
- if (!validatePackageName(uid, packageName)) {
- throw new SecurityException("packageName must match the calling identity");
- }
- UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
- final long token = Binder.clearCallingIdentity();
- try {
- if (mContext.getSystemService(ActivityManager.class).getPackageImportance(packageName)
- > IMPORTANCE_FOREGROUND) {
- Slog.w(TAG, "showMediaOutputSwitcher only works when called from foreground");
- return false;
- }
- synchronized (mLock) {
- StatusBarManagerInternal statusBar =
- LocalServices.getService(StatusBarManagerInternal.class);
- statusBar.showMediaOutputSwitcher(packageName, userHandle);
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- return true;
- }
-
- // Binder call
- @Override
public MediaRouterClientState getState(IMediaRouterClient client) {
final long token = Binder.clearCallingIdentity();
try {
@@ -443,6 +415,17 @@ public final class MediaRouterService extends IMediaRouterService.Stub
}
// Binder call
+ @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS)
+ @Override
+ public boolean showMediaOutputSwitcherWithRouter2(@NonNull String packageName) {
+ int uid = Binder.getCallingUid();
+ if (!validatePackageName(uid, packageName)) {
+ throw new SecurityException("packageName must match the calling identity");
+ }
+ return mService2.showMediaOutputSwitcherWithRouter2(packageName);
+ }
+
+ // Binder call
@Override
public void registerRouter2(IMediaRouter2 router, String packageName) {
final int uid = Binder.getCallingUid();
@@ -676,6 +659,13 @@ public final class MediaRouterService extends IMediaRouterService.Stub
mService2.releaseSessionWithManager(manager, requestId, sessionId);
}
+ @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS)
+ @Override
+ public boolean showMediaOutputSwitcherWithProxyRouter(
+ @NonNull IMediaRouter2Manager proxyRouter) {
+ return mService2.showMediaOutputSwitcherWithProxyRouter(proxyRouter);
+ }
+
void restoreBluetoothA2dp() {
try {
boolean a2dpOn;
diff --git a/services/core/java/com/android/server/power/stats/AudioPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/AudioPowerStatsProcessor.java
new file mode 100644
index 000000000000..a48f162321dd
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/AudioPowerStatsProcessor.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+
+import com.android.internal.os.PowerProfile;
+
+public class AudioPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
+ public AudioPowerStatsProcessor(PowerProfile powerProfile,
+ PowerStatsUidResolver uidResolver) {
+ super(BatteryConsumer.POWER_COMPONENT_AUDIO, uidResolver,
+ powerProfile.getAveragePower(PowerProfile.POWER_AUDIO));
+ }
+
+ @Override
+ protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) {
+ return (item.states & BatteryStats.HistoryItem.STATE_AUDIO_ON_FLAG) != 0
+ ? STATE_ON
+ : STATE_OFF;
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index efaa7a8598c0..5bae5a42d484 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -6490,12 +6490,14 @@ public class BatteryStatsImpl extends BatteryStats {
uid = mapUid(uid);
if (mAudioOnNesting == 0) {
mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE_AUDIO_ON_FLAG);
+ HistoryItem.STATE_AUDIO_ON_FLAG, uid, "audio");
mAudioOnTimer.startRunningLocked(elapsedRealtimeMs);
}
mAudioOnNesting++;
- getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
- .noteAudioTurnedOnLocked(elapsedRealtimeMs);
+ if (!mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_AUDIO)) {
+ getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+ .noteAudioTurnedOnLocked(elapsedRealtimeMs);
+ }
}
@GuardedBy("this")
@@ -6506,11 +6508,13 @@ public class BatteryStatsImpl extends BatteryStats {
uid = mapUid(uid);
if (--mAudioOnNesting == 0) {
mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE_AUDIO_ON_FLAG);
+ HistoryItem.STATE_AUDIO_ON_FLAG, uid, "audio");
mAudioOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
- getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
- .noteAudioTurnedOffLocked(elapsedRealtimeMs);
+ if (!mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_AUDIO)) {
+ getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+ .noteAudioTurnedOffLocked(elapsedRealtimeMs);
+ }
}
@GuardedBy("this")
@@ -6518,12 +6522,14 @@ public class BatteryStatsImpl extends BatteryStats {
uid = mapUid(uid);
if (mVideoOnNesting == 0) {
mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_VIDEO_ON_FLAG);
+ HistoryItem.STATE2_VIDEO_ON_FLAG, uid, "video");
mVideoOnTimer.startRunningLocked(elapsedRealtimeMs);
}
mVideoOnNesting++;
- getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
- .noteVideoTurnedOnLocked(elapsedRealtimeMs);
+ if (!mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_VIDEO)) {
+ getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+ .noteVideoTurnedOnLocked(elapsedRealtimeMs);
+ }
}
@GuardedBy("this")
@@ -6534,11 +6540,13 @@ public class BatteryStatsImpl extends BatteryStats {
uid = mapUid(uid);
if (--mVideoOnNesting == 0) {
mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_VIDEO_ON_FLAG);
+ HistoryItem.STATE2_VIDEO_ON_FLAG, uid, "video");
mVideoOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
- getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
- .noteVideoTurnedOffLocked(elapsedRealtimeMs);
+ if (!mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_VIDEO)) {
+ getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+ .noteVideoTurnedOffLocked(elapsedRealtimeMs);
+ }
}
@GuardedBy("this")
@@ -6613,11 +6621,13 @@ public class BatteryStatsImpl extends BatteryStats {
uid = mapUid(uid);
if (mFlashlightOnNesting++ == 0) {
mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_FLASHLIGHT_FLAG);
+ HistoryItem.STATE2_FLASHLIGHT_FLAG, uid, "flashlight");
mFlashlightOnTimer.startRunningLocked(elapsedRealtimeMs);
}
- getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
- .noteFlashlightTurnedOnLocked(elapsedRealtimeMs);
+ if (!mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) {
+ getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+ .noteFlashlightTurnedOnLocked(elapsedRealtimeMs);
+ }
}
@GuardedBy("this")
@@ -6628,11 +6638,13 @@ public class BatteryStatsImpl extends BatteryStats {
uid = mapUid(uid);
if (--mFlashlightOnNesting == 0) {
mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
- HistoryItem.STATE2_FLASHLIGHT_FLAG);
+ HistoryItem.STATE2_FLASHLIGHT_FLAG, uid, "flashlight");
mFlashlightOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
- getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
- .noteFlashlightTurnedOffLocked(elapsedRealtimeMs);
+ if (!mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) {
+ getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+ .noteFlashlightTurnedOffLocked(elapsedRealtimeMs);
+ }
}
@GuardedBy("this")
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index b25239574071..ba6e4a96ddc4 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -97,9 +97,15 @@ public class BatteryUsageStatsProvider {
mContext.getSystemService(SensorManager.class)));
mPowerCalculators.add(new GnssPowerCalculator(mPowerProfile));
mPowerCalculators.add(new CameraPowerCalculator(mPowerProfile));
- mPowerCalculators.add(new FlashlightPowerCalculator(mPowerProfile));
- mPowerCalculators.add(new AudioPowerCalculator(mPowerProfile));
- mPowerCalculators.add(new VideoPowerCalculator(mPowerProfile));
+ if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) {
+ mPowerCalculators.add(new FlashlightPowerCalculator(mPowerProfile));
+ }
+ if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_AUDIO)) {
+ mPowerCalculators.add(new AudioPowerCalculator(mPowerProfile));
+ }
+ if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_VIDEO)) {
+ mPowerCalculators.add(new VideoPowerCalculator(mPowerProfile));
+ }
mPowerCalculators.add(new ScreenPowerCalculator(mPowerProfile));
mPowerCalculators.add(new AmbientDisplayPowerCalculator(mPowerProfile));
mPowerCalculators.add(new IdlePowerCalculator(mPowerProfile));
diff --git a/services/core/java/com/android/server/power/stats/FlashlightPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/FlashlightPowerStatsProcessor.java
new file mode 100644
index 000000000000..f7216c9af9d6
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/FlashlightPowerStatsProcessor.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+
+import com.android.internal.os.PowerProfile;
+
+public class FlashlightPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
+ public FlashlightPowerStatsProcessor(PowerProfile powerProfile,
+ PowerStatsUidResolver uidResolver) {
+ super(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT, uidResolver,
+ powerProfile.getAveragePower(PowerProfile.POWER_FLASHLIGHT));
+ }
+
+ @Override
+ protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) {
+ return (item.states2 & BatteryStats.HistoryItem.STATE2_FLASHLIGHT_FLAG) != 0
+ ? STATE_ON
+ : STATE_OFF;
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/VideoPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/VideoPowerStatsProcessor.java
new file mode 100644
index 000000000000..48dac8a8a970
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/VideoPowerStatsProcessor.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+
+import com.android.internal.os.PowerProfile;
+
+public class VideoPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
+ public VideoPowerStatsProcessor(PowerProfile powerProfile,
+ PowerStatsUidResolver uidResolver) {
+ super(BatteryConsumer.POWER_COMPONENT_VIDEO, uidResolver,
+ powerProfile.getAveragePower(PowerProfile.POWER_VIDEO));
+ }
+
+ @Override
+ protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) {
+ return (item.states2 & BatteryStats.HistoryItem.STATE2_VIDEO_ON_FLAG) != 0
+ ? STATE_ON
+ : STATE_OFF;
+ }
+}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
index 80f1125a4ecf..f70a3ba107e1 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
@@ -150,7 +150,7 @@ public class WallpaperCropper {
Rect landscapeCrop = getCrop(rotatedDisplaySize, bitmapSize, suggestedCrops, rtl);
landscapeCrop = noParallax(landscapeCrop, rotatedDisplaySize, bitmapSize, rtl);
// compute the crop on portrait at the center of the landscape crop
- crop = getAdjustedCrop(landscapeCrop, bitmapSize, displaySize, false, ADD);
+ crop = getAdjustedCrop(landscapeCrop, bitmapSize, displaySize, false, rtl, ADD);
// add some parallax (until the border of the landscape crop without parallax)
if (rtl) {
@@ -160,7 +160,7 @@ public class WallpaperCropper {
}
}
- return getAdjustedCrop(crop, bitmapSize, displaySize, true, ADD);
+ return getAdjustedCrop(crop, bitmapSize, displaySize, true, rtl, ADD);
}
// If any suggested crop is invalid, fallback to case 1
@@ -176,7 +176,7 @@ public class WallpaperCropper {
// Case 2: if the orientation exists in the suggested crops, adjust the suggested crop
Rect suggestedCrop = suggestedCrops.get(orientation);
if (suggestedCrop != null) {
- return getAdjustedCrop(suggestedCrop, bitmapSize, displaySize, true, ADD);
+ return getAdjustedCrop(suggestedCrop, bitmapSize, displaySize, true, rtl, ADD);
}
// Case 3: if we have the 90° rotated orientation in the suggested crops, reuse it and
@@ -188,7 +188,7 @@ public class WallpaperCropper {
if (suggestedCrop != null) {
// only keep the visible part (without parallax)
Rect adjustedCrop = noParallax(suggestedCrop, suggestedDisplaySize, bitmapSize, rtl);
- return getAdjustedCrop(adjustedCrop, bitmapSize, displaySize, false, BALANCE);
+ return getAdjustedCrop(adjustedCrop, bitmapSize, displaySize, false, rtl, BALANCE);
}
// Case 4: if the device is a foldable, if we're looking for a folded orientation and have
@@ -200,13 +200,13 @@ public class WallpaperCropper {
// compute the visible part (without parallax) of the unfolded screen
Rect adjustedCrop = noParallax(suggestedCrop, suggestedDisplaySize, bitmapSize, rtl);
// compute the folded crop, at the center of the crop of the unfolded screen
- Rect res = getAdjustedCrop(adjustedCrop, bitmapSize, displaySize, false, REMOVE);
+ Rect res = getAdjustedCrop(adjustedCrop, bitmapSize, displaySize, false, rtl, REMOVE);
// if we removed some width, add it back to add a parallax effect
if (res.width() < adjustedCrop.width()) {
if (rtl) res.left = Math.min(res.left, adjustedCrop.left);
else res.right = Math.max(res.right, adjustedCrop.right);
// use getAdjustedCrop(parallax=true) to make sure we don't exceed MAX_PARALLAX
- res = getAdjustedCrop(res, bitmapSize, displaySize, true, ADD);
+ res = getAdjustedCrop(res, bitmapSize, displaySize, true, rtl, ADD);
}
return res;
}
@@ -220,7 +220,7 @@ public class WallpaperCropper {
if (suggestedCrop != null) {
// only keep the visible part (without parallax)
Rect adjustedCrop = noParallax(suggestedCrop, suggestedDisplaySize, bitmapSize, rtl);
- return getAdjustedCrop(adjustedCrop, bitmapSize, displaySize, false, ADD);
+ return getAdjustedCrop(adjustedCrop, bitmapSize, displaySize, false, rtl, ADD);
}
// Case 6: for a foldable device, try to combine case 3 + case 4 or 5:
@@ -255,7 +255,7 @@ public class WallpaperCropper {
@VisibleForTesting
static Rect noParallax(Rect crop, Point displaySize, Point bitmapSize, boolean rtl) {
if (displaySize == null) return crop;
- Rect adjustedCrop = getAdjustedCrop(crop, bitmapSize, displaySize, true, ADD);
+ Rect adjustedCrop = getAdjustedCrop(crop, bitmapSize, displaySize, true, rtl, ADD);
// only keep the visible part (without parallax)
float suggestedDisplayRatio = 1f * displaySize.x / displaySize.y;
int widthToRemove = (int) (adjustedCrop.width()
@@ -272,7 +272,7 @@ public class WallpaperCropper {
* Adjust a given crop:
* <ul>
* <li>If parallax = true, make sure we have a parallax of at most {@link #MAX_PARALLAX},
- * by removing content from both sides if necessary.
+ * by removing content from the right (or left if RTL) if necessary.
* <li>If parallax = false, make sure we do not have additional width for parallax. If we
* have additional width for parallax, remove half of the additional width on both sides.
* <li>Make sure the crop fills the screen, i.e. that the width/height ratio of the crop
@@ -282,7 +282,7 @@ public class WallpaperCropper {
*/
@VisibleForTesting
static Rect getAdjustedCrop(Rect crop, Point bitmapSize, Point screenSize,
- boolean parallax, int mode) {
+ boolean parallax, boolean rtl, int mode) {
Rect adjustedCrop = new Rect(crop);
float cropRatio = ((float) crop.width()) / crop.height();
float screenRatio = ((float) screenSize.x) / screenSize.y;
@@ -297,7 +297,8 @@ public class WallpaperCropper {
Rect rotatedCrop = new Rect(newLeft, newTop, newRight, newBottom);
Point rotatedBitmap = new Point(bitmapSize.y, bitmapSize.x);
Point rotatedScreen = new Point(screenSize.y, screenSize.x);
- Rect rect = getAdjustedCrop(rotatedCrop, rotatedBitmap, rotatedScreen, false, mode);
+ Rect rect = getAdjustedCrop(
+ rotatedCrop, rotatedBitmap, rotatedScreen, false, rtl, mode);
int resultLeft = rect.top;
int resultRight = resultLeft + rect.height();
int resultTop = rotatedBitmap.x - rect.right;
@@ -308,8 +309,11 @@ public class WallpaperCropper {
if (additionalWidthForParallax > MAX_PARALLAX) {
int widthToRemove = (int) Math.ceil(
(additionalWidthForParallax - MAX_PARALLAX) * screenRatio * crop.height());
- adjustedCrop.left += widthToRemove / 2;
- adjustedCrop.right -= widthToRemove / 2 + widthToRemove % 2;
+ if (rtl) {
+ adjustedCrop.left += widthToRemove;
+ } else {
+ adjustedCrop.right -= widthToRemove;
+ }
}
} else {
// Note: the third case when MODE == BALANCE, -W + sqrt(W * H * R), is the width to add
diff --git a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
index 3bdcd9b0fdcd..161a8168d993 100644
--- a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
@@ -246,24 +246,32 @@ class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingS
}
override fun setUidMode(uid: Int, deviceId: String, code: Int, mode: Int): Boolean {
+ val appId = UserHandle.getAppId(uid)
+ val userId = UserHandle.getUserId(uid)
+ val appOpName = AppOpsManager.opToPublicName(code)
+
if (
Flags.runtimePermissionAppopsMappingEnabled() && code in runtimeAppOpToPermissionNames
) {
- Slog.w(
- LOG_TAG,
- "Cannot set UID mode for runtime permission app op, " +
- " callingUid = ${Binder.getCallingUid()}, " +
+ val oldMode =
+ service.getState { with(appIdPolicy) { getAppOpMode(appId, userId, appOpName) } }
+ val wouldHaveChanged = oldMode != mode
+ val logMessage =
+ (if (wouldHaveChanged) "Blocked" else "Ignored") +
+ " setUidMode call for runtime permission app op:" +
" uid = $uid," +
" code = ${AppOpsManager.opToName(code)}," +
- " mode = ${AppOpsManager.modeToName(mode)}",
- RuntimeException()
- )
+ " mode = ${AppOpsManager.modeToName(mode)}," +
+ " callingUid = ${Binder.getCallingUid()}," +
+ " oldMode = ${AppOpsManager.modeToName(oldMode)}"
+ if (wouldHaveChanged) {
+ Slog.e(LOG_TAG, logMessage, RuntimeException())
+ } else {
+ Slog.w(LOG_TAG, logMessage)
+ }
return false
}
- val appId = UserHandle.getAppId(uid)
- val userId = UserHandle.getUserId(uid)
- val appOpName = AppOpsManager.opToPublicName(code)
var wasChanged: Boolean
service.mutateState {
wasChanged = with(appIdPolicy) { setAppOpMode(appId, userId, appOpName, mode) }
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
index 1b0a8d2222b9..f1bf86f2f57c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
@@ -211,9 +211,11 @@ public class WallpaperCropperTest {
new Rect(100, 200, bitmapSize.x - 100, bitmapSize.y))) {
for (int mode: ALL_MODES) {
for (boolean parallax: List.of(true, false)) {
- assertThat(WallpaperCropper.getAdjustedCrop(
- crop, bitmapSize, displaySize, parallax, mode))
- .isEqualTo(crop);
+ for (boolean rtl: List.of(true, false)) {
+ assertThat(WallpaperCropper.getAdjustedCrop(
+ crop, bitmapSize, displaySize, parallax, rtl, mode))
+ .isEqualTo(crop);
+ }
}
}
}
@@ -234,8 +236,11 @@ public class WallpaperCropperTest {
Point expectedCropSize = new Point(expectedWidth, 1000);
for (int mode: ALL_MODES) {
assertThat(WallpaperCropper.getAdjustedCrop(
- crop, bitmapSize, displaySize, true, mode))
- .isEqualTo(centerOf(crop, expectedCropSize));
+ crop, bitmapSize, displaySize, true, false, mode))
+ .isEqualTo(leftOf(crop, expectedCropSize));
+ assertThat(WallpaperCropper.getAdjustedCrop(
+ crop, bitmapSize, displaySize, true, true, mode))
+ .isEqualTo(rightOf(crop, expectedCropSize));
}
}
@@ -254,9 +259,11 @@ public class WallpaperCropperTest {
Point bitmapSize = new Point(acceptableWidth, 1000);
Rect crop = new Rect(0, 0, bitmapSize.x, bitmapSize.y);
for (int mode : ALL_MODES) {
- assertThat(WallpaperCropper.getAdjustedCrop(
- crop, bitmapSize, displaySize, true, mode))
- .isEqualTo(crop);
+ for (boolean rtl : List.of(false, true)) {
+ assertThat(WallpaperCropper.getAdjustedCrop(
+ crop, bitmapSize, displaySize, true, rtl, mode))
+ .isEqualTo(crop);
+ }
}
}
}
@@ -286,9 +293,11 @@ public class WallpaperCropperTest {
for (int i = 0; i < crops.size(); i++) {
Rect crop = crops.get(i);
Rect expectedCrop = expectedAdjustedCrops.get(i);
- assertThat(WallpaperCropper.getAdjustedCrop(
- crop, bitmapSize, displaySize, false, WallpaperCropper.ADD))
- .isEqualTo(expectedCrop);
+ for (boolean rtl: List.of(false, true)) {
+ assertThat(WallpaperCropper.getAdjustedCrop(
+ crop, bitmapSize, displaySize, false, rtl, WallpaperCropper.ADD))
+ .isEqualTo(expectedCrop);
+ }
}
}
@@ -309,9 +318,11 @@ public class WallpaperCropperTest {
Point expectedCropSize = new Point(1000, 1000);
for (Rect crop: crops) {
- assertThat(WallpaperCropper.getAdjustedCrop(
- crop, bitmapSize, displaySize, false, WallpaperCropper.REMOVE))
- .isEqualTo(centerOf(crop, expectedCropSize));
+ for (boolean rtl : List.of(false, true)) {
+ assertThat(WallpaperCropper.getAdjustedCrop(
+ crop, bitmapSize, displaySize, false, rtl, WallpaperCropper.REMOVE))
+ .isEqualTo(centerOf(crop, expectedCropSize));
+ }
}
}
@@ -338,14 +349,14 @@ public class WallpaperCropperTest {
Rect crop = crops.get(i);
Rect expected = expectedAdjustedCrops.get(i);
assertThat(WallpaperCropper.getAdjustedCrop(
- crop, bitmapSize, displaySize, false, WallpaperCropper.BALANCE))
+ crop, bitmapSize, displaySize, false, false, WallpaperCropper.BALANCE))
.isEqualTo(expected);
Rect transposedCrop = new Rect(crop.top, crop.left, crop.bottom, crop.right);
Rect expectedTransposed = new Rect(
expected.top, expected.left, expected.bottom, expected.right);
assertThat(WallpaperCropper.getAdjustedCrop(transposedCrop, bitmapSize,
- transposedDisplaySize, false, WallpaperCropper.BALANCE))
+ transposedDisplaySize, false, false, WallpaperCropper.BALANCE))
.isEqualTo(expectedTransposed);
}
}
@@ -376,9 +387,11 @@ public class WallpaperCropperTest {
Point displaySize = displaySizes.get(i);
Point expectedCropSize = expectedCropSizes.get(i);
for (boolean rtl : List.of(false, true)) {
+ Rect expectedCrop = rtl ? rightOf(bitmapRect, expectedCropSize)
+ : leftOf(bitmapRect, expectedCropSize);
assertThat(mWallpaperCropper.getCrop(
displaySize, bitmapSize, suggestedCrops, rtl))
- .isEqualTo(centerOf(bitmapRect, expectedCropSize));
+ .isEqualTo(expectedCrop);
}
}
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
index 851cf4a535a2..976cc18127f0 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
@@ -91,7 +91,7 @@ public class BatteryUsageStatsTest {
final Parcel parcel = Parcel.obtain();
parcel.writeParcelable(outBatteryUsageStats, 0);
- assertThat(parcel.dataSize()).isLessThan(8000);
+ assertThat(parcel.dataSize()).isLessThan(10000);
parcel.setDataPosition(0);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index bc8f65edaa12..09cb464198b5 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -9842,6 +9842,43 @@ public class CarrierConfigManager {
public static final String KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL =
"remove_satellite_plmn_in_manual_network_scan_bool";
+
+ /** @hide */
+ @IntDef({
+ SATELLITE_DATA_SUPPORT_ONLY_RESTRICTED,
+ SATELLITE_DATA_SUPPORT_BANDWIDTH_CONSTRAINED,
+ SATELLITE_DATA_SUPPORT_ALL,
+ })
+ public @interface SATELLITE_DATA_SUPPORT_MODE {}
+
+ /**
+ * Doesn't support unrestricted traffic on satellite network.
+ * @hide
+ */
+ public static final int SATELLITE_DATA_SUPPORT_ONLY_RESTRICTED = 0;
+ /**
+ * Support unrestricted but bandwidth_constrained traffic on satellite network.
+ * @hide
+ */
+ public static final int SATELLITE_DATA_SUPPORT_BANDWIDTH_CONSTRAINED = 1;
+ /**
+ * Support unrestricted satellite network that serves all traffic.
+ * @hide
+ */
+ public static final int SATELLITE_DATA_SUPPORT_ALL = 2;
+ /**
+ * Indicates what kind of traffic an {@link NetworkCapabilities#NET_CAPABILITY_NOT_RESTRICTED}
+ * satellite network can possibly support. The network may subject to further
+ * restrictions such as entitlement etc.
+ * If no data is allowed on satellite network, exclude
+ * {@link ApnSetting#INFRASTRUCTURE_SATELLITE} from APN infrastructure_bitmask, and this
+ * configuration is ignored.
+ * By default it only supports restricted data.
+ * @hide
+ */
+ public static final String KEY_SATELLITE_DATA_SUPPORT_MODE_INT =
+ "satellite_data_support_mode_int";
+
/**
* Determine whether to override roaming Wi-Fi Calling preference when device is connected to
* non-terrestrial network.
@@ -11084,6 +11121,8 @@ public class CarrierConfigManager {
sDefaults.putInt(KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT,
CellSignalStrengthLte.USE_RSRP);
sDefaults.putBoolean(KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL, true);
+ sDefaults.putInt(KEY_SATELLITE_DATA_SUPPORT_MODE_INT,
+ CarrierConfigManager.SATELLITE_DATA_SUPPORT_ONLY_RESTRICTED);
sDefaults.putBoolean(KEY_OVERRIDE_WFC_ROAMING_MODE_WHILE_USING_NTN_BOOL, true);
sDefaults.putInt(KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT, 7);
sDefaults.putBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, false);