shakebug-react-native 2.1.0 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -1
- package/VideoPlayer/index.js +37 -23
- package/android/build.gradle +0 -1
- package/android/src/main/java/com/shakebug/CreateThumbnailModule.java +217 -0
- package/android/src/main/java/com/shakebug/NetInfoModule.java +1 -2
- package/android/src/main/java/com/shakebug/RNDeviceModule.java +1 -3
- package/android/src/main/java/com/shakebug/ShakebugPackage.java +10 -10
- package/android/src/main/java/com/shakebug/{Shakebug.java → ShakebugSensors.java} +16 -22
- package/ios/GetLocals/ShakebugSDKRNLocalize.h +1 -0
- package/ios/Thumbnaill/{VideoThumbnailManager.h → CreateThumbnail.h} +2 -1
- package/ios/Thumbnaill/CreateThumbnail.m +82 -0
- package/package.json +2 -5
- package/shakebug-react-native.podspec +0 -1
- package/src/APIs/AddIssue.js +13 -4
- package/src/APIs/AddReview.js +46 -0
- package/src/APIs/CustomUserId.js +47 -0
- package/src/APIs/Event.js +8 -3
- package/src/APIs/StartSession.js +6 -2
- package/src/Assets/images/solar_star-bold.png +0 -0
- package/src/Assets/images/solar_star-line-duotone.png +0 -0
- package/src/Componanats/ReviewModal.js +171 -0
- package/src/Componanats/ShakeFunction.js +6 -3
- package/src/Screens/AddDetailsScreen.js +14 -10
- package/src/Screens/FirstScreen.js +2 -2
- package/src/Screens/Shakebug.js +63 -6
- package/src/Screens/TrimVideoScreen.js +13 -7
- package/src/index.tsx +6 -2
- package/android/src/main/java/com/shakebug/ShakebugSDKScreenShotModule.java +0 -196
- package/android/src/main/java/com/shakebug/VideoPlayerModule.java +0 -199
- package/android/src/main/java/com/shakebug/VideoThumbnailModule.java +0 -215
- package/android/src/main/java/com/shakebug/VideoTrimModule.java +0 -372
- package/android/src/main/java/com/shakebug/adapters/VideoTrimmerAdapter.java +0 -54
- package/android/src/main/java/com/shakebug/utils/StorageUtil.java +0 -139
- package/android/src/main/java/com/shakebug/utils/VideoTrimmerUtil.java +0 -89
- package/android/src/main/java/com/shakebug/widgets/RangeSeekBarView.java +0 -534
- package/android/src/main/java/com/shakebug/widgets/VideoPlayerView.java +0 -429
- package/android/src/main/java/com/shakebug/widgets/VideoTrimmerView.java +0 -477
- package/ios/Thumbnaill/VideoThumbnailManager.m +0 -73
- package/ios/TrimVideo/ShakebugSDKVideoTrim.swift +0 -372
- package/ios/TrimVideo/VideoTrim-Bridging-Header.h +0 -10
- package/ios/TrimVideo/VideoTrim.m +0 -23
- package/ios/TrimVideo/VideoTrimmer.swift +0 -810
- package/ios/TrimVideo/VideoTrimmerThumb.swift +0 -119
- package/ios/TrimVideo/VideoTrimmerViewController.swift +0 -301
package/README.md
CHANGED
|
@@ -23,6 +23,18 @@ Or if you prefer to use Yarn instead of npm:
|
|
|
23
23
|
yarn add shakebug-react-native
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
+
Make sure to install Required Libraries also
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm i react-native-view-shot react-native-video react-native-video-trim
|
|
30
|
+
```
|
|
31
|
+
Or if you prefer to use Yarn instead of npm:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
yarn add react-native-view-shot react-native-video react-native-video-trim
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
|
|
26
38
|
### iOS
|
|
27
39
|
|
|
28
40
|
1. You need to provide permissions inside `ios/YourProjectName/info.plist`:
|
|
@@ -37,8 +49,12 @@ Or if you prefer to use Yarn instead of npm:
|
|
|
37
49
|
<key>NSPhotoLibraryUsageDescription</key>
|
|
38
50
|
<string></string>
|
|
39
51
|
```
|
|
52
|
+
2. Manage Architecture:
|
|
53
|
+
```bash
|
|
54
|
+
ENV['RCT_NEW_ARCH_ENABLED'] = '0'
|
|
55
|
+
```
|
|
40
56
|
|
|
41
|
-
|
|
57
|
+
3. CocoaPods installation:
|
|
42
58
|
|
|
43
59
|
```bash
|
|
44
60
|
cd ios && pod install && cd ..
|
package/VideoPlayer/index.js
CHANGED
|
@@ -1,35 +1,38 @@
|
|
|
1
|
-
import React, { useEffect } from 'react';
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import { NativeModules, Image, TouchableOpacity, ImageBackground, NativeEventEmitter, Platform, StatusBar } from 'react-native';
|
|
3
3
|
import { PlayVideo } from './PlayVideo';
|
|
4
|
+
import Video from 'react-native-video';
|
|
4
5
|
|
|
5
6
|
const VideoPlayer = ({ videoUri, img, goToScreenAtIndex, UpdateStatusBar }) => {
|
|
6
|
-
useEffect(() => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
7
|
+
// useEffect(() => {
|
|
8
|
+
// if (Platform.OS == 'android') {
|
|
9
|
+
// const eventEmitter = new NativeEventEmitter(NativeModules.VideoPlayerModule);
|
|
10
|
+
// const subscription = eventEmitter.addListener('ShakebugSDKVideoPlayer', (event) => {
|
|
11
|
+
// switch (event.name) {
|
|
12
|
+
// case 'onCancelPlayer': {
|
|
13
|
+
// UpdateStatusBar()
|
|
14
|
+
// goToScreenAtIndex(3)
|
|
15
|
+
// break;
|
|
16
|
+
// }
|
|
17
|
+
// default:
|
|
18
|
+
// break;
|
|
19
|
+
// }
|
|
20
|
+
// });
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}, []);
|
|
22
|
+
// return () => {
|
|
23
|
+
// subscription.remove();
|
|
24
|
+
// };
|
|
25
|
+
// }
|
|
26
|
+
// }, []);
|
|
27
|
+
|
|
28
|
+
const [isPlayVideo, setIsPlayVideo] = useState(false);
|
|
26
29
|
|
|
27
30
|
const playVideo = () => {
|
|
28
|
-
|
|
29
|
-
PlayVideo(videoUri)
|
|
31
|
+
setIsPlayVideo(true)
|
|
30
32
|
}
|
|
31
|
-
|
|
33
|
+
|
|
32
34
|
return (
|
|
35
|
+
!isPlayVideo ? (
|
|
33
36
|
<ImageBackground
|
|
34
37
|
source={{ uri: img }}
|
|
35
38
|
style={{
|
|
@@ -63,6 +66,17 @@ const VideoPlayer = ({ videoUri, img, goToScreenAtIndex, UpdateStatusBar }) => {
|
|
|
63
66
|
/>
|
|
64
67
|
</TouchableOpacity>
|
|
65
68
|
</ImageBackground>
|
|
69
|
+
)
|
|
70
|
+
: (<Video
|
|
71
|
+
source={{ uri: videoUri }}
|
|
72
|
+
style={{
|
|
73
|
+
width: "90%",
|
|
74
|
+
marginLeft: '5%',
|
|
75
|
+
height: "100%"
|
|
76
|
+
}}
|
|
77
|
+
controls={true}
|
|
78
|
+
fullscreen={Platform.OS == 'ios' && true}
|
|
79
|
+
/>)
|
|
66
80
|
);
|
|
67
81
|
};
|
|
68
82
|
|
package/android/build.gradle
CHANGED
|
@@ -155,7 +155,6 @@ dependencies {
|
|
|
155
155
|
implementation "androidx.core:core:1.3.1"
|
|
156
156
|
implementation "androidx.exifinterface:exifinterface:1.3.3"
|
|
157
157
|
implementation 'androidx.recyclerview:recyclerview:1.3.2'
|
|
158
|
-
implementation 'com.arthenica:ffmpeg-kit-full:6.0-2'
|
|
159
158
|
|
|
160
159
|
|
|
161
160
|
def firebaseBomVersion = safeExtGet("firebaseBomVersion", null)
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
package com.shakebug;
|
|
2
|
+
|
|
3
|
+
import android.content.Context;
|
|
4
|
+
import android.graphics.Bitmap;
|
|
5
|
+
import android.graphics.BitmapFactory;
|
|
6
|
+
import android.media.MediaMetadataRetriever;
|
|
7
|
+
import android.net.Uri;
|
|
8
|
+
import android.os.Build;
|
|
9
|
+
import android.os.Build.VERSION;
|
|
10
|
+
import android.os.Handler;
|
|
11
|
+
import android.os.Looper;
|
|
12
|
+
import android.text.TextUtils;
|
|
13
|
+
import android.webkit.URLUtil;
|
|
14
|
+
|
|
15
|
+
import com.facebook.react.bridge.Arguments;
|
|
16
|
+
import com.facebook.react.bridge.Promise;
|
|
17
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
18
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
19
|
+
import com.facebook.react.bridge.ReactMethod;
|
|
20
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
21
|
+
import com.facebook.react.bridge.WritableMap;
|
|
22
|
+
|
|
23
|
+
import org.apache.commons.io.comparator.LastModifiedFileComparator;
|
|
24
|
+
|
|
25
|
+
import java.io.File;
|
|
26
|
+
import java.io.FileOutputStream;
|
|
27
|
+
import java.io.IOException;
|
|
28
|
+
import java.io.OutputStream;
|
|
29
|
+
import java.io.UnsupportedEncodingException;
|
|
30
|
+
import java.net.URLDecoder;
|
|
31
|
+
import java.util.Arrays;
|
|
32
|
+
import java.util.HashMap;
|
|
33
|
+
import java.util.Map;
|
|
34
|
+
import java.util.UUID;
|
|
35
|
+
import java.util.concurrent.Executor;
|
|
36
|
+
import java.util.concurrent.Executors;
|
|
37
|
+
|
|
38
|
+
public class CreateThumbnailModule extends ReactContextBaseJavaModule {
|
|
39
|
+
private final Executor executor = Executors.newSingleThreadExecutor();
|
|
40
|
+
private final Handler handler = new Handler(Looper.getMainLooper());
|
|
41
|
+
private final ReactApplicationContext reactContext;
|
|
42
|
+
|
|
43
|
+
public CreateThumbnailModule(ReactApplicationContext reactContext) {
|
|
44
|
+
super(reactContext);
|
|
45
|
+
this.reactContext = reactContext;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@Override
|
|
49
|
+
public String getName() {
|
|
50
|
+
return "CreateThumbnail";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@ReactMethod
|
|
54
|
+
public void create(ReadableMap options, Promise promise) {
|
|
55
|
+
executor.execute(() -> {
|
|
56
|
+
try {
|
|
57
|
+
ReadableMap data = processData(options);
|
|
58
|
+
handler.post(() -> {
|
|
59
|
+
promise.resolve(data);
|
|
60
|
+
});
|
|
61
|
+
} catch (IOException e) {
|
|
62
|
+
handler.post(() -> {
|
|
63
|
+
promise.reject("CreateThumbnail_ERROR", e);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private ReadableMap processData(ReadableMap options) throws IOException {
|
|
70
|
+
String format = options.hasKey("format") ? options.getString("format") : "jpeg";
|
|
71
|
+
String cacheName = options.hasKey("cacheName") ? options.getString("cacheName") : "";
|
|
72
|
+
|
|
73
|
+
String thumbnailDir = reactContext.getApplicationContext().getCacheDir().getAbsolutePath() + "/thumbnails";
|
|
74
|
+
File cacheDir = createDirIfNotExists(thumbnailDir);
|
|
75
|
+
if (!TextUtils.isEmpty(cacheName)) {
|
|
76
|
+
File file = new File(thumbnailDir, cacheName + "." + format);
|
|
77
|
+
if (file.exists()) {
|
|
78
|
+
WritableMap map = Arguments.createMap();
|
|
79
|
+
map.putString("path", "file://" + file.getAbsolutePath());
|
|
80
|
+
Bitmap image = BitmapFactory.decodeFile(file.getAbsolutePath());
|
|
81
|
+
map.putDouble("size", image.getByteCount());
|
|
82
|
+
map.putString("mime", "image/" + format);
|
|
83
|
+
map.putDouble("width", image.getWidth());
|
|
84
|
+
map.putDouble("height", image.getHeight());
|
|
85
|
+
return map;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
String filePath = options.hasKey("url") ? options.getString("url") : "";
|
|
90
|
+
int dirSize = options.hasKey("dirSize") ? options.getInt("dirSize") : 100;
|
|
91
|
+
int timeStamp = options.hasKey("timeStamp") ? options.getInt("timeStamp") : 0;
|
|
92
|
+
int maxWidth = options.hasKey("maxWidth") ? options.getInt("maxWidth") : 512;
|
|
93
|
+
int maxHeight = options.hasKey("maxHeight") ? options.getInt("maxHeight") : 512;
|
|
94
|
+
boolean onlySyncedFrames = options.hasKey("onlySyncedFrames") ? options.getBoolean("onlySyncedFrames") : true;
|
|
95
|
+
HashMap headers = options.hasKey("headers") ? options.getMap("headers").toHashMap() : new HashMap<String, String>();
|
|
96
|
+
String fileName = TextUtils.isEmpty(cacheName) ? ("thumb-" + UUID.randomUUID().toString()) : cacheName + "." + format;
|
|
97
|
+
OutputStream fOut = null;
|
|
98
|
+
File file = new File(cacheDir, fileName);
|
|
99
|
+
Context context = reactContext;
|
|
100
|
+
Bitmap image = getBitmapAtTime(context, filePath, timeStamp, maxWidth, maxHeight, onlySyncedFrames, headers);
|
|
101
|
+
file.createNewFile();
|
|
102
|
+
fOut = new FileOutputStream(file);
|
|
103
|
+
|
|
104
|
+
// 100 means no compression, the lower you go, the stronger the compression
|
|
105
|
+
if (format.equals("png")) {
|
|
106
|
+
image.compress(Bitmap.CompressFormat.PNG, 100, fOut);
|
|
107
|
+
} else {
|
|
108
|
+
image.compress(Bitmap.CompressFormat.JPEG, 90, fOut);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
fOut.flush();
|
|
112
|
+
fOut.close();
|
|
113
|
+
|
|
114
|
+
long cacheDirSize = (long) dirSize * 1024 * 1024;
|
|
115
|
+
long newSize = image.getByteCount() + getDirSize(cacheDir);
|
|
116
|
+
// free up some cached data if size of cache dir exceeds CACHE_DIR_MAX_SIZE
|
|
117
|
+
if (newSize > cacheDirSize) {
|
|
118
|
+
cleanDir(cacheDir, cacheDirSize / 2);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
WritableMap map = Arguments.createMap();
|
|
122
|
+
map.putString("path", "file://" + file.getAbsolutePath());
|
|
123
|
+
map.putDouble("size", image.getByteCount());
|
|
124
|
+
map.putString("mime", "image/" + format);
|
|
125
|
+
map.putDouble("width", image.getWidth());
|
|
126
|
+
map.putDouble("height", image.getHeight());
|
|
127
|
+
return map;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// delete previously added files one by one untill requred space is available
|
|
131
|
+
private static void cleanDir(File dir, long bytes) {
|
|
132
|
+
long bytesDeleted = 0;
|
|
133
|
+
File[] files = dir.listFiles();
|
|
134
|
+
Arrays.sort(files, LastModifiedFileComparator.LASTMODIFIED_COMPARATOR);
|
|
135
|
+
|
|
136
|
+
for (File file : files) {
|
|
137
|
+
bytesDeleted += file.length();
|
|
138
|
+
file.delete();
|
|
139
|
+
|
|
140
|
+
if (bytesDeleted >= bytes) {
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private static File createDirIfNotExists(String path) {
|
|
147
|
+
File dir = new File(path);
|
|
148
|
+
if (dir.exists()) {
|
|
149
|
+
return dir;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
dir.mkdirs();
|
|
154
|
+
// Add .nomedia to hide the thumbnail directory from gallery
|
|
155
|
+
File noMedia = new File(path, ".nomedia");
|
|
156
|
+
noMedia.createNewFile();
|
|
157
|
+
} catch (IOException e) {
|
|
158
|
+
e.printStackTrace();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return dir;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private static Bitmap getBitmapAtTime(Context context, String filePath, int time, int maxWidth, int maxHeight, boolean onlySyncedFrames, Map headers) throws IOException, IllegalStateException {
|
|
165
|
+
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
|
|
166
|
+
if (URLUtil.isFileUrl(filePath)) {
|
|
167
|
+
String decodedPath;
|
|
168
|
+
try {
|
|
169
|
+
decodedPath = URLDecoder.decode(filePath, "UTF-8");
|
|
170
|
+
} catch (UnsupportedEncodingException e) {
|
|
171
|
+
decodedPath = filePath;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
retriever.setDataSource(decodedPath.replace("file://", ""));
|
|
175
|
+
} else if (filePath.contains("content://")) {
|
|
176
|
+
retriever.setDataSource(context, Uri.parse(filePath));
|
|
177
|
+
} else {
|
|
178
|
+
if (VERSION.SDK_INT < 14) {
|
|
179
|
+
throw new IllegalStateException("Remote videos aren't supported on sdk_version < 14");
|
|
180
|
+
}
|
|
181
|
+
retriever.setDataSource(filePath, headers);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
Bitmap image;
|
|
185
|
+
if (Build.VERSION.SDK_INT >= 27) {
|
|
186
|
+
image = retriever.getScaledFrameAtTime(time * 1000, MediaMetadataRetriever.OPTION_CLOSEST_SYNC, maxWidth, maxHeight);
|
|
187
|
+
} else {
|
|
188
|
+
// If on versions lower than API 27, use other methods to get frames
|
|
189
|
+
image = retriever.getFrameAtTime(time * 1000, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
|
|
190
|
+
if (image != null) {
|
|
191
|
+
image = Bitmap.createScaledBitmap(image, maxWidth, maxHeight, true);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
retriever.release();
|
|
196
|
+
} catch(IOException e) {
|
|
197
|
+
e.printStackTrace();
|
|
198
|
+
}
|
|
199
|
+
if (image == null) {
|
|
200
|
+
throw new IllegalStateException("File doesn't exist or not supported");
|
|
201
|
+
}
|
|
202
|
+
return image;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private static long getDirSize(File dir) {
|
|
206
|
+
long size = 0;
|
|
207
|
+
File[] files = dir.listFiles();
|
|
208
|
+
|
|
209
|
+
for (File file : files) {
|
|
210
|
+
if (file.isFile()) {
|
|
211
|
+
size += file.length();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return size;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -41,8 +41,7 @@ public class NetInfoModule extends ReactContextBaseJavaModule implements AmazonF
|
|
|
41
41
|
mAmazonConnectivityChecker.register();
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
public void onCatalystInstanceDestroy() {
|
|
44
|
+
public void stopDetection() {
|
|
46
45
|
mAmazonConnectivityChecker.unregister();
|
|
47
46
|
mConnectivityReceiver.unregister();
|
|
48
47
|
mConnectivityReceiver.hasListener = false;
|
|
@@ -153,9 +153,7 @@ public class RNDeviceModule extends ReactContextBaseJavaModule {
|
|
|
153
153
|
getReactApplicationContext().registerReceiver(headphoneConnectionReceiver, filter);
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
|
|
157
|
-
@Override
|
|
158
|
-
public void onCatalystInstanceDestroy() {
|
|
156
|
+
public void stopDetection() {
|
|
159
157
|
getReactApplicationContext().unregisterReceiver(receiver);
|
|
160
158
|
getReactApplicationContext().unregisterReceiver(headphoneConnectionReceiver);
|
|
161
159
|
}
|
|
@@ -22,13 +22,13 @@ public class ShakebugPackage implements ReactPackage {
|
|
|
22
22
|
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
|
23
23
|
|
|
24
24
|
return Arrays.<NativeModule>asList(
|
|
25
|
-
new
|
|
26
|
-
new
|
|
27
|
-
new
|
|
28
|
-
new
|
|
29
|
-
new
|
|
30
|
-
new
|
|
31
|
-
new ShakebugSDKScreenShotModule(reactContext),
|
|
25
|
+
new ShakebugSensors(reactContext, "ShakebugSDKRNSensorsGyroscope", Sensor.TYPE_GYROSCOPE),
|
|
26
|
+
new ShakebugSensors(reactContext, "ShakebugSDKRNSensorsAccelerometer", Sensor.TYPE_ACCELEROMETER),
|
|
27
|
+
new ShakebugSensors(reactContext, "ShakebugSDKRNSensorsGravity", Sensor.TYPE_GRAVITY),
|
|
28
|
+
new ShakebugSensors(reactContext, "ShakebugSDKRNSensorsMagnetometer", Sensor.TYPE_MAGNETIC_FIELD),
|
|
29
|
+
new ShakebugSensors(reactContext, "ShakebugSDKRNSensorsBarometer", Sensor.TYPE_PRESSURE),
|
|
30
|
+
new ShakebugSensors(reactContext, "ShakebugSDKRNSensorsOrientation", Sensor.TYPE_ROTATION_VECTOR),
|
|
31
|
+
// new ShakebugSDKScreenShotModule(reactContext),
|
|
32
32
|
new ShakebugSDKImagePickerModule(reactContext),
|
|
33
33
|
new RecordScreenModule(reactContext),
|
|
34
34
|
new RNDeviceModule(reactContext),
|
|
@@ -37,9 +37,9 @@ public class ShakebugPackage implements ReactPackage {
|
|
|
37
37
|
new NetInfoModule(reactContext),
|
|
38
38
|
new RNLocalizeModule(reactContext),
|
|
39
39
|
new SharedPreferencesModule(reactContext),
|
|
40
|
-
new VideoPlayerModule(reactContext),
|
|
41
|
-
new
|
|
42
|
-
new VideoTrimModule(reactContext),
|
|
40
|
+
// new VideoPlayerModule(reactContext),
|
|
41
|
+
new CreateThumbnailModule(reactContext),
|
|
42
|
+
// new VideoTrimModule(reactContext),
|
|
43
43
|
new ScreenshotDetectorModule(reactContext)
|
|
44
44
|
);
|
|
45
45
|
}
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
package com.shakebug;
|
|
2
2
|
|
|
3
|
-
import androidx.annotation.NonNull;
|
|
4
|
-
|
|
5
3
|
import android.hardware.Sensor;
|
|
6
4
|
import android.hardware.SensorEvent;
|
|
7
5
|
import android.hardware.SensorEventListener;
|
|
8
|
-
import android.hardware.SensorListener;
|
|
9
6
|
import android.hardware.SensorManager;
|
|
10
7
|
import android.os.SystemClock;
|
|
11
8
|
import androidx.annotation.Nullable;
|
|
@@ -16,14 +13,11 @@ import com.facebook.react.bridge.Promise;
|
|
|
16
13
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
17
14
|
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
18
15
|
import com.facebook.react.bridge.ReactMethod;
|
|
19
|
-
import com.facebook.react.module.annotations.ReactModule;
|
|
20
16
|
import com.facebook.react.bridge.WritableMap;
|
|
21
17
|
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
|
22
18
|
|
|
23
|
-
|
|
19
|
+
public class ShakebugSensors extends ReactContextBaseJavaModule implements SensorEventListener {
|
|
24
20
|
|
|
25
|
-
public class Shakebug extends ReactContextBaseJavaModule implements SensorEventListener {
|
|
26
|
-
public static final String NAME = "AwesomeLibrary";
|
|
27
21
|
private final ReactApplicationContext reactContext;
|
|
28
22
|
private final SensorManager sensorManager;
|
|
29
23
|
private final Sensor sensor;
|
|
@@ -37,8 +31,9 @@ public class Shakebug extends ReactContextBaseJavaModule implements SensorEventL
|
|
|
37
31
|
private float[] orientation = new float[3];
|
|
38
32
|
private float[] quaternion = new float[4];
|
|
39
33
|
|
|
40
|
-
private
|
|
41
|
-
|
|
34
|
+
private int listenerCount = 0;
|
|
35
|
+
|
|
36
|
+
public ShakebugSensors(ReactApplicationContext reactContext, String sensorName, int sensorType) {
|
|
42
37
|
super(reactContext);
|
|
43
38
|
this.reactContext = reactContext;
|
|
44
39
|
this.sensorType = sensorType;
|
|
@@ -47,12 +42,6 @@ public class Shakebug extends ReactContextBaseJavaModule implements SensorEventL
|
|
|
47
42
|
this.sensor = this.sensorManager.getDefaultSensor(this.sensorType);
|
|
48
43
|
}
|
|
49
44
|
|
|
50
|
-
// @Override
|
|
51
|
-
// @NonNull
|
|
52
|
-
// public String getName() {
|
|
53
|
-
// return NAME;
|
|
54
|
-
// }
|
|
55
|
-
|
|
56
45
|
// RN Methods
|
|
57
46
|
@ReactMethod
|
|
58
47
|
public void isAvailable(Promise promise) {
|
|
@@ -77,12 +66,12 @@ public class Shakebug extends ReactContextBaseJavaModule implements SensorEventL
|
|
|
77
66
|
@ReactMethod
|
|
78
67
|
public void startUpdates() {
|
|
79
68
|
// Milliseconds to Microseconds conversion
|
|
80
|
-
sensorManager.registerListener(
|
|
69
|
+
sensorManager.registerListener(this, sensor, this.interval * 1000);
|
|
81
70
|
}
|
|
82
71
|
|
|
83
72
|
@ReactMethod
|
|
84
73
|
public void stopUpdates() {
|
|
85
|
-
sensorManager.unregisterListener(
|
|
74
|
+
sensorManager.unregisterListener(this);
|
|
86
75
|
}
|
|
87
76
|
|
|
88
77
|
@Override
|
|
@@ -105,8 +94,9 @@ public class Shakebug extends ReactContextBaseJavaModule implements SensorEventL
|
|
|
105
94
|
}
|
|
106
95
|
}
|
|
107
96
|
|
|
97
|
+
@Override
|
|
108
98
|
public void onSensorChanged(SensorEvent sensorEvent) {
|
|
109
|
-
if(
|
|
99
|
+
if(this.listenerCount <= 0) {
|
|
110
100
|
return; // avoid all the computation if there are no observers
|
|
111
101
|
}
|
|
112
102
|
|
|
@@ -161,6 +151,7 @@ public class Shakebug extends ReactContextBaseJavaModule implements SensorEventL
|
|
|
161
151
|
}
|
|
162
152
|
}
|
|
163
153
|
|
|
154
|
+
@Override
|
|
164
155
|
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
|
165
156
|
}
|
|
166
157
|
|
|
@@ -168,14 +159,17 @@ public class Shakebug extends ReactContextBaseJavaModule implements SensorEventL
|
|
|
168
159
|
// not implementing this method will cause a warning on RN 0.65 onwards
|
|
169
160
|
@ReactMethod
|
|
170
161
|
public void addListener(String eventName) {
|
|
171
|
-
|
|
162
|
+
this.listenerCount += 1;
|
|
172
163
|
}
|
|
173
164
|
|
|
174
165
|
// this is called by RN when the last listener is deregistered
|
|
175
166
|
// not implementing this method will cause a warning on RN 0.65 onwards
|
|
176
167
|
@ReactMethod
|
|
177
168
|
public void removeListeners(Integer count) {
|
|
178
|
-
|
|
179
|
-
|
|
169
|
+
this.listenerCount -= count;
|
|
170
|
+
// If we no longer have listeners registered we should also probably also stop the sensor since the sensor events are essentially being dropped.
|
|
171
|
+
if (this.sensorManager != null && this.listenerCount <= 0) {
|
|
172
|
+
stopUpdates(); // maybe only calling `stopUpdates()` is enough
|
|
173
|
+
}
|
|
180
174
|
}
|
|
181
|
-
}
|
|
175
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
//
|
|
2
|
+
// VideoThumbnailManager.m
|
|
3
|
+
// Shakebug
|
|
4
|
+
//
|
|
5
|
+
// Created by ideveloper2 on 26/04/24.
|
|
6
|
+
// Copyright © 2024 Facebook. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
#import "CreateThumbnail.h"
|
|
10
|
+
#import <AVFoundation/AVFoundation.h>
|
|
11
|
+
#import <UIKit/UIKit.h>
|
|
12
|
+
|
|
13
|
+
@implementation CreateThumbnail
|
|
14
|
+
|
|
15
|
+
RCT_EXPORT_MODULE();
|
|
16
|
+
|
|
17
|
+
RCT_EXPORT_METHOD(create:(NSDictionary *)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
18
|
+
{
|
|
19
|
+
NSString *urlString = options[@"url"];
|
|
20
|
+
NSNumber *timeStamp = options[@"timeStamp"] ?: @(0);
|
|
21
|
+
NSNumber *maxWidth = options[@"maxWidth"] ?: @(512);
|
|
22
|
+
NSNumber *maxHeight = options[@"maxHeight"] ?: @(512);
|
|
23
|
+
NSString *format = options[@"format"] ?: @"jpeg";
|
|
24
|
+
NSString *cacheName = options[@"cacheName"];
|
|
25
|
+
|
|
26
|
+
if (!urlString || ![urlString isKindOfClass:[NSString class]]) {
|
|
27
|
+
reject(@"CreateThumbnail_ERROR", @"Invalid URL", nil);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
NSURL *url = [NSURL URLWithString:urlString];
|
|
32
|
+
if ([url.scheme isEqualToString:@"file"]) {
|
|
33
|
+
url = [NSURL fileURLWithPath:url.path];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
AVAsset *asset = [AVAsset assetWithURL:url];
|
|
37
|
+
AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
|
|
38
|
+
imageGenerator.appliesPreferredTrackTransform = YES;
|
|
39
|
+
imageGenerator.maximumSize = CGSizeMake(maxWidth.floatValue, maxHeight.floatValue);
|
|
40
|
+
|
|
41
|
+
CMTime time = CMTimeMakeWithSeconds(timeStamp.floatValue / 1000.0, 600);
|
|
42
|
+
NSError *error = nil;
|
|
43
|
+
CMTime actualTime;
|
|
44
|
+
CGImageRef cgImage = [imageGenerator copyCGImageAtTime:time actualTime:&actualTime error:&error];
|
|
45
|
+
|
|
46
|
+
if (error || !cgImage) {
|
|
47
|
+
reject(@"FAIL_TO_LOAD_MEDIA", @"Failed to load duration: Operation Stopped", error);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
UIImage *image = [UIImage imageWithCGImage:cgImage];
|
|
52
|
+
CGImageRelease(cgImage);
|
|
53
|
+
|
|
54
|
+
NSData *imageData;
|
|
55
|
+
NSString *mime;
|
|
56
|
+
if ([format.lowercaseString isEqualToString:@"png"]) {
|
|
57
|
+
imageData = UIImagePNGRepresentation(image);
|
|
58
|
+
mime = @"image/png";
|
|
59
|
+
} else {
|
|
60
|
+
imageData = UIImageJPEGRepresentation(image, 0.9);
|
|
61
|
+
mime = @"image/jpeg";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
NSString *fileName = cacheName ?: [NSString stringWithFormat:@"thumb-%@.%@", [[NSUUID UUID] UUIDString], format];
|
|
65
|
+
NSString *thumbDir = [NSTemporaryDirectory() stringByAppendingPathComponent:@"thumbnails"];
|
|
66
|
+
[[NSFileManager defaultManager] createDirectoryAtPath:thumbDir withIntermediateDirectories:YES attributes:nil error:nil];
|
|
67
|
+
|
|
68
|
+
NSString *filePath = [thumbDir stringByAppendingPathComponent:fileName];
|
|
69
|
+
[imageData writeToFile:filePath atomically:YES];
|
|
70
|
+
|
|
71
|
+
NSDictionary *result = @{
|
|
72
|
+
@"path": [NSString stringWithFormat:@"file://%@", filePath],
|
|
73
|
+
@"size": @(imageData.length),
|
|
74
|
+
@"mime": mime,
|
|
75
|
+
@"width": @(image.size.width),
|
|
76
|
+
@"height": @(image.size.height)
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
resolve(result);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@end
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shakebug-react-native",
|
|
3
3
|
"title": "shakebug-react-native",
|
|
4
|
-
"version": "2.1
|
|
4
|
+
"version": "2.2.1",
|
|
5
5
|
"description": "Shakebug allows users to report the bug from your mobile phone for your application. Here users just need to shake their mobile and all the data regarding bugs & crashes can be seen by developers through their log in panel easily",
|
|
6
6
|
"main": "src/index.tsx",
|
|
7
7
|
"scripts": {
|
|
@@ -31,8 +31,6 @@
|
|
|
31
31
|
"@react-navigation/native": "^6.1.9",
|
|
32
32
|
"html2canvas": "^1.4.1",
|
|
33
33
|
"react-native-logs": "^5.1.0",
|
|
34
|
-
"react-native-safe-area-context": "^4.14.0",
|
|
35
|
-
"react-native-screens": "^4.3.0",
|
|
36
34
|
"rxjs": "^7.8.1",
|
|
37
35
|
"typescript": "^5.3.3"
|
|
38
36
|
},
|
|
@@ -42,7 +40,6 @@
|
|
|
42
40
|
},
|
|
43
41
|
"devDependencies": {
|
|
44
42
|
"flow-bin": "^0.170.0",
|
|
45
|
-
"html-webpack-plugin": "^5.5.1"
|
|
46
|
-
"react-native-windows": "^0.63.16"
|
|
43
|
+
"html-webpack-plugin": "^5.5.1"
|
|
47
44
|
}
|
|
48
45
|
}
|
|
@@ -21,7 +21,6 @@ Pod::Spec.new do |s|
|
|
|
21
21
|
s.source = { :git => "https://github.com/github_account/react-native-shakebug.git", :tag => "#{s.version}" }
|
|
22
22
|
|
|
23
23
|
s.source_files = "ios/**/*.{h,c,cc,cpp,m,mm,swift}"
|
|
24
|
-
s.dependency "ffmpeg-kit-ios-#{ENV['FFMPEGKIT_PACKAGE'] || 'min'}", ENV['FFMPEGKIT_PACKAGE_VERSION'] || '~> 6.0'
|
|
25
24
|
s.requires_arc = true
|
|
26
25
|
|
|
27
26
|
s.dependency "React"
|