RNViewShotModule.java 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. package fr.greweb.reactnativeviewshot;
  2. import android.content.Context;
  3. import android.graphics.Bitmap;
  4. import android.net.Uri;
  5. import android.os.AsyncTask;
  6. import android.os.Environment;
  7. import android.util.DisplayMetrics;
  8. import android.view.View;
  9. import com.facebook.react.bridge.ReactApplicationContext;
  10. import com.facebook.react.bridge.ReactContextBaseJavaModule;
  11. import com.facebook.react.bridge.ReactMethod;
  12. import com.facebook.react.bridge.GuardedAsyncTask;
  13. import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
  14. import com.facebook.react.bridge.Promise;
  15. import com.facebook.react.bridge.ReactContext;
  16. import com.facebook.react.bridge.ReadableMap;
  17. import com.facebook.react.uimanager.UIBlock;
  18. import com.facebook.react.uimanager.UIManagerModule;
  19. import java.io.File;
  20. import java.io.FilenameFilter;
  21. import java.io.IOException;
  22. import java.util.Collections;
  23. import java.util.HashMap;
  24. import java.util.Map;
  25. public class RNViewShotModule extends ReactContextBaseJavaModule {
  26. private final ReactApplicationContext reactContext;
  27. public RNViewShotModule(ReactApplicationContext reactContext) {
  28. super(reactContext);
  29. this.reactContext = reactContext;
  30. }
  31. @Override
  32. public String getName() {
  33. return "RNViewShot";
  34. }
  35. @Override
  36. public Map<String, Object> getConstants() {
  37. return Collections.emptyMap();
  38. }
  39. @Override
  40. public void onCatalystInstanceDestroy() {
  41. super.onCatalystInstanceDestroy();
  42. new CleanTask(getReactApplicationContext()).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
  43. }
  44. @ReactMethod
  45. public void releaseCapture(String uri) {
  46. File file = new File(Uri.parse(uri).getPath());
  47. if (!file.exists()) return;
  48. File parent = file.getParentFile();
  49. if (parent.equals(reactContext.getExternalCacheDir()) || parent.equals(reactContext.getCacheDir())) {
  50. file.delete();
  51. }
  52. }
  53. @ReactMethod
  54. public void captureRef(int tag, ReadableMap options, Promise promise) {
  55. ReactApplicationContext context = getReactApplicationContext();
  56. String format = options.getString("format");
  57. Bitmap.CompressFormat compressFormat =
  58. format.equals("jpg")
  59. ? Bitmap.CompressFormat.JPEG
  60. : format.equals("webm")
  61. ? Bitmap.CompressFormat.WEBP
  62. : Bitmap.CompressFormat.PNG;
  63. double quality = options.getDouble("quality");
  64. DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
  65. Integer width = options.hasKey("width") ? (int)(displayMetrics.density * options.getDouble("width")) : null;
  66. Integer height = options.hasKey("height") ? (int)(displayMetrics.density * options.getDouble("height")) : null;
  67. String result = options.getString("result");
  68. Boolean snapshotContentContainer = options.getBoolean("snapshotContentContainer");
  69. try {
  70. File file = null;
  71. if ("tmpfile".equals(result)) {
  72. file = createTempFile(getReactApplicationContext(), format);
  73. }
  74. UIManagerModule uiManager = this.reactContext.getNativeModule(UIManagerModule.class);
  75. uiManager.addUIBlock(new ViewShot(tag, format, compressFormat, quality, width, height, file, result, snapshotContentContainer,reactContext, promise));
  76. }
  77. catch (Exception e) {
  78. promise.reject(ViewShot.ERROR_UNABLE_TO_SNAPSHOT, "Failed to snapshot view tag "+tag);
  79. }
  80. }
  81. private static final String TEMP_FILE_PREFIX = "ReactNative-snapshot-image";
  82. /**
  83. * Asynchronous task that cleans up cache dirs (internal and, if available, external) of cropped
  84. * image files. This is run when the catalyst instance is being destroyed (i.e. app is shutting
  85. * down) and when the module is instantiated, to handle the case where the app crashed.
  86. */
  87. private static class CleanTask extends GuardedAsyncTask<Void, Void> {
  88. private final Context mContext;
  89. private CleanTask(ReactContext context) {
  90. super(context);
  91. mContext = context;
  92. }
  93. @Override
  94. protected void doInBackgroundGuarded(Void... params) {
  95. cleanDirectory(mContext.getCacheDir());
  96. File externalCacheDir = mContext.getExternalCacheDir();
  97. if (externalCacheDir != null) {
  98. cleanDirectory(externalCacheDir);
  99. }
  100. }
  101. private void cleanDirectory(File directory) {
  102. File[] toDelete = directory.listFiles(
  103. new FilenameFilter() {
  104. @Override
  105. public boolean accept(File dir, String filename) {
  106. return filename.startsWith(TEMP_FILE_PREFIX);
  107. }
  108. });
  109. if (toDelete != null) {
  110. for (File file: toDelete) {
  111. file.delete();
  112. }
  113. }
  114. }
  115. }
  116. /**
  117. * Create a temporary file in the cache directory on either internal or external storage,
  118. * whichever is available and has more free space.
  119. */
  120. private File createTempFile(Context context, String ext)
  121. throws IOException {
  122. File externalCacheDir = context.getExternalCacheDir();
  123. File internalCacheDir = context.getCacheDir();
  124. File cacheDir;
  125. if (externalCacheDir == null && internalCacheDir == null) {
  126. throw new IOException("No cache directory available");
  127. }
  128. if (externalCacheDir == null) {
  129. cacheDir = internalCacheDir;
  130. }
  131. else if (internalCacheDir == null) {
  132. cacheDir = externalCacheDir;
  133. } else {
  134. cacheDir = externalCacheDir.getFreeSpace() > internalCacheDir.getFreeSpace() ?
  135. externalCacheDir : internalCacheDir;
  136. }
  137. String suffix = "." + ext;
  138. File tmpFile = File.createTempFile(TEMP_FILE_PREFIX, suffix, cacheDir);
  139. return tmpFile;
  140. }
  141. }