|
@@ -1,35 +1,36 @@
|
1
|
1
|
|
2
|
2
|
package fr.greweb.reactnativeviewshot;
|
3
|
3
|
|
|
4
|
+import android.app.Activity;
|
4
|
5
|
import android.content.Context;
|
5
|
|
-import android.graphics.Bitmap;
|
6
|
6
|
import android.net.Uri;
|
7
|
7
|
import android.os.AsyncTask;
|
8
|
|
-import android.os.Environment;
|
|
8
|
+import android.support.annotation.NonNull;
|
9
|
9
|
import android.util.DisplayMetrics;
|
10
|
|
-import android.view.View;
|
11
|
|
-
|
12
|
|
-import com.facebook.react.bridge.ReactApplicationContext;
|
13
|
|
-import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
14
|
|
-import com.facebook.react.bridge.ReactMethod;
|
|
10
|
+import android.util.Log;
|
15
|
11
|
|
16
|
12
|
import com.facebook.react.bridge.GuardedAsyncTask;
|
17
|
|
-import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
|
18
|
13
|
import com.facebook.react.bridge.Promise;
|
|
14
|
+import com.facebook.react.bridge.ReactApplicationContext;
|
19
|
15
|
import com.facebook.react.bridge.ReactContext;
|
|
16
|
+import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
17
|
+import com.facebook.react.bridge.ReactMethod;
|
20
|
18
|
import com.facebook.react.bridge.ReadableMap;
|
21
|
|
-import com.facebook.react.uimanager.UIBlock;
|
22
|
19
|
import com.facebook.react.uimanager.UIManagerModule;
|
23
|
20
|
|
24
|
21
|
import java.io.File;
|
25
|
22
|
import java.io.FilenameFilter;
|
26
|
23
|
import java.io.IOException;
|
27
|
24
|
import java.util.Collections;
|
28
|
|
-import java.util.HashMap;
|
29
|
25
|
import java.util.Map;
|
30
|
26
|
|
|
27
|
+import fr.greweb.reactnativeviewshot.ViewShot.Formats;
|
|
28
|
+import fr.greweb.reactnativeviewshot.ViewShot.Results;
|
|
29
|
+
|
31
|
30
|
public class RNViewShotModule extends ReactContextBaseJavaModule {
|
32
|
31
|
|
|
32
|
+ public static final String RNVIEW_SHOT = "RNViewShot";
|
|
33
|
+
|
33
|
34
|
private final ReactApplicationContext reactContext;
|
34
|
35
|
|
35
|
36
|
public RNViewShotModule(ReactApplicationContext reactContext) {
|
|
@@ -39,7 +40,7 @@ public class RNViewShotModule extends ReactContextBaseJavaModule {
|
39
|
40
|
|
40
|
41
|
@Override
|
41
|
42
|
public String getName() {
|
42
|
|
- return "RNViewShot";
|
|
43
|
+ return RNVIEW_SHOT;
|
43
|
44
|
}
|
44
|
45
|
|
45
|
46
|
@Override
|
|
@@ -67,30 +68,40 @@ public class RNViewShotModule extends ReactContextBaseJavaModule {
|
67
|
68
|
|
68
|
69
|
@ReactMethod
|
69
|
70
|
public void captureRef(int tag, ReadableMap options, Promise promise) {
|
70
|
|
- ReactApplicationContext context = getReactApplicationContext();
|
71
|
|
- String format = options.getString("format");
|
72
|
|
- Bitmap.CompressFormat compressFormat =
|
73
|
|
- format.equals("jpg")
|
74
|
|
- ? Bitmap.CompressFormat.JPEG
|
75
|
|
- : format.equals("webm")
|
76
|
|
- ? Bitmap.CompressFormat.WEBP
|
77
|
|
- : Bitmap.CompressFormat.PNG;
|
78
|
|
- double quality = options.getDouble("quality");
|
79
|
|
- DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
|
80
|
|
- Integer width = options.hasKey("width") ? (int)(displayMetrics.density * options.getDouble("width")) : null;
|
81
|
|
- Integer height = options.hasKey("height") ? (int)(displayMetrics.density * options.getDouble("height")) : null;
|
82
|
|
- String result = options.getString("result");
|
83
|
|
- Boolean snapshotContentContainer = options.getBoolean("snapshotContentContainer");
|
|
71
|
+ final ReactApplicationContext context = getReactApplicationContext();
|
|
72
|
+ final DisplayMetrics dm = context.getResources().getDisplayMetrics();
|
|
73
|
+
|
|
74
|
+ final String extension = options.getString("format");
|
|
75
|
+ final int imageFormat = "jpg".equals(extension)
|
|
76
|
+ ? Formats.JPEG
|
|
77
|
+ : "webm".equals(extension)
|
|
78
|
+ ? Formats.WEBP
|
|
79
|
+ : "raw".equals(extension)
|
|
80
|
+ ? Formats.RAW
|
|
81
|
+ : Formats.PNG;
|
|
82
|
+
|
|
83
|
+ final double quality = options.getDouble("quality");
|
|
84
|
+ final Integer scaleWidth = options.hasKey("width") ? (int) (dm.density * options.getDouble("width")) : null;
|
|
85
|
+ final Integer scaleHeight = options.hasKey("height") ? (int) (dm.density * options.getDouble("height")) : null;
|
|
86
|
+ final String resultStreamFormat = options.getString("result");
|
|
87
|
+ final Boolean snapshotContentContainer = options.getBoolean("snapshotContentContainer");
|
|
88
|
+
|
84
|
89
|
try {
|
85
|
|
- File file = null;
|
86
|
|
- if ("tmpfile".equals(result)) {
|
87
|
|
- file = createTempFile(getReactApplicationContext(), format);
|
|
90
|
+ File outputFile = null;
|
|
91
|
+ if (Results.TEMP_FILE.equals(resultStreamFormat)) {
|
|
92
|
+ outputFile = createTempFile(getReactApplicationContext(), extension);
|
88
|
93
|
}
|
89
|
|
- UIManagerModule uiManager = this.reactContext.getNativeModule(UIManagerModule.class);
|
90
|
|
- uiManager.addUIBlock(new ViewShot(tag, format, compressFormat, quality, width, height, file, result, snapshotContentContainer,reactContext, getCurrentActivity(), promise));
|
91
|
|
- }
|
92
|
|
- catch (Exception e) {
|
93
|
|
- promise.reject(ViewShot.ERROR_UNABLE_TO_SNAPSHOT, "Failed to snapshot view tag "+tag);
|
|
94
|
+
|
|
95
|
+ final Activity activity = getCurrentActivity();
|
|
96
|
+ final UIManagerModule uiManager = this.reactContext.getNativeModule(UIManagerModule.class);
|
|
97
|
+
|
|
98
|
+ uiManager.addUIBlock(new ViewShot(
|
|
99
|
+ tag, extension, imageFormat, quality,
|
|
100
|
+ scaleWidth, scaleHeight, outputFile, resultStreamFormat,
|
|
101
|
+ snapshotContentContainer, reactContext, activity, promise)
|
|
102
|
+ );
|
|
103
|
+ } catch (final Throwable ignored) {
|
|
104
|
+ promise.reject(ViewShot.ERROR_UNABLE_TO_SNAPSHOT, "Failed to snapshot view tag " + tag);
|
94
|
105
|
}
|
95
|
106
|
}
|
96
|
107
|
|
|
@@ -106,34 +117,41 @@ public class RNViewShotModule extends ReactContextBaseJavaModule {
|
106
|
117
|
* image files. This is run when the catalyst instance is being destroyed (i.e. app is shutting
|
107
|
118
|
* down) and when the module is instantiated, to handle the case where the app crashed.
|
108
|
119
|
*/
|
109
|
|
- private static class CleanTask extends GuardedAsyncTask<Void, Void> {
|
110
|
|
- private final Context mContext;
|
|
120
|
+ private static class CleanTask extends GuardedAsyncTask<Void, Void> implements FilenameFilter {
|
|
121
|
+ private final File cacheDir;
|
|
122
|
+ private final File externalCacheDir;
|
111
|
123
|
|
112
|
124
|
private CleanTask(ReactContext context) {
|
113
|
125
|
super(context);
|
114
|
|
- mContext = context;
|
|
126
|
+
|
|
127
|
+ cacheDir = context.getCacheDir();
|
|
128
|
+ externalCacheDir = context.getExternalCacheDir();
|
115
|
129
|
}
|
116
|
130
|
|
117
|
131
|
@Override
|
118
|
132
|
protected void doInBackgroundGuarded(Void... params) {
|
119
|
|
- cleanDirectory(mContext.getCacheDir());
|
120
|
|
- File externalCacheDir = mContext.getExternalCacheDir();
|
|
133
|
+ if (null != cacheDir) {
|
|
134
|
+ cleanDirectory(cacheDir);
|
|
135
|
+ }
|
|
136
|
+
|
121
|
137
|
if (externalCacheDir != null) {
|
122
|
138
|
cleanDirectory(externalCacheDir);
|
123
|
139
|
}
|
124
|
140
|
}
|
125
|
141
|
|
126
|
|
- private void cleanDirectory(File directory) {
|
127
|
|
- File[] toDelete = directory.listFiles(
|
128
|
|
- new FilenameFilter() {
|
129
|
|
- @Override
|
130
|
|
- public boolean accept(File dir, String filename) {
|
131
|
|
- return filename.startsWith(TEMP_FILE_PREFIX);
|
132
|
|
- }
|
133
|
|
- });
|
|
142
|
+ @Override
|
|
143
|
+ public final boolean accept(File dir, String filename) {
|
|
144
|
+ return filename.startsWith(TEMP_FILE_PREFIX);
|
|
145
|
+ }
|
|
146
|
+
|
|
147
|
+ private void cleanDirectory(@NonNull final File directory) {
|
|
148
|
+ final File[] toDelete = directory.listFiles(this);
|
|
149
|
+
|
134
|
150
|
if (toDelete != null) {
|
135
|
|
- for (File file: toDelete) {
|
136
|
|
- file.delete();
|
|
151
|
+ for (File file : toDelete) {
|
|
152
|
+ if (file.delete()) {
|
|
153
|
+ Log.d(RNVIEW_SHOT, "deleted file: " + file.getAbsolutePath());
|
|
154
|
+ }
|
137
|
155
|
}
|
138
|
156
|
}
|
139
|
157
|
}
|
|
@@ -143,26 +161,26 @@ public class RNViewShotModule extends ReactContextBaseJavaModule {
|
143
|
161
|
* Create a temporary file in the cache directory on either internal or external storage,
|
144
|
162
|
* whichever is available and has more free space.
|
145
|
163
|
*/
|
146
|
|
- private File createTempFile(Context context, String ext)
|
147
|
|
- throws IOException {
|
148
|
|
- File externalCacheDir = context.getExternalCacheDir();
|
149
|
|
- File internalCacheDir = context.getCacheDir();
|
150
|
|
- File cacheDir;
|
|
164
|
+ @NonNull
|
|
165
|
+ private File createTempFile(@NonNull final Context context, @NonNull final String ext) throws IOException {
|
|
166
|
+ final File externalCacheDir = context.getExternalCacheDir();
|
|
167
|
+ final File internalCacheDir = context.getCacheDir();
|
|
168
|
+ final File cacheDir;
|
|
169
|
+
|
151
|
170
|
if (externalCacheDir == null && internalCacheDir == null) {
|
152
|
171
|
throw new IOException("No cache directory available");
|
153
|
172
|
}
|
|
173
|
+
|
154
|
174
|
if (externalCacheDir == null) {
|
155
|
175
|
cacheDir = internalCacheDir;
|
156
|
|
- }
|
157
|
|
- else if (internalCacheDir == null) {
|
|
176
|
+ } else if (internalCacheDir == null) {
|
158
|
177
|
cacheDir = externalCacheDir;
|
159
|
178
|
} else {
|
160
|
179
|
cacheDir = externalCacheDir.getFreeSpace() > internalCacheDir.getFreeSpace() ?
|
161
|
180
|
externalCacheDir : internalCacheDir;
|
162
|
181
|
}
|
163
|
|
- String suffix = "." + ext;
|
164
|
|
- File tmpFile = File.createTempFile(TEMP_FILE_PREFIX, suffix, cacheDir);
|
165
|
|
- return tmpFile;
|
166
|
|
- }
|
167
|
182
|
|
|
183
|
+ final String suffix = "." + ext;
|
|
184
|
+ return File.createTempFile(TEMP_FILE_PREFIX, suffix, cacheDir);
|
|
185
|
+ }
|
168
|
186
|
}
|