Working with Other Components in an Extension

Good idea @Kumaraswamy

@TIMAI2 does not need Component Image, we can try this:

    @SimpleFunction( description = "Canvas to Base 64")
    public void CanvasToBase64(final Canvas canvas) throws Exception {
        final View canvasView = canvas.getView();

        final Field bitmapField = canvasView.getClass().getDeclaredField("bitmap");
        bitmapField.setAccessible(true);
        final Bitmap bitmap = (Bitmap) bitmapField.get(canvasView);

            String encodedString = bitMapToBase64(bitmap);
            AfterPictureBase64(encodedString);
    }



  public String bitMapToBase64(Bitmap bitMap) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        bitMap.compress(Bitmap.CompressFormat.PNG, 50, byteArrayOutputStream);
        byte[] byteArray = byteArrayOutputStream.toByteArray();
        return Base64.encodeToString(byteArray, Base64.DEFAULT);
    }

    @SimpleEvent(description = "Called after the picture is taken. The text argument `base64Data` is the base64 string of the image")
    public void AfterPictureBase64(String base64Data) {
        EventDispatcher.dispatchEvent(this, "AfterPictureBase64", base64Data);
    }
2 Likes

Thank you clever people :smiley:

I will try both and report back.

@Juan_Antonio - how do we indicate which canvas will return its bitmap ? (There could be more than one in the app....)

1 Like

canvass1

We are nearly there....

My tests will return drawn items (text,circles etc) but neither the background colour or a background image.....(I place the base64 output to another canvas as a background image)

@SimpleFunction(
          description = "Get Canvas Image and returns base64 string")
  public void GetCanvasImageAsBase64(final Canvas canvas) throws Exception {
    final View canvasView = canvas.getView();
    final Field bitmapField = canvasView.getClass().getDeclaredField("bitmap");
    bitmapField.setAccessible(true);
    final Bitmap imageBitmap = (Bitmap) bitmapField.get(canvasView);
    String encodedString = bitMapToBase64(imageBitmap);
    AfterCanvasBase64(encodedString);
  }


image

Having built and tested I now see a socket is provided :upside_down_face:

borrar_camara.aia (9.9 KB)

Crash :flushed: when BackgroundBase64.

Edit: It works but without background.

I guess the bitmap field which we are accessing is just the top layer for the Canvas.

Try this

	 @SimpleFunction( description = "Canvas to Base 64.")
    public void CanvasToBase64(final Canvas canvas) throws Exception {
        final View canvasView = canvas.getView();
		final Bitmap bitmap = loadBitmapFromView(canvasView);
     //   final Field bitmapField = canvasView.getClass().getDeclaredField("bitmap");
     //   bitmapField.setAccessible(true);
    //    final Bitmap bitmap = (Bitmap) bitmapField.get(canvasView);

            String encodedString = bitMapToBase64(bitmap);
            AfterPictureBase64(encodedString);
    }
	
	
public static Bitmap loadBitmapFromView(View v) {
    Bitmap bitmap;
    v.setDrawingCacheEnabled(true);
    bitmap = Bitmap.createBitmap(v.getDrawingCache());
    v.setDrawingCacheEnabled(false);
    return bitmap;
}

Unfortunately, setDrawingCacheEnabled and getDrawingCache are deprecated after API 28 :frowning:

I have been looking at this example:

public static Bitmap loadBitmapFromView(View v) {
    Bitmap b = Bitmap.createBitmap( v.getLayoutParams().width, v.getLayoutParams().height, Bitmap.Config.ARGB_8888);                
    Canvas c = new Canvas(b);
    v.layout(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
    v.draw(c);
    return b;
}

or this

public static Bitmap getBitmapFromView(View view) {
    Bitmap returnedBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(returnedBitmap);
    Drawable bgDrawable =view.getBackground();
    if (bgDrawable!=null) 
        bgDrawable.draw(canvas);
    else 
        canvas.drawColor(Color.WHITE);
    view.draw(canvas);
    return returnedBitmap;
}

If we have a second canvas (doesn't look like we can create one)......might cause a blink on screen...but need to link the second canvas in to the function.

Try this D:

@SimpleFunction(description = "Transfer data from Canvas " +
                    "to an Image Component!")
    public void TransferToImage(final Canvas canvas, final Image image) throws Exception {
        final View canvasView = canvas.getView();
        final Method method = canvasView.getClass()
                .getDeclaredMethod("buildCache");
        method.setAccessible(true);
        Bitmap bitmap = (Bitmap) method.invoke(canvasView);

        ((ImageView) image.getView()).setImageBitmap(bitmap);
    }

I tried by setting the background of the canvas with color and the image.

3 Likes

@Kumaraswamy Your code works for me on Android 9.

@SimpleFunction(description = "Transfer data from Canvas to an Image Component!")
    public void TransferToImage(final Canvas canvas) throws Exception {
        final View canvasView = canvas.getView();
        final Method method = canvasView.getClass().getDeclaredMethod("buildCache");
        method.setAccessible(true);
        Bitmap bitmap = (Bitmap) method.invoke(canvasView);

      //  ((ImageView) image.getView()).setImageBitmap(bitmap);
		
		String encodedString = bitMapToBase64(bitmap);
        AfterPictureBase64(encodedString);
    }
2 Likes

BRILLIANT !! @Kumaraswamy

It works !! (tested in companion on Android 10 and 11)

Many thanks

thumbsup2

2 Likes

If you are in the mood anytime, do the same thing for an image ?

Get the bitmap of an image on an image component.

(we can then convert this to a base64 string)

I have tried many things, the SO advice is either for deprecated methods or just doesn't seem to work in AppInventor....

and also perhaps going the other way:

Set an image component with a bitmap, instead of a path to a file.

(this could be done by getting the base64 string for an image, converting it to a bitmap, then setting the bitmap to the image component ?)

If you want to get the bitmap from an Image component then use getView method on it that will return the ImageView, if there is a function named getBitmapDrawable (BitmapDrawable) and finally use getBitmap on that.

You can try:

Bitmap bmp = ((BitmapDrawable)imageView.getDrawable()).getBitmap();
1 Like

Yes, one of the things I tried.

Not available in the IDE drawer (intelliJ), so throws an error.

image

I have

import android.graphics.Bitmap;
import android.graphics.drawable.*;

Should I just ignore and proceed ?

This is what I have so far (not working)

   @SimpleFunction(description = "Get Image and returns base64 string")
    public void GetImageAsBase64(final Image image ) throws Exception {

      final View imageView = image.getView();
      Bitmap imageBitmap = ((BitmapDrawable)imageView.getDrawable()).getBitmap();

      String encodedString = bitMapToBase64(imageBitmap);    
    }

    public String bitMapToBase64(Bitmap bitMap) {
      ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
      bitMap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
      byte[] byteArray = byteArrayOutputStream.toByteArray();
      return Base64.encodeToString(byteArray, Base64.DEFAULT);
    }

Edit: Its actually because you are using View instead of the Image View.

So when you access the Image.getView method, you additionally have to cast it to its ImageView class. The getDrawable method is only on ImageView.

Something like this

final ImageView imageView = (ImageView) imageComponent.getView();
final Bitmap bitmap1 = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
1 Like

That just gives me more unresolvables :frowning:

image

For the first and second errors resulted in by theImageView symbol, it appears the android.view.ImageView class wasn't imported, importing it should get it fixed.
For the second error, i think the imageComponent variable in @Kumaraswamy's code is equivalent to the image variable in your code, renaming imageComponent to image should work.
The fourth error should be resolved by applying the first fix.

2 Likes