• 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.player.platform;
17 
18 import android.annotation.NonNull;
19 import android.annotation.Nullable;
20 import android.graphics.Bitmap;
21 import android.graphics.BitmapFactory;
22 import android.graphics.Canvas;
23 import android.graphics.Paint;
24 
25 import com.android.internal.widget.remotecompose.core.RemoteContext;
26 import com.android.internal.widget.remotecompose.core.TouchListener;
27 import com.android.internal.widget.remotecompose.core.VariableSupport;
28 import com.android.internal.widget.remotecompose.core.operations.BitmapData;
29 import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
30 import com.android.internal.widget.remotecompose.core.operations.ShaderData;
31 import com.android.internal.widget.remotecompose.core.operations.utilities.ArrayAccess;
32 import com.android.internal.widget.remotecompose.core.operations.utilities.DataMap;
33 import com.android.internal.widget.remotecompose.core.types.LongConstant;
34 
35 import java.io.IOException;
36 import java.net.MalformedURLException;
37 import java.net.URL;
38 import java.util.HashMap;
39 
40 /**
41  * An implementation of Context for Android.
42  *
43  * <p>This is used to play the RemoteCompose operations on Android.
44  */
45 public class AndroidRemoteContext extends RemoteContext {
46 
useCanvas(Canvas canvas)47     public void useCanvas(Canvas canvas) {
48         if (mPaintContext == null) {
49             mPaintContext = new AndroidPaintContext(this, canvas);
50         } else {
51             // need to make sure to update the canvas for the current one
52             mPaintContext.reset();
53             ((AndroidPaintContext) mPaintContext).setCanvas(canvas);
54         }
55         mWidth = canvas.getWidth();
56         mHeight = canvas.getHeight();
57     }
58 
59     ///////////////////////////////////////////////////////////////////////////////////////////////
60     // Data handling
61     ///////////////////////////////////////////////////////////////////////////////////////////////
62 
63     @Override
loadPathData(int instanceId, @NonNull float[] floatPath)64     public void loadPathData(int instanceId, @NonNull float[] floatPath) {
65         mRemoteComposeState.putPathData(instanceId, floatPath);
66     }
67 
68     @Override
getPathData(int instanceId)69     public float[] getPathData(int instanceId) {
70         return mRemoteComposeState.getPathData(instanceId);
71     }
72 
73     static class VarName {
74         String mName;
75         int mId;
76         int mType;
77 
VarName(String name, int id, int type)78         VarName(String name, int id, int type) {
79             mName = name;
80             mId = id;
81             mType = type;
82         }
83     }
84 
85     HashMap<String, VarName> mVarNameHashMap = new HashMap<>();
86 
87     @Override
loadVariableName(@onNull String varName, int varId, int varType)88     public void loadVariableName(@NonNull String varName, int varId, int varType) {
89         mVarNameHashMap.put(varName, new VarName(varName, varId, varType));
90     }
91 
92     @Override
setNamedStringOverride(@onNull String stringName, @NonNull String value)93     public void setNamedStringOverride(@NonNull String stringName, @NonNull String value) {
94         if (mVarNameHashMap.get(stringName) != null) {
95             int id = mVarNameHashMap.get(stringName).mId;
96             overrideText(id, value);
97         }
98     }
99 
100     @Override
clearNamedStringOverride(@onNull String stringName)101     public void clearNamedStringOverride(@NonNull String stringName) {
102         if (mVarNameHashMap.get(stringName) != null) {
103             int id = mVarNameHashMap.get(stringName).mId;
104             clearDataOverride(id);
105         }
106         mVarNameHashMap.put(stringName, null);
107     }
108 
109     @Override
setNamedIntegerOverride(@onNull String stringName, int value)110     public void setNamedIntegerOverride(@NonNull String stringName, int value) {
111         if (mVarNameHashMap.get(stringName) != null) {
112             int id = mVarNameHashMap.get(stringName).mId;
113             overrideInt(id, value);
114         }
115     }
116 
117     @Override
clearNamedIntegerOverride(@onNull String integerName)118     public void clearNamedIntegerOverride(@NonNull String integerName) {
119         if (mVarNameHashMap.get(integerName) != null) {
120             int id = mVarNameHashMap.get(integerName).mId;
121             clearIntegerOverride(id);
122         }
123         mVarNameHashMap.put(integerName, null);
124     }
125 
126     @Override
setNamedFloatOverride(String floatName, float value)127     public void setNamedFloatOverride(String floatName, float value) {
128         if (mVarNameHashMap.get(floatName) != null) {
129             int id = mVarNameHashMap.get(floatName).mId;
130             overrideFloat(id, value);
131         }
132     }
133 
134     @Override
clearNamedFloatOverride(String floatName)135     public void clearNamedFloatOverride(String floatName) {
136         if (mVarNameHashMap.get(floatName) != null) {
137             int id = mVarNameHashMap.get(floatName).mId;
138             clearFloatOverride(id);
139         }
140         mVarNameHashMap.put(floatName, null);
141     }
142 
143     @Override
setNamedLong(String name, long value)144     public void setNamedLong(String name, long value) {
145         VarName entry = mVarNameHashMap.get(name);
146         if (entry != null) {
147             int id = entry.mId;
148             LongConstant longConstant = (LongConstant) mRemoteComposeState.getObject(id);
149             longConstant.setValue(value);
150         }
151     }
152 
153     @Override
setNamedDataOverride(String dataName, Object value)154     public void setNamedDataOverride(String dataName, Object value) {
155         if (mVarNameHashMap.get(dataName) != null) {
156             int id = mVarNameHashMap.get(dataName).mId;
157             overrideData(id, value);
158         }
159     }
160 
161     @Override
clearNamedDataOverride(String dataName)162     public void clearNamedDataOverride(String dataName) {
163         if (mVarNameHashMap.get(dataName) != null) {
164             int id = mVarNameHashMap.get(dataName).mId;
165             clearDataOverride(id);
166         }
167         mVarNameHashMap.put(dataName, null);
168     }
169 
170     /**
171      * Override a color to force it to be the color provided
172      *
173      * @param colorName name of color
174      * @param color
175      */
setNamedColorOverride(@onNull String colorName, int color)176     public void setNamedColorOverride(@NonNull String colorName, int color) {
177         int id = mVarNameHashMap.get(colorName).mId;
178         mRemoteComposeState.overrideColor(id, color);
179     }
180 
181     @Override
addCollection(int id, @NonNull ArrayAccess collection)182     public void addCollection(int id, @NonNull ArrayAccess collection) {
183         mRemoteComposeState.addCollection(id, collection);
184     }
185 
186     @Override
putDataMap(int id, @NonNull DataMap map)187     public void putDataMap(int id, @NonNull DataMap map) {
188         mRemoteComposeState.putDataMap(id, map);
189     }
190 
191     @Override
getDataMap(int id)192     public DataMap getDataMap(int id) {
193         return mRemoteComposeState.getDataMap(id);
194     }
195 
196     @Override
runAction(int id, @NonNull String metadata)197     public void runAction(int id, @NonNull String metadata) {
198         mDocument.performClick(this, id, metadata);
199     }
200 
201     @Override
runNamedAction(int id, Object value)202     public void runNamedAction(int id, Object value) {
203         String text = getText(id);
204         mDocument.runNamedAction(text, value);
205     }
206 
207     /**
208      * Decode a byte array into an image and cache it using the given imageId
209      *
210      * @param encoding how the data is encoded 0 = png, 1 = raw, 2 = url
211      * @param type the type of the data 0 = RGBA 8888, 1 = 888, 2 = 8 gray
212      * @param width with of image to be loaded largest dimension is 32767
213      * @param height height of image to be loaded
214      * @param data a byte array containing the image information
215      * @oaram imageId the id of the image
216      */
217     @Override
loadBitmap( int imageId, short encoding, short type, int width, int height, @NonNull byte[] data)218     public void loadBitmap(
219             int imageId, short encoding, short type, int width, int height, @NonNull byte[] data) {
220         if (!mRemoteComposeState.containsId(imageId)) {
221             Bitmap image = null;
222             switch (encoding) {
223                 case BitmapData.ENCODING_INLINE:
224                     switch (type) {
225                         case BitmapData.TYPE_PNG_8888:
226                             image = BitmapFactory.decodeByteArray(data, 0, data.length);
227                             break;
228                         case BitmapData.TYPE_PNG_ALPHA_8:
229                             image = decodePreferringAlpha8(data);
230 
231                             // If needed convert to ALPHA_8.
232                             if (!image.getConfig().equals(Bitmap.Config.ALPHA_8)) {
233                                 Bitmap alpha8Bitmap =
234                                         Bitmap.createBitmap(
235                                                 image.getWidth(),
236                                                 image.getHeight(),
237                                                 Bitmap.Config.ALPHA_8);
238                                 Canvas canvas = new Canvas(alpha8Bitmap);
239                                 Paint paint = new Paint();
240                                 paint.setXfermode(
241                                         new android.graphics.PorterDuffXfermode(
242                                                 android.graphics.PorterDuff.Mode.SRC));
243                                 canvas.drawBitmap(image, 0, 0, paint);
244                                 image.recycle(); // Release resources
245 
246                                 image = alpha8Bitmap;
247                             }
248                             break;
249                         case BitmapData.TYPE_RAW8888:
250                             image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
251                             int[] idata = new int[data.length / 4];
252                             for (int i = 0; i < idata.length; i++) {
253                                 int p = i * 4;
254                                 idata[i] =
255                                         (data[p] << 24)
256                                                 | (data[p + 1] << 16)
257                                                 | (data[p + 2] << 8)
258                                                 | data[p + 3];
259                             }
260                             image.setPixels(idata, 0, width, 0, 0, width, height);
261                             break;
262                         case BitmapData.TYPE_RAW8:
263                             image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
264                             int[] bdata = new int[data.length / 4];
265                             for (int i = 0; i < bdata.length; i++) {
266 
267                                 bdata[i] = 0x1010101 * data[i];
268                             }
269                             image.setPixels(bdata, 0, width, 0, 0, width, height);
270                             break;
271                     }
272                     break;
273                 case BitmapData.ENCODING_FILE:
274                     image = BitmapFactory.decodeFile(new String(data));
275                     break;
276                 case BitmapData.ENCODING_URL:
277                     try {
278                         image = BitmapFactory.decodeStream(new URL(new String(data)).openStream());
279                     } catch (MalformedURLException e) {
280                         throw new RuntimeException(e);
281                     } catch (IOException e) {
282                         throw new RuntimeException(e);
283                     }
284             }
285             mRemoteComposeState.cacheData(imageId, image);
286         }
287     }
288 
decodePreferringAlpha8(@onNull byte[] data)289     private Bitmap decodePreferringAlpha8(@NonNull byte[] data) {
290         BitmapFactory.Options options = new BitmapFactory.Options();
291         options.inPreferredConfig = Bitmap.Config.ALPHA_8;
292         return BitmapFactory.decodeByteArray(data, 0, data.length, options);
293     }
294 
295     @Override
loadText(int id, @NonNull String text)296     public void loadText(int id, @NonNull String text) {
297         if (!mRemoteComposeState.containsId(id)) {
298             mRemoteComposeState.cacheData(id, text);
299         } else {
300             mRemoteComposeState.updateData(id, text);
301         }
302     }
303 
overrideText(int id, String text)304     public void overrideText(int id, String text) {
305         mRemoteComposeState.overrideData(id, text);
306     }
307 
overrideInt(int id, int value)308     public void overrideInt(int id, int value) {
309         mRemoteComposeState.overrideInteger(id, value);
310     }
311 
overrideData(int id, Object value)312     public void overrideData(int id, Object value) {
313         mRemoteComposeState.overrideData(id, value);
314     }
315 
clearDataOverride(int id)316     public void clearDataOverride(int id) {
317         mRemoteComposeState.clearDataOverride(id);
318     }
319 
clearIntegerOverride(int id)320     public void clearIntegerOverride(int id) {
321         mRemoteComposeState.clearIntegerOverride(id);
322     }
323 
clearFloatOverride(int id)324     public void clearFloatOverride(int id) {
325         mRemoteComposeState.clearFloatOverride(id);
326     }
327 
328     @Override
getText(int id)329     public String getText(int id) {
330         return (String) mRemoteComposeState.getFromId(id);
331     }
332 
333     @Override
loadFloat(int id, float value)334     public void loadFloat(int id, float value) {
335         mRemoteComposeState.updateFloat(id, value);
336     }
337 
338     @Override
overrideFloat(int id, float value)339     public void overrideFloat(int id, float value) {
340         mRemoteComposeState.overrideFloat(id, value);
341     }
342 
343     @Override
loadInteger(int id, int value)344     public void loadInteger(int id, int value) {
345         mRemoteComposeState.updateInteger(id, value);
346     }
347 
overrideInteger(int id, int value)348     public void overrideInteger(int id, int value) {
349         mRemoteComposeState.overrideInteger(id, value);
350     }
351 
overrideText(int id, int valueId)352     public void overrideText(int id, int valueId) {
353         String text = getText(valueId);
354         overrideText(id, text);
355     }
356 
357     @Override
loadColor(int id, int color)358     public void loadColor(int id, int color) {
359         mRemoteComposeState.updateColor(id, color);
360     }
361 
362     @Override
loadAnimatedFloat(int id, @NonNull FloatExpression animatedFloat)363     public void loadAnimatedFloat(int id, @NonNull FloatExpression animatedFloat) {
364         mRemoteComposeState.cacheData(id, animatedFloat);
365     }
366 
367     @Override
loadShader(int id, @NonNull ShaderData value)368     public void loadShader(int id, @NonNull ShaderData value) {
369         mRemoteComposeState.cacheData(id, value);
370     }
371 
372     @Override
getFloat(int id)373     public float getFloat(int id) {
374         return (float) mRemoteComposeState.getFloat(id);
375     }
376 
377     @Override
putObject(int id, @NonNull Object value)378     public void putObject(int id, @NonNull Object value) {
379         mRemoteComposeState.updateObject(id, value);
380     }
381 
382     @Override
getObject(int id)383     public Object getObject(int id) {
384         return mRemoteComposeState.getObject(id);
385     }
386 
387     @Override
getInteger(int id)388     public int getInteger(int id) {
389         return mRemoteComposeState.getInteger(id);
390     }
391 
392     @Override
getLong(int id)393     public long getLong(int id) {
394         return ((LongConstant) mRemoteComposeState.getObject(id)).getValue();
395     }
396 
397     @Override
getColor(int id)398     public int getColor(int id) {
399         return mRemoteComposeState.getColor(id);
400     }
401 
402     @Override
listensTo(int id, @NonNull VariableSupport variableSupport)403     public void listensTo(int id, @NonNull VariableSupport variableSupport) {
404         mRemoteComposeState.listenToVar(id, variableSupport);
405     }
406 
407     @Override
updateOps()408     public int updateOps() {
409         return mRemoteComposeState.getOpsToUpdate(this);
410     }
411 
412     @Override
413     @Nullable
getShader(int id)414     public ShaderData getShader(int id) {
415         return (ShaderData) mRemoteComposeState.getFromId(id);
416     }
417 
418     @Override
addTouchListener(TouchListener touchExpression)419     public void addTouchListener(TouchListener touchExpression) {
420         mDocument.addTouchListener(touchExpression);
421     }
422 
423     ///////////////////////////////////////////////////////////////////////////////////////////////
424     // Click handling
425     ///////////////////////////////////////////////////////////////////////////////////////////////
426 
427     @Override
addClickArea( int id, int contentDescriptionId, float left, float top, float right, float bottom, int metadataId)428     public void addClickArea(
429             int id,
430             int contentDescriptionId,
431             float left,
432             float top,
433             float right,
434             float bottom,
435             int metadataId) {
436         String contentDescription = (String) mRemoteComposeState.getFromId(contentDescriptionId);
437         String metadata = (String) mRemoteComposeState.getFromId(metadataId);
438         mDocument.addClickArea(id, contentDescription, left, top, right, bottom, metadata);
439     }
440 
hapticEffect(int type)441     public void hapticEffect(int type) {
442         mDocument.haptic(type);
443     }
444 }
445