• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.internal.widget.remotecompose.core;
17 
18 import static com.android.internal.widget.remotecompose.core.operations.utilities.AnimatedFloatExpression.MUL;
19 
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 
23 import com.android.internal.widget.remotecompose.core.operations.BitmapData;
24 import com.android.internal.widget.remotecompose.core.operations.BitmapFontData;
25 import com.android.internal.widget.remotecompose.core.operations.ClickArea;
26 import com.android.internal.widget.remotecompose.core.operations.ClipPath;
27 import com.android.internal.widget.remotecompose.core.operations.ClipRect;
28 import com.android.internal.widget.remotecompose.core.operations.ColorAttribute;
29 import com.android.internal.widget.remotecompose.core.operations.ColorConstant;
30 import com.android.internal.widget.remotecompose.core.operations.ColorExpression;
31 import com.android.internal.widget.remotecompose.core.operations.ComponentValue;
32 import com.android.internal.widget.remotecompose.core.operations.ConditionalOperations;
33 import com.android.internal.widget.remotecompose.core.operations.DataListFloat;
34 import com.android.internal.widget.remotecompose.core.operations.DataListIds;
35 import com.android.internal.widget.remotecompose.core.operations.DataMapIds;
36 import com.android.internal.widget.remotecompose.core.operations.DataMapLookup;
37 import com.android.internal.widget.remotecompose.core.operations.DebugMessage;
38 import com.android.internal.widget.remotecompose.core.operations.DrawArc;
39 import com.android.internal.widget.remotecompose.core.operations.DrawBitmap;
40 import com.android.internal.widget.remotecompose.core.operations.DrawBitmapFontText;
41 import com.android.internal.widget.remotecompose.core.operations.DrawBitmapInt;
42 import com.android.internal.widget.remotecompose.core.operations.DrawBitmapScaled;
43 import com.android.internal.widget.remotecompose.core.operations.DrawCircle;
44 import com.android.internal.widget.remotecompose.core.operations.DrawContent;
45 import com.android.internal.widget.remotecompose.core.operations.DrawLine;
46 import com.android.internal.widget.remotecompose.core.operations.DrawOval;
47 import com.android.internal.widget.remotecompose.core.operations.DrawPath;
48 import com.android.internal.widget.remotecompose.core.operations.DrawRect;
49 import com.android.internal.widget.remotecompose.core.operations.DrawRoundRect;
50 import com.android.internal.widget.remotecompose.core.operations.DrawSector;
51 import com.android.internal.widget.remotecompose.core.operations.DrawText;
52 import com.android.internal.widget.remotecompose.core.operations.DrawTextAnchored;
53 import com.android.internal.widget.remotecompose.core.operations.DrawTextOnPath;
54 import com.android.internal.widget.remotecompose.core.operations.DrawTweenPath;
55 import com.android.internal.widget.remotecompose.core.operations.FloatConstant;
56 import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
57 import com.android.internal.widget.remotecompose.core.operations.FloatFunctionCall;
58 import com.android.internal.widget.remotecompose.core.operations.FloatFunctionDefine;
59 import com.android.internal.widget.remotecompose.core.operations.HapticFeedback;
60 import com.android.internal.widget.remotecompose.core.operations.Header;
61 import com.android.internal.widget.remotecompose.core.operations.ImageAttribute;
62 import com.android.internal.widget.remotecompose.core.operations.IntegerExpression;
63 import com.android.internal.widget.remotecompose.core.operations.MatrixRestore;
64 import com.android.internal.widget.remotecompose.core.operations.MatrixRotate;
65 import com.android.internal.widget.remotecompose.core.operations.MatrixSave;
66 import com.android.internal.widget.remotecompose.core.operations.MatrixScale;
67 import com.android.internal.widget.remotecompose.core.operations.MatrixSkew;
68 import com.android.internal.widget.remotecompose.core.operations.MatrixTranslate;
69 import com.android.internal.widget.remotecompose.core.operations.NamedVariable;
70 import com.android.internal.widget.remotecompose.core.operations.PaintData;
71 import com.android.internal.widget.remotecompose.core.operations.ParticlesCreate;
72 import com.android.internal.widget.remotecompose.core.operations.ParticlesLoop;
73 import com.android.internal.widget.remotecompose.core.operations.PathAppend;
74 import com.android.internal.widget.remotecompose.core.operations.PathCombine;
75 import com.android.internal.widget.remotecompose.core.operations.PathCreate;
76 import com.android.internal.widget.remotecompose.core.operations.PathData;
77 import com.android.internal.widget.remotecompose.core.operations.PathTween;
78 import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
79 import com.android.internal.widget.remotecompose.core.operations.RootContentDescription;
80 import com.android.internal.widget.remotecompose.core.operations.TextAttribute;
81 import com.android.internal.widget.remotecompose.core.operations.TextData;
82 import com.android.internal.widget.remotecompose.core.operations.TextFromFloat;
83 import com.android.internal.widget.remotecompose.core.operations.TextLength;
84 import com.android.internal.widget.remotecompose.core.operations.TextLookup;
85 import com.android.internal.widget.remotecompose.core.operations.TextLookupInt;
86 import com.android.internal.widget.remotecompose.core.operations.TextMeasure;
87 import com.android.internal.widget.remotecompose.core.operations.TextMerge;
88 import com.android.internal.widget.remotecompose.core.operations.Theme;
89 import com.android.internal.widget.remotecompose.core.operations.TimeAttribute;
90 import com.android.internal.widget.remotecompose.core.operations.TouchExpression;
91 import com.android.internal.widget.remotecompose.core.operations.Utils;
92 import com.android.internal.widget.remotecompose.core.operations.layout.CanvasContent;
93 import com.android.internal.widget.remotecompose.core.operations.layout.CanvasOperations;
94 import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStart;
95 import com.android.internal.widget.remotecompose.core.operations.layout.ContainerEnd;
96 import com.android.internal.widget.remotecompose.core.operations.layout.ImpulseOperation;
97 import com.android.internal.widget.remotecompose.core.operations.layout.ImpulseProcess;
98 import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponentContent;
99 import com.android.internal.widget.remotecompose.core.operations.layout.LoopOperation;
100 import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent;
101 import com.android.internal.widget.remotecompose.core.operations.layout.managers.BoxLayout;
102 import com.android.internal.widget.remotecompose.core.operations.layout.managers.CanvasLayout;
103 import com.android.internal.widget.remotecompose.core.operations.layout.managers.CollapsibleColumnLayout;
104 import com.android.internal.widget.remotecompose.core.operations.layout.managers.CollapsibleRowLayout;
105 import com.android.internal.widget.remotecompose.core.operations.layout.managers.ColumnLayout;
106 import com.android.internal.widget.remotecompose.core.operations.layout.managers.FitBoxLayout;
107 import com.android.internal.widget.remotecompose.core.operations.layout.managers.ImageLayout;
108 import com.android.internal.widget.remotecompose.core.operations.layout.managers.RowLayout;
109 import com.android.internal.widget.remotecompose.core.operations.layout.managers.StateLayout;
110 import com.android.internal.widget.remotecompose.core.operations.layout.managers.TextLayout;
111 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BackgroundModifierOperation;
112 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BorderModifierOperation;
113 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ClipRectModifierOperation;
114 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.GraphicsLayerModifierOperation;
115 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.MarqueeModifierOperation;
116 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.OffsetModifierOperation;
117 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.PaddingModifierOperation;
118 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.RippleModifierOperation;
119 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.RoundedClipRectModifierOperation;
120 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.RunActionOperation;
121 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ScrollModifierOperation;
122 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ZIndexModifierOperation;
123 import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
124 import com.android.internal.widget.remotecompose.core.operations.utilities.NanMap;
125 import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation;
126 import com.android.internal.widget.remotecompose.core.types.BooleanConstant;
127 import com.android.internal.widget.remotecompose.core.types.IntegerConstant;
128 import com.android.internal.widget.remotecompose.core.types.LongConstant;
129 
130 import java.io.File;
131 import java.io.FileInputStream;
132 import java.io.FileOutputStream;
133 import java.io.IOException;
134 import java.io.InputStream;
135 import java.util.ArrayList;
136 import java.util.Arrays;
137 import java.util.HashMap;
138 
139 /** Provides an abstract buffer to encode/decode RemoteCompose operations */
140 public class RemoteComposeBuffer {
141     public static final int EASING_CUBIC_STANDARD = FloatAnimation.CUBIC_STANDARD;
142     public static final int EASING_CUBIC_ACCELERATE = FloatAnimation.CUBIC_ACCELERATE;
143     public static final int EASING_CUBIC_DECELERATE = FloatAnimation.CUBIC_DECELERATE;
144     public static final int EASING_CUBIC_LINEAR = FloatAnimation.CUBIC_LINEAR;
145     public static final int EASING_CUBIC_ANTICIPATE = FloatAnimation.CUBIC_ANTICIPATE;
146     public static final int EASING_CUBIC_OVERSHOOT = FloatAnimation.CUBIC_OVERSHOOT;
147     public static final int EASING_CUBIC_CUSTOM = FloatAnimation.CUBIC_CUSTOM;
148     public static final int EASING_SPLINE_CUSTOM = FloatAnimation.SPLINE_CUSTOM;
149     public static final int EASING_EASE_OUT_BOUNCE = FloatAnimation.EASE_OUT_BOUNCE;
150     public static final int EASING_EASE_OUT_ELASTIC = FloatAnimation.EASE_OUT_ELASTIC;
151     private @NonNull WireBuffer mBuffer = new WireBuffer();
152     @Nullable private Platform mPlatform = null;
153     @NonNull private final RemoteComposeState mRemoteComposeState;
154     private static final boolean DEBUG = false;
155 
156     private int mLastComponentId = 0;
157     private int mGeneratedComponentId = -1;
158 
159     /**
160      * Provides an abstract buffer to encode/decode RemoteCompose operations
161      *
162      * @param remoteComposeState the state used while encoding on the buffer
163      */
RemoteComposeBuffer(@onNull RemoteComposeState remoteComposeState)164     public RemoteComposeBuffer(@NonNull RemoteComposeState remoteComposeState) {
165         this.mRemoteComposeState = remoteComposeState;
166     }
167 
168     /**
169      * Reset the internal buffers
170      *
171      * @param expectedSize provided hint for the main buffer size
172      */
reset(int expectedSize)173     public void reset(int expectedSize) {
174         mBuffer.reset(expectedSize);
175         mRemoteComposeState.reset();
176         mLastComponentId = 0;
177         mGeneratedComponentId = -1;
178     }
179 
getLastComponentId()180     public int getLastComponentId() {
181         return mLastComponentId;
182     }
183 
184     @Nullable
getPlatform()185     public Platform getPlatform() {
186         return mPlatform;
187     }
188 
setPlatform(@onNull Platform platform)189     public void setPlatform(@NonNull Platform platform) {
190         this.mPlatform = platform;
191     }
192 
getBuffer()193     public @NonNull WireBuffer getBuffer() {
194         return mBuffer;
195     }
196 
setBuffer(@onNull WireBuffer buffer)197     public void setBuffer(@NonNull WireBuffer buffer) {
198         this.mBuffer = buffer;
199     }
200 
201     ///////////////////////////////////////////////////////////////////////////////////////////////
202     // Supported operations on the buffer
203     ///////////////////////////////////////////////////////////////////////////////////////////////
204 
205     /** Insert a header */
addHeader(short[] tags, Object[] values)206     public void addHeader(short[] tags, Object[] values) {
207         Header.apply(mBuffer, tags, values);
208     }
209 
210     /**
211      * Insert a header
212      *
213      * @param width the width of the document in pixels
214      * @param height the height of the document in pixels
215      * @param contentDescription content description of the document
216      * @param capabilities bitmask indicating needed capabilities (unused for now)
217      */
header( int width, int height, @Nullable String contentDescription, float density, long capabilities)218     public void header(
219             int width,
220             int height,
221             @Nullable String contentDescription,
222             float density,
223             long capabilities) {
224         Header.apply(mBuffer, width, height, density, capabilities);
225         int contentDescriptionId = 0;
226         if (contentDescription != null) {
227             contentDescriptionId = addText(contentDescription);
228             RootContentDescription.apply(mBuffer, contentDescriptionId);
229         }
230     }
231 
232     /**
233      * Insert a header
234      *
235      * @param width the width of the document in pixels
236      * @param height the height of the document in pixels
237      * @param contentDescription content description of the document
238      * @param capabilities bitmask indicating needed capabilities (unused for now)
239      */
addHeader( int width, int height, @Nullable String contentDescription, float density, long capabilities)240     public void addHeader(
241             int width,
242             int height,
243             @Nullable String contentDescription,
244             float density,
245             long capabilities) {
246         Header.apply(mBuffer, width, height, density, capabilities);
247         int contentDescriptionId = 0;
248         if (contentDescription != null) {
249             contentDescriptionId = addText(contentDescription);
250             RootContentDescription.apply(mBuffer, contentDescriptionId);
251         }
252     }
253 
254     /**
255      * Insert a header
256      *
257      * @param width the width of the document in pixels
258      * @param height the height of the document in pixels
259      * @param contentDescription content description of the document
260      */
header(int width, int height, @Nullable String contentDescription)261     public void header(int width, int height, @Nullable String contentDescription) {
262         header(width, height, contentDescription, 1f, 0);
263     }
264 
265     /**
266      * Insert a bitmap
267      *
268      * @param image an opaque image that we'll add to the buffer
269      * @param imageWidth the width of the image
270      * @param imageHeight the height of the image
271      * @param srcLeft left coordinate of the source area
272      * @param srcTop top coordinate of the source area
273      * @param srcRight right coordinate of the source area
274      * @param srcBottom bottom coordinate of the source area
275      * @param dstLeft left coordinate of the destination area
276      * @param dstTop top coordinate of the destination area
277      * @param dstRight right coordinate of the destination area
278      * @param dstBottom bottom coordinate of the destination area
279      */
drawBitmap( @onNull Object image, int imageWidth, int imageHeight, int srcLeft, int srcTop, int srcRight, int srcBottom, int dstLeft, int dstTop, int dstRight, int dstBottom, @Nullable String contentDescription)280     public void drawBitmap(
281             @NonNull Object image,
282             int imageWidth,
283             int imageHeight,
284             int srcLeft,
285             int srcTop,
286             int srcRight,
287             int srcBottom,
288             int dstLeft,
289             int dstTop,
290             int dstRight,
291             int dstBottom,
292             @Nullable String contentDescription) {
293         int imageId = storeBitmap(image);
294         int contentDescriptionId = 0;
295         if (contentDescription != null) {
296             contentDescriptionId = addText(contentDescription);
297         }
298         DrawBitmapInt.apply(
299                 mBuffer,
300                 imageId,
301                 srcLeft,
302                 srcTop,
303                 srcRight,
304                 srcBottom,
305                 dstLeft,
306                 dstTop,
307                 dstRight,
308                 dstBottom,
309                 contentDescriptionId);
310     }
311 
312     /**
313      * look up map and return the id of the object looked up
314      *
315      * @param mapId the map to access
316      * @param strId the string to lookup
317      * @return id containing the result of the lookup
318      */
mapLookup(int mapId, int strId)319     public int mapLookup(int mapId, int strId) {
320         int hash = mapId + strId * 33;
321         int id = mRemoteComposeState.dataGetId(hash);
322         if (id == -1) {
323             id = mRemoteComposeState.cacheData(hash);
324             DataMapLookup.apply(mBuffer, id, mapId, strId);
325         }
326         return id;
327     }
328 
329     /**
330      * Adds a text string data to the stream and returns its id Will be used to insert string with
331      * bitmaps etc.
332      *
333      * @param text the string to inject in the buffer
334      */
addText(@onNull String text)335     public int addText(@NonNull String text) {
336         int id = mRemoteComposeState.dataGetId(text);
337         if (id == -1) {
338             id = mRemoteComposeState.cacheData(text);
339             TextData.apply(mBuffer, id, text);
340         }
341         return id;
342     }
343 
344     /**
345      * Add a click area to the document
346      *
347      * @param id the id of the click area, reported in the click listener callback
348      * @param contentDescription the content description of that click area (accessibility)
349      * @param left left coordinate of the area bounds
350      * @param top top coordinate of the area bounds
351      * @param right right coordinate of the area bounds
352      * @param bottom bottom coordinate of the area bounds
353      * @param metadata associated metadata, user-provided
354      */
addClickArea( int id, @Nullable String contentDescription, float left, float top, float right, float bottom, @Nullable String metadata)355     public void addClickArea(
356             int id,
357             @Nullable String contentDescription,
358             float left,
359             float top,
360             float right,
361             float bottom,
362             @Nullable String metadata) {
363         int contentDescriptionId = 0;
364         if (contentDescription != null) {
365             contentDescriptionId = addText(contentDescription);
366         }
367         int metadataId = 0;
368         if (metadata != null) {
369             metadataId = addText(metadata);
370         }
371         ClickArea.apply(mBuffer, id, contentDescriptionId, left, top, right, bottom, metadataId);
372     }
373 
374     /**
375      * Sets the way the player handles the content
376      *
377      * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
378      * @param alignment set the alignment of the content (TOP|CENTER|BOTTOM|START|END)
379      * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
380      * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes the LAYOUT modes are:
381      *     - LAYOUT_MATCH_PARENT - LAYOUT_WRAP_CONTENT or adding an horizontal mode and a vertical
382      *     mode: - LAYOUT_HORIZONTAL_MATCH_PARENT - LAYOUT_HORIZONTAL_WRAP_CONTENT -
383      *     LAYOUT_HORIZONTAL_FIXED - LAYOUT_VERTICAL_MATCH_PARENT - LAYOUT_VERTICAL_WRAP_CONTENT -
384      *     LAYOUT_VERTICAL_FIXED The LAYOUT_*_FIXED modes will use the intrinsic document size
385      */
setRootContentBehavior(int scroll, int alignment, int sizing, int mode)386     public void setRootContentBehavior(int scroll, int alignment, int sizing, int mode) {
387         RootContentBehavior.apply(mBuffer, scroll, alignment, sizing, mode);
388     }
389 
390     /**
391      * add Drawing the specified arc, which will be scaled to fit inside the specified oval. <br>
392      * If the start angle is negative or >= 360, the start angle is treated as start angle modulo
393      * 360. <br>
394      * If the sweep angle is >= 360, then the oval is drawn completely. Note that this differs
395      * slightly from SkPath::arcTo, which treats the sweep angle modulo 360. If the sweep angle is
396      * negative, the sweep angle is treated as sweep angle modulo 360 <br>
397      * The arc is drawn clockwise. An angle of 0 degrees correspond to the geometric angle of 0
398      * degrees (3 o'clock on a watch.) <br>
399      *
400      * @param left left coordinate of oval used to define the shape and size of the arc
401      * @param top top coordinate of oval used to define the shape and size of the arc
402      * @param right right coordinate of oval used to define the shape and size of the arc
403      * @param bottom bottom coordinate of oval used to define the shape and size of the arc
404      * @param startAngle Starting angle (in degrees) where the arc begins
405      * @param sweepAngle Sweep angle (in degrees) measured clockwise
406      */
addDrawArc( float left, float top, float right, float bottom, float startAngle, float sweepAngle)407     public void addDrawArc(
408             float left, float top, float right, float bottom, float startAngle, float sweepAngle) {
409         DrawArc.apply(mBuffer, left, top, right, bottom, startAngle, sweepAngle);
410     }
411 
412     /**
413      * add Drawing the specified sector, which will be scaled to fit inside the specified oval. <br>
414      * If the start angle is negative or >= 360, the start angle is treated as start angle modulo
415      * 360. <br>
416      * If the sweep angle is >= 360, then the oval is drawn completely. Note that this differs
417      * slightly from SkPath::arcTo, which treats the sweep angle modulo 360. If the sweep angle is
418      * negative, the sweep angle is treated as sweep angle modulo 360 <br>
419      * The arc is drawn clockwise. An angle of 0 degrees correspond to the geometric angle of 0
420      * degrees (3 o'clock on a watch.) <br>
421      *
422      * @param left left coordinate of oval used to define the shape and size of the arc
423      * @param top top coordinate of oval used to define the shape and size of the arc
424      * @param right right coordinate of oval used to define the shape and size of the arc
425      * @param bottom bottom coordinate of oval used to define the shape and size of the arc
426      * @param startAngle Starting angle (in degrees) where the arc begins
427      * @param sweepAngle Sweep angle (in degrees) measured clockwise
428      */
addDrawSector( float left, float top, float right, float bottom, float startAngle, float sweepAngle)429     public void addDrawSector(
430             float left, float top, float right, float bottom, float startAngle, float sweepAngle) {
431         DrawSector.apply(mBuffer, left, top, right, bottom, startAngle, sweepAngle);
432     }
433 
434     /**
435      * @param image The bitmap to be drawn
436      * @param left left coordinate of rectangle that the bitmap will be to fit into
437      * @param top top coordinate of rectangle that the bitmap will be to fit into
438      * @param right right coordinate of rectangle that the bitmap will be to fit into
439      * @param bottom bottom coordinate of rectangle that the bitmap will be to fit into
440      * @param contentDescription content description of the image
441      */
addDrawBitmap( @onNull Object image, float left, float top, float right, float bottom, @Nullable String contentDescription)442     public void addDrawBitmap(
443             @NonNull Object image,
444             float left,
445             float top,
446             float right,
447             float bottom,
448             @Nullable String contentDescription) {
449         int imageId = storeBitmap(image);
450         addDrawBitmap(imageId, left, top, right, bottom, contentDescription);
451     }
452 
453     /**
454      * @param imageId The Id bitmap to be drawn
455      * @param left left coordinate of rectangle that the bitmap will be to fit into
456      * @param top top coordinate of rectangle that the bitmap will be to fit into
457      * @param right right coordinate of rectangle that the bitmap will be to fit into
458      * @param bottom bottom coordinate of rectangle that the bitmap will be to fit into
459      * @param contentDescription content description of the image
460      */
addDrawBitmap( int imageId, float left, float top, float right, float bottom, @Nullable String contentDescription)461     public void addDrawBitmap(
462             int imageId,
463             float left,
464             float top,
465             float right,
466             float bottom,
467             @Nullable String contentDescription) {
468         int contentDescriptionId = 0;
469         if (contentDescription != null) {
470             contentDescriptionId = addText(contentDescription);
471         }
472         DrawBitmap.apply(mBuffer, imageId, left, top, right, bottom, contentDescriptionId);
473     }
474 
475     /**
476      * @param imageId The Id bitmap to be drawn
477      * @param left left coordinate of rectangle that the bitmap will be to fit into
478      * @param top top coordinate of rectangle that the bitmap will be to fit into
479      * @param contentDescription content description of the image
480      */
addDrawBitmap( int imageId, float left, float top, @Nullable String contentDescription)481     public void addDrawBitmap(
482             int imageId, float left, float top, @Nullable String contentDescription) {
483         int imageWidth = mPlatform.getImageWidth(imageId);
484         int imageHeight = mPlatform.getImageHeight(imageId);
485         int contentDescriptionId = 0;
486         if (contentDescription != null) {
487             contentDescriptionId = addText(contentDescription);
488         }
489         DrawBitmap.apply(
490                 mBuffer, imageId, left, top, imageWidth, imageHeight, contentDescriptionId);
491     }
492 
493     /**
494      * @param image The bitmap to be drawn
495      * @param srcLeft left coordinate in the source bitmap will be to extracted
496      * @param srcTop top coordinate in the source bitmap will be to extracted
497      * @param srcRight right coordinate in the source bitmap will be to extracted
498      * @param srcBottom bottom coordinate in the source bitmap will be to extracted
499      * @param dstLeft left coordinate of rectangle that the bitmap will be to fit into
500      * @param dstTop top coordinate of rectangle that the bitmap will be to fit into
501      * @param dstRight right coordinate of rectangle that the bitmap will be to fit into
502      * @param dstBottom bottom coordinate of rectangle that the bitmap will be to fit into
503      * @param scaleType The type of scaling to allow the image to fit.
504      * @param scaleFactor the scale factor when scale type is FIXED_SCALE (type = 7)
505      * @param contentDescription associate a string with image for accessibility
506      */
drawScaledBitmap( @onNull Object image, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, float dstBottom, int scaleType, float scaleFactor, @Nullable String contentDescription)507     public void drawScaledBitmap(
508             @NonNull Object image,
509             float srcLeft,
510             float srcTop,
511             float srcRight,
512             float srcBottom,
513             float dstLeft,
514             float dstTop,
515             float dstRight,
516             float dstBottom,
517             int scaleType,
518             float scaleFactor,
519             @Nullable String contentDescription) {
520         int imageId = storeBitmap(image);
521         int contentDescriptionId = 0;
522         if (contentDescription != null) {
523             contentDescriptionId = addText(contentDescription);
524         }
525         DrawBitmapScaled.apply(
526                 mBuffer,
527                 imageId,
528                 srcLeft,
529                 srcTop,
530                 srcRight,
531                 srcBottom,
532                 dstLeft,
533                 dstTop,
534                 dstRight,
535                 dstBottom,
536                 scaleType,
537                 scaleFactor,
538                 contentDescriptionId);
539     }
540 
541     /**
542      * Transmit bitmap so the you can use the id form. This is useful if
543      *
544      * @param image drawScaledBitmap
545      * @return id of the image useful with
546      */
addBitmap(@onNull Object image)547     public int addBitmap(@NonNull Object image) {
548         return storeBitmap(image);
549     }
550 
551     /**
552      * Transmit bitmap so the you can use the id form. This is useful if
553      *
554      * @param image drawScaledBitmap
555      * @return id of the image useful with
556      */
addBitmap(@onNull Object image, @NonNull String name)557     public int addBitmap(@NonNull Object image, @NonNull String name) {
558         return storeBitmap(image);
559     }
560 
561     /**
562      * Records a bitmap font and returns an ID.
563      *
564      * @param glyphs The glyphs that define the bitmap font
565      * @return id of the BitmapFont
566      */
addBitmapFont(BitmapFontData.Glyph[] glyphs)567     public int addBitmapFont(BitmapFontData.Glyph[] glyphs) {
568         int id = mRemoteComposeState.nextId();
569         BitmapFontData.apply(mBuffer, id, glyphs);
570         return id;
571     }
572 
573     /**
574      * This defines the name of the bitmap given the id.
575      *
576      * @param id of the Bitmap
577      * @param name Name of the color
578      */
setBitmapName(int id, @NonNull String name)579     public void setBitmapName(int id, @NonNull String name) {
580         NamedVariable.apply(mBuffer, id, NamedVariable.IMAGE_TYPE, name);
581     }
582 
583     /**
584      * @param imageId The id of the bitmap to be drawn
585      * @param srcLeft left coordinate in the source bitmap will be to extracted
586      * @param srcTop top coordinate in the source bitmap will be to extracted
587      * @param srcRight right coordinate in the source bitmap will be to extracted
588      * @param srcBottom bottom coordinate in the source bitmap will be to extracted
589      * @param dstLeft left coordinate of rectangle that the bitmap will be to fit into
590      * @param dstTop top coordinate of rectangle that the bitmap will be to fit into
591      * @param dstRight right coordinate of rectangle that the bitmap will be to fit into
592      * @param dstBottom bottom coordinate of rectangle that the bitmap will be to fit into
593      * @param scaleType The type of scaling to allow the image to fit.
594      * @param scaleFactor the scale factor when scale type is FIXED_SCALE (type = 7)
595      * @param contentDescription associate a string with image for accessibility
596      */
drawScaledBitmap( int imageId, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, float dstBottom, int scaleType, float scaleFactor, @Nullable String contentDescription)597     public void drawScaledBitmap(
598             int imageId,
599             float srcLeft,
600             float srcTop,
601             float srcRight,
602             float srcBottom,
603             float dstLeft,
604             float dstTop,
605             float dstRight,
606             float dstBottom,
607             int scaleType,
608             float scaleFactor,
609             @Nullable String contentDescription) {
610         int contentDescriptionId = 0;
611         if (contentDescription != null) {
612             contentDescriptionId = addText(contentDescription);
613         }
614         DrawBitmapScaled.apply(
615                 mBuffer,
616                 imageId,
617                 srcLeft,
618                 srcTop,
619                 srcRight,
620                 srcBottom,
621                 dstLeft,
622                 dstTop,
623                 dstRight,
624                 dstBottom,
625                 scaleType,
626                 scaleFactor,
627                 contentDescriptionId);
628     }
629 
630     /**
631      * Draw the specified circle using the specified paint. If radius is <= 0, then nothing will be
632      * drawn.
633      *
634      * @param centerX The x-coordinate of the center of the circle to be drawn
635      * @param centerY The y-coordinate of the center of the circle to be drawn
636      * @param radius The radius of the circle to be drawn
637      */
addDrawCircle(float centerX, float centerY, float radius)638     public void addDrawCircle(float centerX, float centerY, float radius) {
639         DrawCircle.apply(mBuffer, centerX, centerY, radius);
640     }
641 
642     /**
643      * Draw a line segment with the specified start and stop x,y coordinates, using the specified
644      * paint.
645      *
646      * @param x1 The x-coordinate of the start point of the line
647      * @param y1 The y-coordinate of the start point of the line
648      * @param x2 The x-coordinate of the end point of the line
649      * @param y2 The y-coordinate of the end point of the line
650      */
addDrawLine(float x1, float y1, float x2, float y2)651     public void addDrawLine(float x1, float y1, float x2, float y2) {
652         DrawLine.apply(mBuffer, x1, y1, x2, y2);
653     }
654 
655     /**
656      * Draw the specified oval using the specified paint.
657      *
658      * @param left left coordinate of oval
659      * @param top top coordinate of oval
660      * @param right right coordinate of oval
661      * @param bottom bottom coordinate of oval
662      */
addDrawOval(float left, float top, float right, float bottom)663     public void addDrawOval(float left, float top, float right, float bottom) {
664         DrawOval.apply(mBuffer, left, top, right, bottom);
665     }
666 
667     /**
668      * Draw the specified path
669      *
670      * <p>Note: path objects are not immutable modifying them and calling this will not change the
671      * drawing
672      *
673      * @param path The path to be drawn
674      */
addDrawPath(@onNull Object path)675     public void addDrawPath(@NonNull Object path) {
676         int id = mRemoteComposeState.dataGetId(path);
677         if (id == -1) { // never been seen before
678             id = addPathData(path);
679         }
680         addDrawPath(id);
681     }
682 
683     /**
684      * interpolate the two paths to produce a 3rd
685      *
686      * @param pid1 the first path
687      * @param pid2 the second path
688      * @param tween path is the path1+(pat2-path1)*tween
689      * @return id of the tweened path
690      */
pathTween(int pid1, int pid2, float tween)691     public int pathTween(int pid1, int pid2, float tween) {
692         int out = mRemoteComposeState.nextId();
693         PathTween.apply(mBuffer, out, pid1, pid2, tween);
694         return out;
695     }
696 
697     /**
698      * Create a path with an initial moveTo
699      *
700      * @param x x coordinate of the moveto
701      * @param y y coordinate of the moveto
702      * @return id of the created path
703      */
pathCreate(float x, float y)704     public int pathCreate(float x, float y) {
705         int out = mRemoteComposeState.nextId();
706         PathCreate.apply(mBuffer, out, x, y);
707         return out;
708     }
709 
710     /**
711      * Append a path to an existing path
712      *
713      * @param id id of the path to append to
714      * @param path the path to append
715      */
pathAppend(int id, float... path)716     public void pathAppend(int id, float... path) {
717         PathAppend.apply(mBuffer, id, path);
718     }
719 
720     /**
721      * Draw the specified path
722      *
723      * @param pathId
724      */
addDrawPath(int pathId)725     public void addDrawPath(int pathId) {
726         DrawPath.apply(mBuffer, pathId);
727     }
728 
729     /**
730      * Draw the specified Rect
731      *
732      * @param left left coordinate of rectangle to be drawn
733      * @param top top coordinate of rectangle to be drawn
734      * @param right right coordinate of rectangle to be drawn
735      * @param bottom bottom coordinate of rectangle to be drawn
736      */
addDrawRect(float left, float top, float right, float bottom)737     public void addDrawRect(float left, float top, float right, float bottom) {
738         DrawRect.apply(mBuffer, left, top, right, bottom);
739     }
740 
741     /**
742      * Draw the specified round-rect
743      *
744      * @param left left coordinate of rectangle to be drawn
745      * @param top left coordinate of rectangle to be drawn
746      * @param right left coordinate of rectangle to be drawn
747      * @param bottom left coordinate of rectangle to be drawn
748      * @param radiusX The x-radius of the oval used to round the corners
749      * @param radiusY The y-radius of the oval used to round the corners
750      */
addDrawRoundRect( float left, float top, float right, float bottom, float radiusX, float radiusY)751     public void addDrawRoundRect(
752             float left, float top, float right, float bottom, float radiusX, float radiusY) {
753         DrawRoundRect.apply(mBuffer, left, top, right, bottom, radiusX, radiusY);
754     }
755 
756     /**
757      * Draw the text, with origin at (x,y) along the specified path.
758      *
759      * @param text The text to be drawn
760      * @param path The path the text should follow for its baseline
761      * @param hOffset The distance along the path to add to the text's starting position
762      * @param vOffset The distance above(-) or below(+) the path to position the text
763      */
addDrawTextOnPath( @onNull String text, @NonNull Object path, float hOffset, float vOffset)764     public void addDrawTextOnPath(
765             @NonNull String text, @NonNull Object path, float hOffset, float vOffset) {
766         int pathId = mRemoteComposeState.dataGetId(path);
767         if (pathId == -1) { // never been seen before
768             pathId = addPathData(path);
769         }
770         int textId = addText(text);
771         DrawTextOnPath.apply(mBuffer, textId, pathId, hOffset, vOffset);
772     }
773 
774     /**
775      * Draw the text, with origin at (x,y) along the specified path.
776      *
777      * @param textId The text to be drawn
778      * @param path The path the text should follow for its baseline
779      * @param hOffset The distance along the path to add to the text's starting position
780      * @param vOffset The distance above(-) or below(+) the path to position the text
781      */
addDrawTextOnPath(int textId, Object path, float hOffset, float vOffset)782     public void addDrawTextOnPath(int textId, Object path, float hOffset, float vOffset) {
783         int pathId = mRemoteComposeState.dataGetId(path);
784         if (pathId == -1) { // never been seen before
785             pathId = addPathData(path);
786         }
787         DrawTextOnPath.apply(mBuffer, textId, pathId, hOffset, vOffset);
788     }
789 
790     /**
791      * Draw the text, with origin at (x,y). The origin is interpreted based on the Align setting in
792      * the paint.
793      *
794      * @param text The text to be drawn
795      * @param start The index of the first character in text to draw
796      * @param end (end - 1) is the index of the last character in text to draw
797      * @param contextStart the context start
798      * @param contextEnd the context end
799      * @param x The x-coordinate of the origin of the text being drawn
800      * @param y The y-coordinate of the baseline of the text being drawn
801      * @param rtl Draw RTTL
802      */
addDrawTextRun( @onNull String text, int start, int end, int contextStart, int contextEnd, float x, float y, boolean rtl)803     public void addDrawTextRun(
804             @NonNull String text,
805             int start,
806             int end,
807             int contextStart,
808             int contextEnd,
809             float x,
810             float y,
811             boolean rtl) {
812         int textId = addText(text);
813         DrawText.apply(mBuffer, textId, start, end, contextStart, contextEnd, x, y, rtl);
814     }
815 
816     /**
817      * Draw the text, with origin at (x,y). The origin is interpreted based on the Align setting in
818      * the paint.
819      *
820      * @param textId The text to be drawn
821      * @param start The index of the first character in text to draw
822      * @param end (end - 1) is the index of the last character in text to draw
823      * @param contextStart the context start
824      * @param contextEnd the context end
825      * @param x The x-coordinate of the origin of the text being drawn
826      * @param y The y-coordinate of the baseline of the text being drawn
827      * @param rtl Draw RTTL
828      */
addDrawTextRun( int textId, int start, int end, int contextStart, int contextEnd, float x, float y, boolean rtl)829     public void addDrawTextRun(
830             int textId,
831             int start,
832             int end,
833             int contextStart,
834             int contextEnd,
835             float x,
836             float y,
837             boolean rtl) {
838         DrawText.apply(mBuffer, textId, start, end, contextStart, contextEnd, x, y, rtl);
839     }
840 
841     /**
842      * Draw the text with a bitmap font, with origin at (x,y). The origin is interpreted based on
843      * the Align setting in the paint.
844      *
845      * @param textId The text to be drawn
846      * @param bitmapFontId The id of the bitmap font to draw with
847      * @param start The index of the first character in text to draw
848      * @param end (end - 1) is the index of the last character in text to draw
849      * @param x The x-coordinate of the origin of the text being drawn
850      * @param y The y-coordinate of the baseline of the text being drawn
851      */
addDrawBitmapFontTextRun( int textId, int bitmapFontId, int start, int end, float x, float y)852     public void addDrawBitmapFontTextRun(
853             int textId, int bitmapFontId, int start, int end, float x, float y) {
854         DrawBitmapFontText.apply(mBuffer, textId, bitmapFontId, start, end, x, y);
855     }
856 
857     /**
858      * Draw a text on canvas at relative to position (x, y), offset panX and panY. <br>
859      * The panning factors (panX, panY) mapped to the resulting bounding box of the text, in such a
860      * way that a panning factor of (0.0, 0.0) would center the text at (x, y)
861      *
862      * <ul>
863      *   <li>Panning of -1.0, -1.0 - the text above & right of x,y.
864      *   <li>Panning of 1.0, 1.0 - the text is below and to the left
865      *   <li>Panning of 1.0, 0.0 - the test is centered & to the right of x,y
866      * </ul>
867      *
868      * <p>Setting panY to NaN results in y being the baseline of the text.
869      *
870      * @param text text to draw
871      * @param x Coordinate of the Anchor
872      * @param y Coordinate of the Anchor
873      * @param panX justifies text -1.0=right, 0.0=center, 1.0=left
874      * @param panY position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline
875      * @param flags 1 = RTL
876      */
drawTextAnchored( @onNull String text, float x, float y, float panX, float panY, int flags)877     public void drawTextAnchored(
878             @NonNull String text, float x, float y, float panX, float panY, int flags) {
879         int textId = addText(text);
880         DrawTextAnchored.apply(mBuffer, textId, x, y, panX, panY, flags);
881     }
882 
883     /**
884      * Add a text and id so that it can be used
885      *
886      * @param text
887      * @return
888      */
createTextId(@onNull String text)889     public int createTextId(@NonNull String text) {
890         return addText(text);
891     }
892 
893     /**
894      * Merge two text (from id's) output one id
895      *
896      * @param id1 left id
897      * @param id2 right id
898      * @return new id that merges the two text
899      */
textMerge(int id1, int id2)900     public int textMerge(int id1, int id2) {
901         int textId = nextId();
902         TextMerge.apply(mBuffer, textId, id1, id2);
903         return textId;
904     }
905 
906     public static final int PAD_AFTER_SPACE = TextFromFloat.PAD_AFTER_SPACE;
907     public static final int PAD_AFTER_NONE = TextFromFloat.PAD_AFTER_NONE;
908     public static final int PAD_AFTER_ZERO = TextFromFloat.PAD_AFTER_ZERO;
909     public static final int PAD_PRE_SPACE = TextFromFloat.PAD_PRE_SPACE;
910     public static final int PAD_PRE_NONE = TextFromFloat.PAD_PRE_NONE;
911     public static final int PAD_PRE_ZERO = TextFromFloat.PAD_PRE_ZERO;
912 
913     /**
914      * Create a TextFromFloat command which creates text from a Float.
915      *
916      * @param value The value to convert
917      * @param digitsBefore the digits before the decimal point
918      * @param digitsAfter the digits after the decimal point
919      * @param flags configure the behaviour using PAD_PRE_* and PAD_AFTER* flags
920      * @return id of the string that can be passed to drawTextAnchored
921      */
createTextFromFloat(float value, short digitsBefore, short digitsAfter, int flags)922     public int createTextFromFloat(float value, short digitsBefore, short digitsAfter, int flags) {
923         String placeHolder =
924                 Utils.floatToString(value)
925                         + "("
926                         + digitsBefore
927                         + ","
928                         + digitsAfter
929                         + ","
930                         + flags
931                         + ")";
932         int id = mRemoteComposeState.dataGetId(placeHolder);
933         if (id == -1) {
934             id = mRemoteComposeState.cacheData(placeHolder);
935             //   TextData.apply(mBuffer, id, text);
936         }
937         TextFromFloat.apply(mBuffer, id, value, digitsBefore, digitsAfter, flags);
938         return id;
939     }
940 
941     /**
942      * Draw a text on canvas at relative to position (x, y), offset panX and panY. <br>
943      * The panning factors (panX, panY) mapped to the resulting bounding box of the text, in such a
944      * way that a panning factor of (0.0, 0.0) would center the text at (x, y)
945      *
946      * <ul>
947      *   <li>Panning of -1.0, -1.0 - the text above & right of x,y.
948      *   <li>Panning of 1.0, 1.0 - the text is below and to the left
949      *   <li>Panning of 1.0, 0.0 - the test is centered & to the right of x,y
950      * </ul>
951      *
952      * <p>Setting panY to NaN results in y being the baseline of the text.
953      *
954      * @param textId text to draw
955      * @param x Coordinate of the Anchor
956      * @param y Coordinate of the Anchor
957      * @param panX justifies text -1.0=right, 0.0=center, 1.0=left
958      * @param panY position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline
959      * @param flags 1 = RTL
960      */
drawTextAnchored(int textId, float x, float y, float panX, float panY, int flags)961     public void drawTextAnchored(int textId, float x, float y, float panX, float panY, int flags) {
962 
963         DrawTextAnchored.apply(mBuffer, textId, x, y, panX, panY, flags);
964     }
965 
966     /**
967      * draw an interpolation between two paths that have the same pattern
968      *
969      * <p>Warning paths objects are not immutable and this is not taken into consideration
970      *
971      * @param path1 The path1 to be drawn between
972      * @param path2 The path2 to be drawn between
973      * @param tween The ratio of path1 and path2 to 0 = all path 1, 1 = all path2
974      * @param start The start of the subrange of paths to draw 0 = start form start 0.5 is half way
975      * @param stop The end of the subrange of paths to draw 1 = end at the end 0.5 is end half way
976      */
addDrawTweenPath( @onNull Object path1, @NonNull Object path2, float tween, float start, float stop)977     public void addDrawTweenPath(
978             @NonNull Object path1, @NonNull Object path2, float tween, float start, float stop) {
979         int path1Id = mRemoteComposeState.dataGetId(path1);
980         if (path1Id == -1) { // never been seen before
981             path1Id = addPathData(path1);
982         }
983         int path2Id = mRemoteComposeState.dataGetId(path2);
984         if (path2Id == -1) { // never been seen before
985             path2Id = addPathData(path2);
986         }
987         addDrawTweenPath(path1Id, path2Id, tween, start, stop);
988     }
989 
990     /**
991      * draw an interpolation between two paths that have the same pattern
992      *
993      * @param path1Id The path1 to be drawn between
994      * @param path2Id The path2 to be drawn between
995      * @param tween The ratio of path1 and path2 to 0 = all path 1, 1 = all path2
996      * @param start The start of the subrange of paths to draw 0 = start form start .5 is 1/2 way
997      * @param stop The end of the subrange of paths to draw 1 = end at the end .5 is end 1/2 way
998      */
addDrawTweenPath(int path1Id, int path2Id, float tween, float start, float stop)999     public void addDrawTweenPath(int path1Id, int path2Id, float tween, float start, float stop) {
1000         DrawTweenPath.apply(mBuffer, path1Id, path2Id, tween, start, stop);
1001     }
1002 
1003     /**
1004      * Add a path object
1005      *
1006      * @param path
1007      * @return the id of the path on the wire
1008      */
addPathData(@onNull Object path)1009     public int addPathData(@NonNull Object path) {
1010         float[] pathData = mPlatform.pathToFloatArray(path);
1011         int id = mRemoteComposeState.cacheData(path);
1012         PathData.apply(mBuffer, id, pathData);
1013         return id;
1014     }
1015 
1016     /**
1017      * Adds a paint Bundle to the doc
1018      *
1019      * @param paint
1020      */
addPaint(@onNull PaintBundle paint)1021     public void addPaint(@NonNull PaintBundle paint) {
1022         PaintData.apply(mBuffer, paint);
1023     }
1024 
1025     ///////////////////////////////////////////////////////////////////////////////////////////////
1026 
1027     /**
1028      * inflate the buffer into a list of operations
1029      *
1030      * @param operations the operations list to add to
1031      */
inflateFromBuffer(@onNull ArrayList<Operation> operations)1032     public void inflateFromBuffer(@NonNull ArrayList<Operation> operations) {
1033         mBuffer.setIndex(0);
1034         while (mBuffer.available()) {
1035             int opId = mBuffer.readByte();
1036             if (DEBUG) {
1037                 Utils.log(">> " + opId);
1038             }
1039             CompanionOperation operation = Operations.map.get(opId);
1040             if (operation == null) {
1041                 throw new RuntimeException("Unknown operation encountered " + opId);
1042             }
1043             operation.read(mBuffer, operations);
1044         }
1045     }
1046 
1047     /**
1048      * Read the next operation from the buffer
1049      *
1050      * @param buffer The buff to read
1051      * @param operations the operations list to add to
1052      */
readNextOperation( @onNull WireBuffer buffer, @NonNull ArrayList<Operation> operations)1053     public static void readNextOperation(
1054             @NonNull WireBuffer buffer, @NonNull ArrayList<Operation> operations) {
1055         int opId = buffer.readByte();
1056         if (DEBUG) {
1057             Utils.log(">> " + opId);
1058         }
1059         CompanionOperation operation = Operations.map.get(opId);
1060         if (operation == null) {
1061             throw new RuntimeException("Unknown operation encountered " + opId);
1062         }
1063         operation.read(buffer, operations);
1064     }
1065 
1066     /**
1067      * copy the current buffer to a new one
1068      *
1069      * @return A new RemoteComposeBuffer
1070      */
1071     @NonNull
copy()1072     RemoteComposeBuffer copy() {
1073         ArrayList<Operation> operations = new ArrayList<>();
1074         inflateFromBuffer(operations);
1075         RemoteComposeBuffer buffer = new RemoteComposeBuffer(mRemoteComposeState);
1076         return copyFromOperations(operations, buffer);
1077     }
1078 
1079     /**
1080      * add a set theme
1081      *
1082      * @param theme The theme to set
1083      */
setTheme(int theme)1084     public void setTheme(int theme) {
1085         Theme.apply(mBuffer, theme);
1086     }
1087 
1088     @NonNull
version()1089     static String version() {
1090         return "v1.0";
1091     }
1092 
1093     /**
1094      * Initialize a buffer from a file
1095      *
1096      * @param path the file path
1097      * @param remoteComposeState the associated state
1098      * @return the RemoteComposeBuffer
1099      * @throws IOException
1100      */
1101     @NonNull
fromFile( @onNull String path, @NonNull RemoteComposeState remoteComposeState)1102     public static RemoteComposeBuffer fromFile(
1103             @NonNull String path, @NonNull RemoteComposeState remoteComposeState)
1104             throws IOException {
1105         RemoteComposeBuffer buffer = new RemoteComposeBuffer(remoteComposeState);
1106         read(new File(path), buffer);
1107         return buffer;
1108     }
1109 
1110     /**
1111      * Create a RemoteComposeBuffer from a file
1112      *
1113      * @param file A file
1114      * @param remoteComposeState The RemoteComposeState
1115      * @return A RemoteComposeBuffer
1116      * @throws IOException if the file cannot be read
1117      */
1118     @NonNull
fromFile( @onNull File file, @NonNull RemoteComposeState remoteComposeState)1119     public RemoteComposeBuffer fromFile(
1120             @NonNull File file, @NonNull RemoteComposeState remoteComposeState) throws IOException {
1121         RemoteComposeBuffer buffer = new RemoteComposeBuffer(remoteComposeState);
1122         read(file, buffer);
1123         return buffer;
1124     }
1125 
1126     /**
1127      * Create a RemoteComposeBuffer from an InputStream
1128      *
1129      * @param inputStream An InputStream
1130      * @param remoteComposeState The RemoteComposeState
1131      * @return A RemoteComposeBuffer
1132      */
1133     @NonNull
fromInputStream( @onNull InputStream inputStream, @NonNull RemoteComposeState remoteComposeState)1134     public static RemoteComposeBuffer fromInputStream(
1135             @NonNull InputStream inputStream, @NonNull RemoteComposeState remoteComposeState) {
1136         RemoteComposeBuffer buffer = new RemoteComposeBuffer(remoteComposeState);
1137         read(inputStream, buffer);
1138         return buffer;
1139     }
1140 
1141     /**
1142      * Create a RemoteComposeBuffer from an array of operations
1143      *
1144      * @param operations An array of operations
1145      * @param buffer A RemoteComposeBuffer
1146      * @return A RemoteComposeBuffer
1147      */
1148     @NonNull
copyFromOperations( @onNull ArrayList<Operation> operations, @NonNull RemoteComposeBuffer buffer)1149     RemoteComposeBuffer copyFromOperations(
1150             @NonNull ArrayList<Operation> operations, @NonNull RemoteComposeBuffer buffer) {
1151 
1152         for (Operation operation : operations) {
1153             operation.write(buffer.mBuffer);
1154         }
1155         return buffer;
1156     }
1157 
1158     /**
1159      * Write the given RemoteComposeBuffer to the given file
1160      *
1161      * @param buffer a RemoteComposeBuffer
1162      * @param file a target file
1163      */
write(@onNull RemoteComposeBuffer buffer, @NonNull File file)1164     public void write(@NonNull RemoteComposeBuffer buffer, @NonNull File file) {
1165         try {
1166             FileOutputStream fd = new FileOutputStream(file);
1167             fd.write(buffer.mBuffer.getBuffer(), 0, buffer.mBuffer.getSize());
1168             fd.flush();
1169             fd.close();
1170         } catch (Exception ex) {
1171             throw new RuntimeException(ex);
1172         }
1173     }
1174 
1175     /**
1176      * Read the content of the file into the buffer
1177      *
1178      * @param file a target file
1179      * @param buffer a RemoteComposeBuffer
1180      * @throws IOException
1181      */
read(@onNull File file, @NonNull RemoteComposeBuffer buffer)1182     static void read(@NonNull File file, @NonNull RemoteComposeBuffer buffer) throws IOException {
1183         FileInputStream fd = new FileInputStream(file);
1184         read(fd, buffer);
1185     }
1186 
1187     /**
1188      * Initialize a buffer from an input stream
1189      *
1190      * @param fd the input stream
1191      * @param buffer a RemoteComposeBuffer
1192      */
read(@onNull InputStream fd, @NonNull RemoteComposeBuffer buffer)1193     public static void read(@NonNull InputStream fd, @NonNull RemoteComposeBuffer buffer) {
1194         try {
1195             byte[] bytes = readAllBytes(fd);
1196             buffer.reset(bytes.length);
1197             System.arraycopy(bytes, 0, buffer.mBuffer.mBuffer, 0, bytes.length);
1198             buffer.mBuffer.mSize = bytes.length;
1199         } catch (Exception e) {
1200             throw new RuntimeException(e);
1201         }
1202     }
1203 
1204     /**
1205      * Load a byte buffer from the input stream
1206      *
1207      * @param is the input stream
1208      * @return a byte buffer containing the input stream content
1209      * @throws IOException
1210      */
readAllBytes(@onNull InputStream is)1211     private static byte[] readAllBytes(@NonNull InputStream is) throws IOException {
1212         byte[] buff = new byte[32 * 1024]; // moderate size buff to start
1213         int red = 0;
1214         while (true) {
1215             int ret = is.read(buff, red, buff.length - red);
1216             if (ret == -1) {
1217                 is.close();
1218                 return Arrays.copyOf(buff, red);
1219             }
1220             red += ret;
1221             if (red == buff.length) {
1222                 buff = Arrays.copyOf(buff, buff.length * 2);
1223             }
1224         }
1225     }
1226 
1227     /**
1228      * add a Pre-concat the current matrix with the specified skew.
1229      *
1230      * @param skewX The amount to skew in X
1231      * @param skewY The amount to skew in Y
1232      */
addMatrixSkew(float skewX, float skewY)1233     public void addMatrixSkew(float skewX, float skewY) {
1234         MatrixSkew.apply(mBuffer, skewX, skewY);
1235     }
1236 
1237     /**
1238      * This call balances a previous call to save(), and is used to remove all modifications to the
1239      * matrix/clip state since the last save call. Do not call restore() more times than save() was
1240      * called.
1241      */
addMatrixRestore()1242     public void addMatrixRestore() {
1243         MatrixRestore.apply(mBuffer);
1244     }
1245 
1246     /**
1247      * Add a saves the current matrix and clip onto a private stack.
1248      *
1249      * <p>Subsequent calls to translate,scale,rotate,skew,concat or clipRect, clipPath will all
1250      * operate as usual, but when the balancing call to restore() is made, those calls will be
1251      * forgotten, and the settings that existed before the save() will be reinstated.
1252      */
addMatrixSave()1253     public void addMatrixSave() {
1254         MatrixSave.apply(mBuffer);
1255     }
1256 
1257     /**
1258      * add a pre-concat the current matrix with the specified rotation.
1259      *
1260      * @param angle The amount to rotate, in degrees
1261      * @param centerX The x-coord for the pivot point (unchanged by the rotation)
1262      * @param centerY The y-coord for the pivot point (unchanged by the rotation)
1263      */
addMatrixRotate(float angle, float centerX, float centerY)1264     public void addMatrixRotate(float angle, float centerX, float centerY) {
1265         MatrixRotate.apply(mBuffer, angle, centerX, centerY);
1266     }
1267 
1268     /**
1269      * add a Pre-concat to the current matrix with the specified translation
1270      *
1271      * @param dx The distance to translate in X
1272      * @param dy The distance to translate in Y
1273      */
addMatrixTranslate(float dx, float dy)1274     public void addMatrixTranslate(float dx, float dy) {
1275         MatrixTranslate.apply(mBuffer, dx, dy);
1276     }
1277 
1278     /**
1279      * Add a pre-concat of the current matrix with the specified scale.
1280      *
1281      * @param scaleX The amount to scale in X
1282      * @param scaleY The amount to scale in Y
1283      */
addMatrixScale(float scaleX, float scaleY)1284     public void addMatrixScale(float scaleX, float scaleY) {
1285         MatrixScale.apply(mBuffer, scaleX, scaleY, Float.NaN, Float.NaN);
1286     }
1287 
1288     /**
1289      * Add a pre-concat of the current matrix with the specified scale.
1290      *
1291      * @param scaleX The amount to scale in X
1292      * @param scaleY The amount to scale in Y
1293      * @param centerX The x-coord for the pivot point (unchanged by the scale)
1294      * @param centerY The y-coord for the pivot point (unchanged by the scale)
1295      */
addMatrixScale(float scaleX, float scaleY, float centerX, float centerY)1296     public void addMatrixScale(float scaleX, float scaleY, float centerX, float centerY) {
1297         MatrixScale.apply(mBuffer, scaleX, scaleY, centerX, centerY);
1298     }
1299 
1300     /**
1301      * sets the clip based on clip id
1302      *
1303      * @param pathId 0 clears the clip
1304      */
addClipPath(int pathId)1305     public void addClipPath(int pathId) {
1306         ClipPath.apply(mBuffer, pathId);
1307     }
1308 
1309     /**
1310      * Sets the clip based on clip rec
1311      *
1312      * @param left left coordinate of the clip rectangle
1313      * @param top top coordinate of the clip rectangle
1314      * @param right right coordinate of the clip rectangle
1315      * @param bottom bottom coordinate of the clip rectangle
1316      */
addClipRect(float left, float top, float right, float bottom)1317     public void addClipRect(float left, float top, float right, float bottom) {
1318         ClipRect.apply(mBuffer, left, top, right, bottom);
1319     }
1320 
1321     /**
1322      * Add a float return a NaN number pointing to that float
1323      *
1324      * @param value the value of the float
1325      * @return the nan id of float
1326      */
addFloat(float value)1327     public float addFloat(float value) {
1328         int id = mRemoteComposeState.cacheFloat(value);
1329         FloatConstant.apply(mBuffer, id, value);
1330         return Utils.asNan(id);
1331     }
1332 
1333     /**
1334      * Reserve a float and returns a NaN number pointing to that float
1335      *
1336      * @return the nan id of float
1337      */
reserveFloatVariable()1338     public float reserveFloatVariable() {
1339         int id = mRemoteComposeState.nextId();
1340         return Utils.asNan(id);
1341     }
1342 
1343     /**
1344      * Add a Integer return an id number pointing to that float.
1345      *
1346      * @param value adds an integer and assigns it an id
1347      * @return the id of the integer to be used
1348      */
addInteger(int value)1349     public int addInteger(int value) {
1350         int id = mRemoteComposeState.cacheInteger(value);
1351         IntegerConstant.apply(mBuffer, id, value);
1352         return id;
1353     }
1354 
1355     /**
1356      * Add a long constant return a id. They can be used as parameters to Custom Attributes.
1357      *
1358      * @param value the value of the long
1359      * @return the id of the command representing long
1360      */
addLong(long value)1361     public int addLong(long value) {
1362         int id = mRemoteComposeState.nextId();
1363         LongConstant.apply(mBuffer, id, value);
1364         return id;
1365     }
1366 
1367     /**
1368      * Add a boolean constant return a id. They can be used as parameters to Custom Attributes.
1369      *
1370      * @param value the value of the boolean
1371      * @return the id
1372      */
addBoolean(boolean value)1373     public int addBoolean(boolean value) {
1374         int id = mRemoteComposeState.nextId();
1375         BooleanConstant.apply(mBuffer, id, value);
1376         return id;
1377     }
1378 
1379     /**
1380      * Add a IntegerId as float ID.
1381      *
1382      * @param id id to be converted
1383      * @return the id wrapped in a NaN
1384      */
asFloatId(int id)1385     public float asFloatId(int id) {
1386         return Utils.asNan(id);
1387     }
1388 
1389     /**
1390      * Add a float that is a computation based on variables
1391      *
1392      * @param value A RPN style float operation i.e. "4, 3, ADD" outputs 7
1393      * @return NaN id of the result of the calculation
1394      */
addAnimatedFloat(@onNull float... value)1395     public float addAnimatedFloat(@NonNull float... value) {
1396         int id = mRemoteComposeState.cacheData(value);
1397         FloatExpression.apply(mBuffer, id, value, null);
1398         return Utils.asNan(id);
1399     }
1400 
1401     /**
1402      * Add a touch handle system
1403      *
1404      * @param id the float NaN id used for the returned position
1405      * @param value the default value
1406      * @param min the minimum value
1407      * @param max the maximum value
1408      * @param velocityId the id for the velocity TODO support in v2
1409      * @param exp The Float Expression
1410      * @param touchMode the touch up handling behaviour
1411      * @param touchSpec the touch up handling parameters
1412      * @param easingSpec the easing parameter TODO support in v2
1413      */
addTouchExpression( float id, float value, float min, float max, float velocityId, int touchEffects, float[] exp, int touchMode, float[] touchSpec, float[] easingSpec)1414     public void addTouchExpression(
1415             float id,
1416             float value,
1417             float min,
1418             float max,
1419             float velocityId,
1420             int touchEffects,
1421             float[] exp,
1422             int touchMode,
1423             float[] touchSpec,
1424             float[] easingSpec) {
1425         TouchExpression.apply(
1426                 mBuffer,
1427                 Utils.idFromNan(id),
1428                 value,
1429                 min,
1430                 max,
1431                 velocityId,
1432                 touchEffects,
1433                 exp,
1434                 touchMode,
1435                 touchSpec,
1436                 easingSpec);
1437     }
1438 
1439     /**
1440      * Add a touch handle system
1441      *
1442      * @param value the default value
1443      * @param min the minimum value
1444      * @param max the maximum value
1445      * @param velocityId the id for the velocity TODO support in v2
1446      * @param exp The Float Expression
1447      * @param touchMode the touch up handling behaviour
1448      * @param touchSpec the touch up handling parameters
1449      * @param easingSpec the easing parameter TODO support in v2
1450      * @return id of the variable to be used controlled by touch handling
1451      */
addTouchExpression( float value, float min, float max, float velocityId, int touchEffects, float[] exp, int touchMode, float[] touchSpec, float[] easingSpec)1452     public float addTouchExpression(
1453             float value,
1454             float min,
1455             float max,
1456             float velocityId,
1457             int touchEffects,
1458             float[] exp,
1459             int touchMode,
1460             float[] touchSpec,
1461             float[] easingSpec) {
1462         float id = Utils.asNan(mRemoteComposeState.nextId());
1463         addTouchExpression(
1464                 id,
1465                 value,
1466                 min,
1467                 max,
1468                 velocityId,
1469                 touchEffects,
1470                 exp,
1471                 touchMode,
1472                 touchSpec,
1473                 easingSpec);
1474         return id;
1475     }
1476 
1477     /**
1478      * Add a float that is a computation based on variables. see packAnimation
1479      *
1480      * @param value A RPN style float operation i.e. "4, 3, ADD" outputs 7
1481      * @param animation Array of floats that represents animation
1482      * @return NaN id of the result of the calculation
1483      */
addAnimatedFloat(@onNull float[] value, @Nullable float[] animation)1484     public float addAnimatedFloat(@NonNull float[] value, @Nullable float[] animation) {
1485         int id = mRemoteComposeState.cacheData(value);
1486         FloatExpression.apply(mBuffer, id, value, animation);
1487         return Utils.asNan(id);
1488     }
1489 
1490     /**
1491      * measure the text and return a measure as a float
1492      *
1493      * @param textId id of the text
1494      * @param mode the mode 0 is the width
1495      * @return
1496      */
textMeasure(int textId, int mode)1497     public float textMeasure(int textId, int mode) {
1498         int id = mRemoteComposeState.cacheData(textId + mode * 31);
1499         TextMeasure.apply(mBuffer, id, textId, mode);
1500         return Utils.asNan(id);
1501     }
1502 
1503     /**
1504      * measure the text and return the length of the text as float
1505      *
1506      * @param textId id of the text
1507      * @return id of a float that is the length
1508      */
textLength(int textId)1509     public float textLength(int textId) {
1510         // The cache id is computed buy merging the two values together
1511         // to create a relatively unique value
1512         int id = mRemoteComposeState.cacheData(textId + (TextLength.id() << 16));
1513         TextLength.apply(mBuffer, id, textId);
1514         return Utils.asNan(id);
1515     }
1516 
1517     /**
1518      * add a float array
1519      *
1520      * @param values
1521      * @return the id of the array, encoded as a float NaN
1522      */
addFloatArray(@onNull float[] values)1523     public float addFloatArray(@NonNull float[] values) {
1524         int id = mRemoteComposeState.cacheData(values, NanMap.TYPE_ARRAY);
1525         DataListFloat.apply(mBuffer, id, values);
1526         return Utils.asNan(id);
1527     }
1528 
1529     /**
1530      * This creates a list of individual floats
1531      *
1532      * @param values array of floats to be individually stored
1533      * @return id of the list
1534      */
addFloatList(@onNull float[] values)1535     public float addFloatList(@NonNull float[] values) {
1536         int[] listId = new int[values.length];
1537         for (int i = 0; i < listId.length; i++) {
1538             listId[i] = mRemoteComposeState.cacheFloat(values[i]);
1539             FloatConstant.apply(mBuffer, listId[i], values[i]);
1540         }
1541         return addList(listId);
1542     }
1543 
1544     /**
1545      * This creates a list of individual floats
1546      *
1547      * @param listId array id to be stored
1548      * @return id of the list
1549      */
addList(@onNull int[] listId)1550     public float addList(@NonNull int[] listId) {
1551         int id = mRemoteComposeState.cacheData(listId, NanMap.TYPE_ARRAY);
1552         DataListIds.apply(mBuffer, id, listId);
1553         return Utils.asNan(id);
1554     }
1555 
1556     /**
1557      * add a float map
1558      *
1559      * @param keys
1560      * @param values
1561      * @return the id of the map, encoded as a float NaN
1562      */
addFloatMap(@onNull String[] keys, @NonNull float[] values)1563     public float addFloatMap(@NonNull String[] keys, @NonNull float[] values) {
1564         int[] listId = new int[values.length];
1565         byte[] type = new byte[values.length];
1566         for (int i = 0; i < listId.length; i++) {
1567             listId[i] = mRemoteComposeState.cacheFloat(values[i]);
1568             FloatConstant.apply(mBuffer, listId[i], values[i]);
1569             type[i] = DataMapIds.TYPE_FLOAT;
1570         }
1571         return addMap(keys, type, listId);
1572     }
1573 
1574     /**
1575      * add an int map
1576      *
1577      * @param keys
1578      * @param listId
1579      * @return the id of the map, encoded as a float NaN
1580      */
addMap(@onNull String[] keys, @Nullable byte[] types, @NonNull int[] listId)1581     public int addMap(@NonNull String[] keys, @Nullable byte[] types, @NonNull int[] listId) {
1582         int id = mRemoteComposeState.cacheData(listId, NanMap.TYPE_ARRAY);
1583         DataMapIds.apply(mBuffer, id, keys, types, listId);
1584         return id;
1585     }
1586 
1587     /**
1588      * This provides access to text in RemoteList
1589      *
1590      * <p>TODO: do we want both a float and an int index version of this method? bbade@ TODO
1591      * for @hoford - add a unit test for this method
1592      *
1593      * @param dataSet
1594      * @param index index as a float variable
1595      * @return
1596      */
textLookup(float dataSet, float index)1597     public int textLookup(float dataSet, float index) {
1598         long hash =
1599                 (((long) Float.floatToRawIntBits(dataSet)) << 32)
1600                         + Float.floatToRawIntBits(
1601                                 index); // TODO: is this the correct ()s? -- bbade@
1602         int id = mRemoteComposeState.cacheData(hash);
1603         TextLookup.apply(mBuffer, id, Utils.idFromNan(dataSet), index);
1604         return id;
1605     }
1606 
1607     /**
1608      * This provides access to text in RemoteList
1609      *
1610      * <p>TODO for hoford - add a unit test for this method
1611      *
1612      * @param dataSet
1613      * @param index index as an int variable
1614      * @return
1615      */
textLookup(float dataSet, int index)1616     public int textLookup(float dataSet, int index) {
1617         long hash =
1618                 (((long) Float.floatToRawIntBits(dataSet)) << 32)
1619                         + Float.floatToRawIntBits(index); // TODO: is this the correct ()s?
1620         int id = mRemoteComposeState.cacheData(hash);
1621         TextLookupInt.apply(mBuffer, id, Utils.idFromNan(dataSet), index);
1622         return id;
1623     }
1624 
1625     /**
1626      * Add and integer expression
1627      *
1628      * @param mask defines which elements are operators or variables
1629      * @param value array of values to calculate maximum 32
1630      * @return the id as an integer
1631      */
addIntegerExpression(int mask, @NonNull int[] value)1632     public int addIntegerExpression(int mask, @NonNull int[] value) {
1633         int id = mRemoteComposeState.cacheData(value);
1634         IntegerExpression.apply(mBuffer, id, mask, value);
1635         return id;
1636     }
1637 
1638     /**
1639      * Add a simple color
1640      *
1641      * @param color the RGB color value
1642      * @return id that represents that color
1643      */
addColor(int color)1644     public int addColor(int color) {
1645         ColorConstant c = new ColorConstant(0, color);
1646         short id = (short) mRemoteComposeState.cacheData(c);
1647         c.mColorId = id;
1648         c.write(mBuffer);
1649         return id;
1650     }
1651 
1652     /**
1653      * Add a color that represents the tween between two colors
1654      *
1655      * @param color1 the ARGB value of the first color
1656      * @param color2 the ARGB value of the second color
1657      * @param tween the interpolation bet
1658      * @return id of the color (color ids are short)
1659      */
addColorExpression(int color1, int color2, float tween)1660     public short addColorExpression(int color1, int color2, float tween) {
1661         ColorExpression c = new ColorExpression(0, 0, color1, color2, tween);
1662         short id = (short) mRemoteComposeState.cacheData(c);
1663         c.mId = id;
1664         c.write(mBuffer);
1665         return id;
1666     }
1667 
1668     /**
1669      * Add a color that represents the tween between two colors where color1 is the id of a color
1670      *
1671      * @param color1 id of color
1672      * @param color2 rgb color value
1673      * @param tween the tween between color1 and color2 (1 = color2)
1674      * @return id of the color (color ids are short)
1675      */
addColorExpression(short color1, int color2, float tween)1676     public short addColorExpression(short color1, int color2, float tween) {
1677         ColorExpression c = new ColorExpression(0, 1, color1, color2, tween);
1678         short id = (short) mRemoteComposeState.cacheData(c);
1679         c.mId = id;
1680         c.write(mBuffer);
1681         return id;
1682     }
1683 
1684     /**
1685      * Add a color that represents the tween between two colors where color2 is the id of a color
1686      *
1687      * @param color1 the ARGB value of the first color
1688      * @param color2 id of the second color
1689      * @param tween the tween between color1 and color2 (1 = color2)
1690      * @return id of the color (color ids are short)
1691      */
addColorExpression(int color1, short color2, float tween)1692     public short addColorExpression(int color1, short color2, float tween) {
1693         ColorExpression c = new ColorExpression(0, 2, color1, color2, tween);
1694         short id = (short) mRemoteComposeState.cacheData(c);
1695         c.mId = id;
1696         c.write(mBuffer);
1697         return id;
1698     }
1699 
1700     /**
1701      * Add a color that represents the tween between two colors where color1 & color2 are the ids of
1702      * colors
1703      *
1704      * @param color1 id of the first color
1705      * @param color2 id of the second color
1706      * @param tween the tween between color1 and color2 (1 = color2)
1707      * @return id of the color (color ids are short)
1708      */
addColorExpression(short color1, short color2, float tween)1709     public short addColorExpression(short color1, short color2, float tween) {
1710         ColorExpression c = new ColorExpression(0, 3, color1, color2, tween);
1711         short id = (short) mRemoteComposeState.cacheData(c);
1712         c.mId = id;
1713         c.write(mBuffer);
1714         return id;
1715     }
1716 
1717     /**
1718      * Color calculated by Hue saturation and value. (as floats they can be variables used to create
1719      * color transitions)
1720      *
1721      * @param hue the Hue
1722      * @param sat the saturation
1723      * @param value the value
1724      * @return id of the color (color ids are short)
1725      */
addColorExpression(float hue, float sat, float value)1726     public short addColorExpression(float hue, float sat, float value) {
1727         ColorExpression c = new ColorExpression(0, hue, sat, value);
1728         short id = (short) mRemoteComposeState.cacheData(c);
1729         c.mId = id;
1730         c.write(mBuffer);
1731         return id;
1732     }
1733 
1734     /**
1735      * Color calculated by Alpha, Hue saturation and value. (as floats they can be variables used to
1736      * create color transitions)
1737      *
1738      * @param alpha the Alpha
1739      * @param hue the hue
1740      * @param sat the saturation
1741      * @param value the value
1742      * @return id of the color (color ids are short)
1743      */
addColorExpression(int alpha, float hue, float sat, float value)1744     public short addColorExpression(int alpha, float hue, float sat, float value) {
1745         ColorExpression c =
1746                 new ColorExpression(0, ColorExpression.HSV_MODE, alpha, hue, sat, value);
1747         short id = (short) mRemoteComposeState.cacheData(c);
1748         c.mId = id;
1749         c.write(mBuffer);
1750         return id;
1751     }
1752 
1753     /**
1754      * Color calculated by Alpha, Red, Green and Blue. (as floats they can be variables used to
1755      * create color transitions)
1756      *
1757      * @param alpha the alpha value of the color
1758      * @param red the red component of the color
1759      * @param green the green component of the color
1760      * @param blue the blue component of the color
1761      * @return id of the color (color ids are short)
1762      */
addColorExpression(float alpha, float red, float green, float blue)1763     public short addColorExpression(float alpha, float red, float green, float blue) {
1764         ColorExpression c =
1765                 new ColorExpression(0, ColorExpression.ARGB_MODE, alpha, red, green, blue);
1766         short id = (short) mRemoteComposeState.cacheData(c);
1767         c.mId = id;
1768         c.write(mBuffer);
1769         return id;
1770     }
1771 
1772     /**
1773      * create and animation based on description and return as an array of floats. see
1774      * addAnimatedFloat
1775      *
1776      * @param duration the duration of the animation in seconds
1777      * @param type the type of animation
1778      * @param spec the parameters of the animation if any
1779      * @param initialValue the initial value if it animates to a start
1780      * @param wrap the wraps value so (e.g 360 so angles 355 would animate to 5)
1781      * @return
1782      */
packAnimation( float duration, int type, @Nullable float[] spec, float initialValue, float wrap)1783     public static @NonNull float[] packAnimation(
1784             float duration, int type, @Nullable float[] spec, float initialValue, float wrap) {
1785 
1786         return FloatAnimation.packToFloatArray(duration, type, spec, initialValue, wrap);
1787     }
1788 
1789     /**
1790      * This defines the name of a type of given object
1791      *
1792      * @param id of the float
1793      * @param name name of the float
1794      * @param type the type of variable NamedVariable.COLOR_TYPE, STRING_TYPE, etc
1795      */
setNamedVariable(int id, @NonNull String name, int type)1796     public void setNamedVariable(int id, @NonNull String name, int type) {
1797         NamedVariable.apply(mBuffer, id, type, name);
1798     }
1799 
1800     /**
1801      * Returns a usable component id -- either the one passed in parameter if not -1 or a generated
1802      * one.
1803      *
1804      * @param id the current component id (if -1, we'll generate a new one)
1805      * @return a usable component id
1806      */
getComponentId(int id)1807     private int getComponentId(int id) {
1808         int resolvedId = 0;
1809         if (id != -1) {
1810             resolvedId = id;
1811         } else {
1812             mGeneratedComponentId--;
1813             resolvedId = mGeneratedComponentId;
1814         }
1815         return resolvedId;
1816     }
1817 
1818     /**
1819      * Add a component start tag
1820      *
1821      * @param type type of component
1822      * @param id component id
1823      */
addComponentStart(int type, int id)1824     public void addComponentStart(int type, int id) {
1825         mLastComponentId = getComponentId(id);
1826         ComponentStart.apply(mBuffer, type, mLastComponentId, 0f, 0f);
1827     }
1828 
1829     /**
1830      * Add a component start tag
1831      *
1832      * @param type type of component
1833      */
addComponentStart(int type)1834     public void addComponentStart(int type) {
1835         addComponentStart(type, -1);
1836     }
1837 
1838     /** Add a component end tag */
addContainerEnd()1839     public void addContainerEnd() {
1840         ContainerEnd.apply(mBuffer);
1841     }
1842 
1843     /**
1844      * Add a scroll modifier
1845      *
1846      * @param direction HORIZONTAL(0) or VERTICAL(1)
1847      * @param positionId the position id as a NaN
1848      * @param notches
1849      */
addModifierScroll(int direction, float positionId, int notches)1850     public void addModifierScroll(int direction, float positionId, int notches) {
1851         // TODO: add support for non-notch behaviors etc.
1852         float max = this.reserveFloatVariable();
1853         float notchMax = this.reserveFloatVariable();
1854         float touchExpressionDirection =
1855                 direction != 0 ? RemoteContext.FLOAT_TOUCH_POS_X : RemoteContext.FLOAT_TOUCH_POS_Y;
1856 
1857         ScrollModifierOperation.apply(mBuffer, direction, positionId, max, notchMax);
1858 
1859         this.addTouchExpression(
1860                 positionId,
1861                 0f,
1862                 0f,
1863                 max,
1864                 0f,
1865                 3,
1866                 new float[] {
1867                     touchExpressionDirection, -1, MUL,
1868                 },
1869                 TouchExpression.STOP_NOTCHES_EVEN,
1870                 new float[] {notches, notchMax},
1871                 null);
1872 
1873         ContainerEnd.apply(mBuffer);
1874     }
1875 
1876     /**
1877      * Add a scroll modifier
1878      *
1879      * @param direction HORIZONTAL(0) or VERTICAL(1)
1880      * @param positionId the position id as a NaN
1881      */
addModifierScroll(int direction, float positionId)1882     public void addModifierScroll(int direction, float positionId) {
1883         float max = this.reserveFloatVariable();
1884         float notchMax = this.reserveFloatVariable();
1885         float touchExpressionDirection =
1886                 direction != 0 ? RemoteContext.FLOAT_TOUCH_POS_X : RemoteContext.FLOAT_TOUCH_POS_Y;
1887 
1888         ScrollModifierOperation.apply(mBuffer, direction, positionId, max, notchMax);
1889         this.addTouchExpression(
1890                 positionId,
1891                 0f,
1892                 0f,
1893                 max,
1894                 0f,
1895                 3,
1896                 new float[] {
1897                     touchExpressionDirection, -1, MUL,
1898                 },
1899                 TouchExpression.STOP_GENTLY,
1900                 null,
1901                 null);
1902         ContainerEnd.apply(mBuffer);
1903     }
1904 
1905     /**
1906      * Add a scroll modifier
1907      *
1908      * @param direction HORIZONTAL(0) or VERTICAL(1)
1909      */
addModifierScroll(int direction)1910     public void addModifierScroll(int direction) {
1911         float max = this.reserveFloatVariable();
1912         ScrollModifierOperation.apply(mBuffer, direction, 0f, max, 0f);
1913         ContainerEnd.apply(mBuffer);
1914     }
1915 
1916     /**
1917      * Add a background modifier of provided color
1918      *
1919      * @param color the color of the background
1920      * @param shape the background shape -- SHAPE_RECTANGLE, SHAPE_CIRCLE
1921      */
addModifierBackground(int color, int shape)1922     public void addModifierBackground(int color, int shape) {
1923         float r = (color >> 16 & 0xff) / 255.0f;
1924         float g = (color >> 8 & 0xff) / 255.0f;
1925         float b = (color & 0xff) / 255.0f;
1926         float a = (color >> 24 & 0xff) / 255.0f;
1927         BackgroundModifierOperation.apply(mBuffer, 0f, 0f, 0f, 0f, r, g, b, a, shape);
1928     }
1929 
1930     /**
1931      * Add a border modifier
1932      *
1933      * @param borderWidth the border width
1934      * @param borderRoundedCorner the rounded corner radius if the shape is ROUNDED_RECT
1935      * @param color the color of the border
1936      * @param shape the shape of the border
1937      */
addModifierBorder( float borderWidth, float borderRoundedCorner, int color, int shape)1938     public void addModifierBorder(
1939             float borderWidth, float borderRoundedCorner, int color, int shape) {
1940         float r = (color >> 16 & 0xff) / 255.0f;
1941         float g = (color >> 8 & 0xff) / 255.0f;
1942         float b = (color & 0xff) / 255.0f;
1943         float a = (color >> 24 & 0xff) / 255.0f;
1944         BorderModifierOperation.apply(
1945                 mBuffer, 0f, 0f, 0f, 0f, borderWidth, borderRoundedCorner, r, g, b, a, shape);
1946     }
1947 
1948     /**
1949      * Add a padding modifier
1950      *
1951      * @param left left padding
1952      * @param top top padding
1953      * @param right right padding
1954      * @param bottom bottom padding
1955      */
addModifierPadding(float left, float top, float right, float bottom)1956     public void addModifierPadding(float left, float top, float right, float bottom) {
1957         PaddingModifierOperation.apply(mBuffer, left, top, right, bottom);
1958     }
1959 
1960     /**
1961      * Add an offset modifier
1962      *
1963      * @param x x offset
1964      * @param y y offset
1965      */
addModifierOffset(float x, float y)1966     public void addModifierOffset(float x, float y) {
1967         OffsetModifierOperation.apply(mBuffer, x, y);
1968     }
1969 
1970     /**
1971      * Add a zIndex modifier
1972      *
1973      * @param value z-Index value
1974      */
addModifierZIndex(float value)1975     public void addModifierZIndex(float value) {
1976         ZIndexModifierOperation.apply(mBuffer, value);
1977     }
1978 
1979     /** Add a ripple effect on touch down as a modifier */
addModifierRipple()1980     public void addModifierRipple() {
1981         RippleModifierOperation.apply(mBuffer);
1982     }
1983 
1984     /**
1985      * Add a marquee modifier
1986      *
1987      * @param iterations number of iterations
1988      * @param animationMode animation mode
1989      * @param repeatDelayMillis repeat delay
1990      * @param initialDelayMillis initial delay
1991      * @param spacing spacing between items
1992      * @param velocity velocity of the marquee
1993      */
addModifierMarquee( int iterations, int animationMode, float repeatDelayMillis, float initialDelayMillis, float spacing, float velocity)1994     public void addModifierMarquee(
1995             int iterations,
1996             int animationMode,
1997             float repeatDelayMillis,
1998             float initialDelayMillis,
1999             float spacing,
2000             float velocity) {
2001         MarqueeModifierOperation.apply(
2002                 mBuffer,
2003                 iterations,
2004                 animationMode,
2005                 repeatDelayMillis,
2006                 initialDelayMillis,
2007                 spacing,
2008                 velocity);
2009     }
2010 
2011     /**
2012      * Add a graphics layer
2013      *
2014      * @param attributes
2015      */
addModifierGraphicsLayer(HashMap<Integer, Object> attributes)2016     public void addModifierGraphicsLayer(HashMap<Integer, Object> attributes) {
2017         GraphicsLayerModifierOperation.apply(mBuffer, attributes);
2018     }
2019 
2020     /**
2021      * Sets the clip based on rounded clip rect
2022      *
2023      * @param topStart
2024      * @param topEnd
2025      * @param bottomStart
2026      * @param bottomEnd
2027      */
addRoundClipRectModifier( float topStart, float topEnd, float bottomStart, float bottomEnd)2028     public void addRoundClipRectModifier(
2029             float topStart, float topEnd, float bottomStart, float bottomEnd) {
2030         RoundedClipRectModifierOperation.apply(mBuffer, topStart, topEnd, bottomStart, bottomEnd);
2031     }
2032 
2033     /** Add a clip rect modifier */
addClipRectModifier()2034     public void addClipRectModifier() {
2035         ClipRectModifierOperation.apply(mBuffer);
2036     }
2037 
2038     /**
2039      * add start of loop
2040      *
2041      * @param indexId id of the variable
2042      * @param from start value
2043      * @param step step value
2044      * @param until stop value
2045      */
addLoopStart(int indexId, float from, float step, float until)2046     public void addLoopStart(int indexId, float from, float step, float until) {
2047         LoopOperation.apply(mBuffer, indexId, from, step, until);
2048     }
2049 
2050     /** Add a loop end */
addLoopEnd()2051     public void addLoopEnd() {
2052         ContainerEnd.apply(mBuffer);
2053     }
2054 
2055     /**
2056      * add a state layout
2057      *
2058      * @param componentId id of the state
2059      * @param animationId animation id
2060      * @param horizontal horizontal alignment
2061      * @param vertical vertical alignment
2062      * @param indexId index of the state
2063      */
addStateLayout( int componentId, int animationId, int horizontal, int vertical, int indexId)2064     public void addStateLayout(
2065             int componentId, int animationId, int horizontal, int vertical, int indexId) {
2066         mLastComponentId = getComponentId(componentId);
2067         StateLayout.apply(mBuffer, mLastComponentId, animationId, horizontal, vertical, indexId);
2068     }
2069 
2070     /**
2071      * Add a box start tag
2072      *
2073      * @param componentId component id
2074      * @param animationId animation id
2075      * @param horizontal horizontal alignment
2076      * @param vertical vertical alignment
2077      */
addBoxStart(int componentId, int animationId, int horizontal, int vertical)2078     public void addBoxStart(int componentId, int animationId, int horizontal, int vertical) {
2079         mLastComponentId = getComponentId(componentId);
2080         BoxLayout.apply(mBuffer, mLastComponentId, animationId, horizontal, vertical);
2081     }
2082 
2083     /**
2084      * Add a fitbox start tag
2085      *
2086      * @param componentId component id
2087      * @param animationId animation id
2088      * @param horizontal horizontal alignment
2089      * @param vertical vertical alignment
2090      */
addFitBoxStart(int componentId, int animationId, int horizontal, int vertical)2091     public void addFitBoxStart(int componentId, int animationId, int horizontal, int vertical) {
2092         mLastComponentId = getComponentId(componentId);
2093         FitBoxLayout.apply(mBuffer, mLastComponentId, animationId, horizontal, vertical);
2094     }
2095 
2096     /**
2097      * Add an imagelayout command
2098      *
2099      * @param componentId component id
2100      * @param animationId animation id
2101      * @param bitmapId bitmap id
2102      */
addImage( int componentId, int animationId, int bitmapId, int scaleType, float alpha)2103     public void addImage(
2104             int componentId, int animationId, int bitmapId, int scaleType, float alpha) {
2105         mLastComponentId = getComponentId(componentId);
2106         ImageLayout.apply(mBuffer, componentId, animationId, bitmapId, scaleType, alpha);
2107     }
2108 
2109     /**
2110      * Add a row start tag
2111      *
2112      * @param componentId component id
2113      * @param animationId animation id
2114      * @param horizontal horizontal alignment
2115      * @param vertical vertical alignment
2116      * @param spacedBy spacing between items
2117      */
addRowStart( int componentId, int animationId, int horizontal, int vertical, float spacedBy)2118     public void addRowStart(
2119             int componentId, int animationId, int horizontal, int vertical, float spacedBy) {
2120         mLastComponentId = getComponentId(componentId);
2121         RowLayout.apply(mBuffer, mLastComponentId, animationId, horizontal, vertical, spacedBy);
2122     }
2123 
2124     /**
2125      * Add a row start tag
2126      *
2127      * @param componentId component id
2128      * @param animationId animation id
2129      * @param horizontal horizontal alignment
2130      * @param vertical vertical alignment
2131      * @param spacedBy spacing between items
2132      */
addCollapsibleRowStart( int componentId, int animationId, int horizontal, int vertical, float spacedBy)2133     public void addCollapsibleRowStart(
2134             int componentId, int animationId, int horizontal, int vertical, float spacedBy) {
2135         mLastComponentId = getComponentId(componentId);
2136         CollapsibleRowLayout.apply(
2137                 mBuffer, mLastComponentId, animationId, horizontal, vertical, spacedBy);
2138     }
2139 
2140     /**
2141      * Add a column start tag
2142      *
2143      * @param componentId component id
2144      * @param animationId animation id
2145      * @param horizontal horizontal alignment
2146      * @param vertical vertical alignment
2147      * @param spacedBy spacing between items
2148      */
addColumnStart( int componentId, int animationId, int horizontal, int vertical, float spacedBy)2149     public void addColumnStart(
2150             int componentId, int animationId, int horizontal, int vertical, float spacedBy) {
2151         mLastComponentId = getComponentId(componentId);
2152         ColumnLayout.apply(mBuffer, mLastComponentId, animationId, horizontal, vertical, spacedBy);
2153     }
2154 
2155     /**
2156      * Add a column start tag
2157      *
2158      * @param componentId component id
2159      * @param animationId animation id
2160      * @param horizontal horizontal alignment
2161      * @param vertical vertical alignment
2162      * @param spacedBy spacing between items
2163      */
addCollapsibleColumnStart( int componentId, int animationId, int horizontal, int vertical, float spacedBy)2164     public void addCollapsibleColumnStart(
2165             int componentId, int animationId, int horizontal, int vertical, float spacedBy) {
2166         mLastComponentId = getComponentId(componentId);
2167         CollapsibleColumnLayout.apply(
2168                 mBuffer, mLastComponentId, animationId, horizontal, vertical, spacedBy);
2169     }
2170 
2171     /**
2172      * Add a canvas start tag
2173      *
2174      * @param componentId component id
2175      * @param animationId animation id
2176      */
addCanvasStart(int componentId, int animationId)2177     public void addCanvasStart(int componentId, int animationId) {
2178         mLastComponentId = getComponentId(componentId);
2179         CanvasLayout.apply(mBuffer, mLastComponentId, animationId);
2180     }
2181 
2182     /**
2183      * Add a canvas content start tag
2184      *
2185      * @param componentId component id
2186      */
addCanvasContentStart(int componentId)2187     public void addCanvasContentStart(int componentId) {
2188         mLastComponentId = getComponentId(componentId);
2189         CanvasContent.apply(mBuffer, mLastComponentId);
2190     }
2191 
2192     /** Add a root start tag */
addRootStart()2193     public void addRootStart() {
2194         mLastComponentId = getComponentId(-1);
2195         RootLayoutComponent.apply(mBuffer, mLastComponentId);
2196     }
2197 
2198     /** Add a content start tag */
addContentStart()2199     public void addContentStart() {
2200         mLastComponentId = getComponentId(-1);
2201         LayoutComponentContent.apply(mBuffer, mLastComponentId);
2202     }
2203 
2204     /** Add a canvas operations start tag */
addCanvasOperationsStart()2205     public void addCanvasOperationsStart() {
2206         CanvasOperations.apply(mBuffer);
2207     }
2208 
2209     /** Add container hosting actions */
addRunActionsStart()2210     public void addRunActionsStart() {
2211         RunActionOperation.apply(mBuffer);
2212     }
2213 
2214     /**
2215      * Add a component width value
2216      *
2217      * @param id id of the value
2218      */
addComponentWidthValue(int id)2219     public void addComponentWidthValue(int id) {
2220         ComponentValue.apply(mBuffer, ComponentValue.WIDTH, mLastComponentId, id);
2221     }
2222 
2223     /**
2224      * Add a component height value
2225      *
2226      * @param id id of the value
2227      */
addComponentHeightValue(int id)2228     public void addComponentHeightValue(int id) {
2229         ComponentValue.apply(mBuffer, ComponentValue.HEIGHT, mLastComponentId, id);
2230     }
2231 
2232     /**
2233      * Add a text component start tag
2234      *
2235      * @param componentId component id
2236      * @param animationId animation id
2237      * @param textId id of the text
2238      * @param color color of the text
2239      * @param fontSize font size
2240      * @param fontStyle font style (0 : Normal, 1 : Italic)
2241      * @param fontWeight font weight (1 to 1000, normal is 400)
2242      * @param fontFamily font family or null
2243      * @param overflow
2244      * @param maxLines
2245      */
addTextComponentStart( int componentId, int animationId, int textId, int color, float fontSize, int fontStyle, float fontWeight, @Nullable String fontFamily, int textAlign, int overflow, int maxLines)2246     public void addTextComponentStart(
2247             int componentId,
2248             int animationId,
2249             int textId,
2250             int color,
2251             float fontSize,
2252             int fontStyle,
2253             float fontWeight,
2254             @Nullable String fontFamily,
2255             int textAlign,
2256             int overflow,
2257             int maxLines) {
2258         mLastComponentId = getComponentId(componentId);
2259         int fontFamilyId = -1;
2260         if (fontFamily != null) {
2261             fontFamilyId = addText(fontFamily);
2262         }
2263         TextLayout.apply(
2264                 mBuffer,
2265                 mLastComponentId,
2266                 animationId,
2267                 textId,
2268                 color,
2269                 fontSize,
2270                 fontStyle,
2271                 fontWeight,
2272                 fontFamilyId,
2273                 textAlign,
2274                 overflow,
2275                 maxLines);
2276     }
2277 
2278     /**
2279      * Returns the next available id for the given type
2280      *
2281      * @param type the type of the value
2282      * @return a unique id
2283      */
createID(int type)2284     public int createID(int type) {
2285         return mRemoteComposeState.nextId(type);
2286     }
2287 
2288     /**
2289      * Returns the next available id
2290      *
2291      * @return a unique id
2292      */
nextId()2293     public int nextId() {
2294         return mRemoteComposeState.nextId();
2295     }
2296 
2297     /**
2298      * add an impulse. (must be followed by impulse end)
2299      *
2300      * @param duration duration of the impulse
2301      * @param start the start time
2302      */
addImpulse(float duration, float start)2303     public void addImpulse(float duration, float start) {
2304         ImpulseOperation.apply(mBuffer, duration, start);
2305     }
2306 
2307     /** add an impulse process */
addImpulseProcess()2308     public void addImpulseProcess() {
2309         ImpulseProcess.apply(mBuffer);
2310     }
2311 
2312     /** Add an impulse end */
addImpulseEnd()2313     public void addImpulseEnd() {
2314         ContainerEnd.apply(mBuffer);
2315     }
2316 
2317     /**
2318      * Start a particle engine container & initial setup
2319      *
2320      * @param id the particle engine id
2321      * @param varIds list of variable ids
2322      * @param initialExpressions the expressions used to initialize the variables
2323      * @param particleCount the number of particles to draw
2324      */
addParticles( int id, int[] varIds, float[][] initialExpressions, int particleCount)2325     public void addParticles(
2326             int id, int[] varIds, float[][] initialExpressions, int particleCount) {
2327         ParticlesCreate.apply(mBuffer, id, varIds, initialExpressions, particleCount);
2328     }
2329 
2330     /**
2331      * Setup the particle engine loop
2332      *
2333      * @param id the particle engine id
2334      * @param restart value on restart
2335      * @param expressions the expressions used to update the variables during the particles run
2336      */
addParticlesLoop(int id, float[] restart, float[][] expressions)2337     public void addParticlesLoop(int id, float[] restart, float[][] expressions) {
2338         ParticlesLoop.apply(mBuffer, id, restart, expressions);
2339     }
2340 
2341     /** Closes the particle engine container */
addParticleLoopEnd()2342     public void addParticleLoopEnd() {
2343         ContainerEnd.apply(mBuffer);
2344     }
2345 
2346     /**
2347      * @param fid The id of the function
2348      * @param args The arguments of the function
2349      */
defineFloatFunction(int fid, int[] args)2350     public void defineFloatFunction(int fid, int[] args) {
2351         FloatFunctionDefine.apply(mBuffer, fid, args);
2352     }
2353 
2354     /** end the definition of the function */
addEndFloatFunctionDef()2355     public void addEndFloatFunctionDef() {
2356         ContainerEnd.apply(mBuffer);
2357     }
2358 
2359     /**
2360      * add a function call
2361      *
2362      * @param id the id of the function to call
2363      * @param args the arguments of the function
2364      */
callFloatFunction(int id, float[] args)2365     public void callFloatFunction(int id, float[] args) {
2366         FloatFunctionCall.apply(mBuffer, id, args);
2367     }
2368 
2369     /**
2370      * @param bitmapId the id of the bitmap
2371      * @param attribute the attribute to get
2372      * @return the nan id of the attribute
2373      */
bitmapAttribute(int bitmapId, short attribute)2374     public float bitmapAttribute(int bitmapId, short attribute) {
2375         int id = mRemoteComposeState.nextId();
2376         ImageAttribute.apply(mBuffer, id, bitmapId, attribute, null);
2377         return Utils.asNan(id);
2378     }
2379 
2380     /**
2381      * @param textId the id of the bitmap
2382      * @param attribute the attribute to get
2383      * @return the nan id of the attribute
2384      */
textAttribute(int textId, short attribute)2385     public float textAttribute(int textId, short attribute) {
2386         int id = mRemoteComposeState.nextId();
2387         TextAttribute.apply(mBuffer, id, textId, attribute);
2388         return Utils.asNan(id);
2389     }
2390 
2391     /**
2392      * @param timeId the id of the long
2393      * @param attribute the attribute to get
2394      * @param args the arguments of the function
2395      * @return the nan id of the attribute
2396      */
timeAttribute(int timeId, short attribute, int... args)2397     public float timeAttribute(int timeId, short attribute, int... args) {
2398         int id = mRemoteComposeState.nextId();
2399         TimeAttribute.apply(mBuffer, id, timeId, attribute, args);
2400         return Utils.asNan(id);
2401     }
2402 
2403     /** In the context of a component draw modifier, draw the content of the component */
drawComponentContent()2404     public void drawComponentContent() {
2405         DrawContent.apply(mBuffer);
2406     }
2407 
2408     /**
2409      * Ensures the bitmap is stored.
2410      *
2411      * @param image the bitbap to store
2412      * @return the id of the bitmap
2413      */
storeBitmap(Object image)2414     private int storeBitmap(Object image) {
2415         int imageId = mRemoteComposeState.dataGetId(image);
2416         if (imageId == -1) {
2417             imageId = mRemoteComposeState.cacheData(image);
2418             byte[] data = mPlatform.imageToByteArray(image); // todo: potential npe
2419             short imageWidth = (short) mPlatform.getImageWidth(image);
2420             short imageHeight = (short) mPlatform.getImageHeight(image);
2421             if (mPlatform.isAlpha8Image(image)) {
2422                 BitmapData.apply(
2423                         mBuffer,
2424                         imageId,
2425                         BitmapData.TYPE_PNG_ALPHA_8,
2426                         imageWidth,
2427                         BitmapData.ENCODING_INLINE,
2428                         imageHeight,
2429                         data); // todo: potential npe
2430             } else {
2431                 BitmapData.apply(
2432                         mBuffer, imageId, imageWidth, imageHeight, data); // todo: potential npe
2433             }
2434         }
2435 
2436         return imageId;
2437     }
2438 
2439     /**
2440      * Combine two paths
2441      *
2442      * @param id output id
2443      * @param path1 first path
2444      * @param path2 second path
2445      * @param op operation to perform OP_DIFFERENCE, OP_INTERSECT, OP_REVERSE_DIFFERENCE, OP_UNION,
2446      *     OP_XOR
2447      */
pathCombine(int id, int path1, int path2, byte op)2448     public void pathCombine(int id, int path1, int path2, byte op) {
2449         PathCombine.apply(mBuffer, id, path1, path2, op);
2450     }
2451 
2452     /**
2453      * Perform a haptic feedback
2454      *
2455      * @param feedbackConstant
2456      */
performHaptic(int feedbackConstant)2457     public void performHaptic(int feedbackConstant) {
2458         HapticFeedback.apply(mBuffer, feedbackConstant);
2459     }
2460 
2461     /**
2462      * Add a conditional operation
2463      *
2464      * @param type type of comparison
2465      * @param a first value
2466      * @param b second value
2467      */
addConditionalOperations(byte type, float a, float b)2468     public void addConditionalOperations(byte type, float a, float b) {
2469         ConditionalOperations.apply(mBuffer, type, a, b);
2470     }
2471 
2472     /**
2473      * Add a debug message
2474      *
2475      * @param textId text id
2476      * @param value value
2477      * @param flags flags
2478      */
addDebugMessage(int textId, float value, int flags)2479     public void addDebugMessage(int textId, float value, int flags) {
2480         DebugMessage.apply(mBuffer, textId, value, flags);
2481     }
2482 
2483     /**
2484      * Return a color attribute value on the given color
2485      *
2486      * @param baseColor
2487      * @param type type of attribute
2488      * @return
2489      */
getColorAttribute(int baseColor, short type)2490     public float getColorAttribute(int baseColor, short type) {
2491         int id = mRemoteComposeState.nextId();
2492         ColorAttribute.apply(mBuffer, id, baseColor, type);
2493         return Utils.asNan(id);
2494     }
2495 }
2496