|  | @@ -1,6 +1,7 @@
 | 
	
		
			
			| 1 | 1 |  package fr.greweb.reactnativeviewshot;
 | 
	
		
			
			| 2 | 2 |  
 | 
	
		
			
			| 3 | 3 |  import javax.annotation.Nullable;
 | 
	
		
			
			|  | 4 | +
 | 
	
		
			
			| 4 | 5 |  import android.graphics.Bitmap;
 | 
	
		
			
			| 5 | 6 |  import android.graphics.Canvas;
 | 
	
		
			
			| 6 | 7 |  import android.net.Uri;
 | 
	
	
		
			
			|  | @@ -16,6 +17,8 @@ import java.io.File;
 | 
	
		
			
			| 16 | 17 |  import java.io.FileOutputStream;
 | 
	
		
			
			| 17 | 18 |  import java.io.IOException;
 | 
	
		
			
			| 18 | 19 |  import java.io.OutputStream;
 | 
	
		
			
			|  | 20 | +import java.util.concurrent.ExecutorService;
 | 
	
		
			
			|  | 21 | +import java.util.concurrent.Executors;
 | 
	
		
			
			| 19 | 22 |  
 | 
	
		
			
			| 20 | 23 |  /**
 | 
	
		
			
			| 21 | 24 |   * Snapshot utility class allow to screenshot a view.
 | 
	
	
		
			
			|  | @@ -24,6 +27,8 @@ public class ViewShot implements UIBlock {
 | 
	
		
			
			| 24 | 27 |  
 | 
	
		
			
			| 25 | 28 |      static final String ERROR_UNABLE_TO_SNAPSHOT = "E_UNABLE_TO_SNAPSHOT";
 | 
	
		
			
			| 26 | 29 |  
 | 
	
		
			
			|  | 30 | +    private final static ExecutorService sExecutor = Executors.newFixedThreadPool(5);
 | 
	
		
			
			|  | 31 | +
 | 
	
		
			
			| 27 | 32 |      private int tag;
 | 
	
		
			
			| 28 | 33 |      private String extension;
 | 
	
		
			
			| 29 | 34 |      private Bitmap.CompressFormat format;
 | 
	
	
		
			
			|  | @@ -57,59 +62,63 @@ public class ViewShot implements UIBlock {
 | 
	
		
			
			| 57 | 62 |  
 | 
	
		
			
			| 58 | 63 |      @Override
 | 
	
		
			
			| 59 | 64 |      public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) {
 | 
	
		
			
			| 60 |  | -        OutputStream os = null;
 | 
	
		
			
			| 61 | 65 |          View view = nativeViewHierarchyManager.resolveView(tag);
 | 
	
		
			
			| 62 | 66 |          if (view == null) {
 | 
	
		
			
			| 63 |  | -            promise.reject(ERROR_UNABLE_TO_SNAPSHOT, "No view found with reactTag: "+tag);
 | 
	
		
			
			|  | 67 | +            promise.reject(ERROR_UNABLE_TO_SNAPSHOT, "No view found with reactTag: " + tag);
 | 
	
		
			
			| 64 | 68 |              return;
 | 
	
		
			
			| 65 | 69 |          }
 | 
	
		
			
			| 66 |  | -        try {
 | 
	
		
			
			| 67 |  | -            if ("file".equals(result)) {
 | 
	
		
			
			| 68 |  | -                os = new FileOutputStream(output);
 | 
	
		
			
			| 69 |  | -                captureView(view, os);
 | 
	
		
			
			| 70 |  | -                String uri = Uri.fromFile(output).toString();
 | 
	
		
			
			| 71 |  | -                promise.resolve(uri);
 | 
	
		
			
			| 72 |  | -            }
 | 
	
		
			
			| 73 |  | -            else if ("base64".equals(result)) {
 | 
	
		
			
			| 74 |  | -                os = new ByteArrayOutputStream();
 | 
	
		
			
			| 75 |  | -                captureView(view, os);
 | 
	
		
			
			| 76 |  | -                byte[] bytes = ((ByteArrayOutputStream) os).toByteArray();
 | 
	
		
			
			| 77 |  | -                String data = Base64.encodeToString(bytes, Base64.NO_WRAP);
 | 
	
		
			
			| 78 |  | -                promise.resolve(data);
 | 
	
		
			
			| 79 |  | -            }
 | 
	
		
			
			| 80 |  | -            else if ("data-uri".equals(result)) {
 | 
	
		
			
			| 81 |  | -                os = new ByteArrayOutputStream();
 | 
	
		
			
			| 82 |  | -                captureView(view, os);
 | 
	
		
			
			| 83 |  | -                byte[] bytes = ((ByteArrayOutputStream) os).toByteArray();
 | 
	
		
			
			| 84 |  | -                String data = Base64.encodeToString(bytes, Base64.NO_WRAP);
 | 
	
		
			
			| 85 |  | -                data = "data:image/"+extension+";base64," + data;
 | 
	
		
			
			| 86 |  | -                promise.resolve(data);
 | 
	
		
			
			| 87 |  | -            }
 | 
	
		
			
			| 88 |  | -            else {
 | 
	
		
			
			| 89 |  | -                promise.reject(ERROR_UNABLE_TO_SNAPSHOT, "Unsupported result: "+result+". Try one of: file | base64 | data-uri");
 | 
	
		
			
			| 90 |  | -            }
 | 
	
		
			
			| 91 |  | -        }
 | 
	
		
			
			| 92 |  | -        catch (Exception e) {
 | 
	
		
			
			| 93 |  | -            e.printStackTrace();
 | 
	
		
			
			| 94 |  | -            promise.reject(ERROR_UNABLE_TO_SNAPSHOT, "Failed to capture view snapshot");
 | 
	
		
			
			| 95 |  | -        }
 | 
	
		
			
			| 96 |  | -        finally {
 | 
	
		
			
			| 97 |  | -            if (os != null) {
 | 
	
		
			
			|  | 70 | +        final Bitmap bitmap = captureView(view);
 | 
	
		
			
			|  | 71 | +        sExecutor.execute(new Runnable() {
 | 
	
		
			
			|  | 72 | +            @Override
 | 
	
		
			
			|  | 73 | +            public void run() {
 | 
	
		
			
			|  | 74 | +                OutputStream os = null;
 | 
	
		
			
			| 98 | 75 |                  try {
 | 
	
		
			
			| 99 |  | -                    os.close();
 | 
	
		
			
			| 100 |  | -                } catch (IOException e) {
 | 
	
		
			
			|  | 76 | +                    if ("file".equals(result)) {
 | 
	
		
			
			|  | 77 | +                        os = new FileOutputStream(output);
 | 
	
		
			
			|  | 78 | +                        String uri = Uri.fromFile(output).toString();
 | 
	
		
			
			|  | 79 | +                        promise.resolve(uri);
 | 
	
		
			
			|  | 80 | +                    } else if ("base64".equals(result)) {
 | 
	
		
			
			|  | 81 | +                        os = new ByteArrayOutputStream();
 | 
	
		
			
			|  | 82 | +                        convertImageToData(bitmap, os);
 | 
	
		
			
			|  | 83 | +                        byte[] bytes = ((ByteArrayOutputStream) os).toByteArray();
 | 
	
		
			
			|  | 84 | +                        String data = Base64.encodeToString(bytes, Base64.NO_WRAP);
 | 
	
		
			
			|  | 85 | +                        promise.resolve(data);
 | 
	
		
			
			|  | 86 | +                    } else if ("data-uri".equals(result)) {
 | 
	
		
			
			|  | 87 | +                        os = new ByteArrayOutputStream();
 | 
	
		
			
			|  | 88 | +                        convertImageToData(bitmap, os);
 | 
	
		
			
			|  | 89 | +                        byte[] bytes = ((ByteArrayOutputStream) os).toByteArray();
 | 
	
		
			
			|  | 90 | +                        String data = Base64.encodeToString(bytes, Base64.NO_WRAP);
 | 
	
		
			
			|  | 91 | +                        data = "data:image/" + extension + ";base64," + data;
 | 
	
		
			
			|  | 92 | +                        promise.resolve(data);
 | 
	
		
			
			|  | 93 | +                    } else {
 | 
	
		
			
			|  | 94 | +                        promise.reject(ERROR_UNABLE_TO_SNAPSHOT, "Unsupported result: " + result +
 | 
	
		
			
			|  | 95 | +                                ". Try one of: file | base64 | data-uri");
 | 
	
		
			
			|  | 96 | +                    }
 | 
	
		
			
			|  | 97 | +                } catch (Exception e) {
 | 
	
		
			
			| 101 | 98 |                      e.printStackTrace();
 | 
	
		
			
			|  | 99 | +                    promise.reject(ERROR_UNABLE_TO_SNAPSHOT, "Failed to capture view snapshot");
 | 
	
		
			
			|  | 100 | +                } finally {
 | 
	
		
			
			|  | 101 | +                    if (os != null) {
 | 
	
		
			
			|  | 102 | +                        try {
 | 
	
		
			
			|  | 103 | +                            os.close();
 | 
	
		
			
			|  | 104 | +                        } catch (IOException e) {
 | 
	
		
			
			|  | 105 | +                            e.printStackTrace();
 | 
	
		
			
			|  | 106 | +                        }
 | 
	
		
			
			|  | 107 | +                    }
 | 
	
		
			
			| 102 | 108 |                  }
 | 
	
		
			
			| 103 | 109 |              }
 | 
	
		
			
			| 104 |  | -        }
 | 
	
		
			
			|  | 110 | +        });
 | 
	
		
			
			|  | 111 | +
 | 
	
		
			
			|  | 112 | +
 | 
	
		
			
			| 105 | 113 |      }
 | 
	
		
			
			| 106 | 114 |  
 | 
	
		
			
			| 107 | 115 |      /**
 | 
	
		
			
			| 108 | 116 |       * Screenshot a view and return the captured bitmap.
 | 
	
		
			
			|  | 117 | +     *
 | 
	
		
			
			| 109 | 118 |       * @param view the view to capture
 | 
	
		
			
			| 110 |  | -     * @return the screenshot or null if it failed.
 | 
	
		
			
			|  | 119 | +     * @return bitmap drawn by view
 | 
	
		
			
			| 111 | 120 |       */
 | 
	
		
			
			| 112 |  | -    private void captureView (View view, OutputStream os) {
 | 
	
		
			
			|  | 121 | +    private Bitmap captureView(View view) {
 | 
	
		
			
			| 113 | 122 |          int w = view.getWidth();
 | 
	
		
			
			| 114 | 123 |          int h = view.getHeight();
 | 
	
		
			
			| 115 | 124 |          if (w <= 0 || h <= 0) {
 | 
	
	
		
			
			|  | @@ -125,6 +134,17 @@ public class ViewShot implements UIBlock {
 | 
	
		
			
			| 125 | 134 |          if (bitmap == null) {
 | 
	
		
			
			| 126 | 135 |              throw new RuntimeException("Impossible to snapshot the view");
 | 
	
		
			
			| 127 | 136 |          }
 | 
	
		
			
			| 128 |  | -        bitmap.compress(format, (int)(100.0 * quality), os);
 | 
	
		
			
			|  | 137 | +        return bitmap;
 | 
	
		
			
			|  | 138 | +    }
 | 
	
		
			
			|  | 139 | +
 | 
	
		
			
			|  | 140 | +    /**
 | 
	
		
			
			|  | 141 | +     * As Bitmap.compress() may take a long time, it's better to
 | 
	
		
			
			|  | 142 | +     * call it in a separate thread
 | 
	
		
			
			|  | 143 | +     *
 | 
	
		
			
			|  | 144 | +     * @param bitmap image to convert
 | 
	
		
			
			|  | 145 | +     * @param os     output stream
 | 
	
		
			
			|  | 146 | +     */
 | 
	
		
			
			|  | 147 | +    private void convertImageToData(Bitmap bitmap, OutputStream os) {
 | 
	
		
			
			|  | 148 | +        bitmap.compress(format, (int) (100.0 * quality), os);
 | 
	
		
			
			| 129 | 149 |      }
 | 
	
		
			
			| 130 | 150 |  }
 |