shakebug-react-native 2.4.9 → 2.4.10

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.
Files changed (42) hide show
  1. package/README.md +0 -12
  2. package/VideoPlayer/PlayVideo.tsx +4 -46
  3. package/VideoPlayer/index.js +6 -43
  4. package/android/src/main/java/com/shakebug/BaseUtils.java +7 -19
  5. package/android/src/main/java/com/shakebug/DeviceUtil.java +34 -21
  6. package/android/src/main/java/com/shakebug/ShakebugPackage.java +3 -3
  7. package/android/src/main/java/com/shakebug/UnitConverter.java +8 -8
  8. package/android/src/main/java/com/shakebug/VideoPlayerModule.java +81 -0
  9. package/android/src/main/java/com/shakebug/VideoTrimModule.java +387 -0
  10. package/android/src/main/java/com/shakebug/VideoTrimUtil.java +133 -0
  11. package/android/src/main/java/com/shakebug/adapters/VideoTrimmerAdapter.java +53 -0
  12. package/android/src/main/java/com/shakebug/callback/SingleCallback.java +0 -6
  13. package/android/src/main/java/com/shakebug/thread/BackgroundExecutor.java +1 -93
  14. package/android/src/main/java/com/shakebug/thread/UiThreadExecutor.java +0 -37
  15. package/android/src/main/java/com/shakebug/utils/StorageUtil.java +135 -0
  16. package/android/src/main/java/com/shakebug/utils/VideoTrimmerUtil.java +70 -0
  17. package/android/src/main/java/com/shakebug/widgets/RangeSeekBarView.java +534 -0
  18. package/android/src/main/java/com/shakebug/widgets/SpacesItemDecoration2.java +1 -2
  19. package/android/src/main/java/com/shakebug/widgets/VideoPlayerView.java +317 -0
  20. package/android/src/main/java/com/shakebug/widgets/VideoTrimmerView.java +520 -0
  21. package/android/src/main/res/drawable/ic_volume_off.xml +10 -0
  22. package/android/src/main/res/drawable/ic_volume_on.xml +10 -0
  23. package/android/src/main/res/layout/{video_player_view.xml → shakebug_video_player_view.xml} +29 -9
  24. package/ios/TrimVideo/ShakebugSDKVideoTrim.swift +389 -0
  25. package/ios/TrimVideo/VideoTrim-Bridging-Header.h +10 -0
  26. package/ios/TrimVideo/VideoTrim.m +23 -0
  27. package/ios/TrimVideo/VideoTrimmer.swift +810 -0
  28. package/ios/TrimVideo/VideoTrimmerThumb.swift +119 -0
  29. package/ios/TrimVideo/VideoTrimmerViewController.swift +322 -0
  30. package/ios/VideoPlayer/VideoPlayerModule.m +23 -7
  31. package/package.json +1 -1
  32. package/src/Componanats/LoadingModal.js +59 -0
  33. package/src/Componanats/ShakeFunction.js +2 -10
  34. package/src/Screens/AddDetailsScreen.js +28 -31
  35. package/src/Screens/FirstScreen.js +15 -18
  36. package/src/Screens/Shakebug.js +1 -1
  37. package/src/Screens/TrimVideoScreen.js +37 -22
  38. package/src/index.tsx +4 -4
  39. package/trimVideo/src/index.tsx +12 -48
  40. package/shakebug-react-native-2.4.8.tgz +0 -0
  41. /package/android/src/main/res/layout/{video_thumb_item_layout.xml → shakebug_video_thumb_item_layout.xml} +0 -0
  42. /package/android/src/main/res/layout/{video_trimmer_view.xml → shakebug_video_trimmer_view.xml} +0 -0
@@ -1,18 +1,3 @@
1
- /**
2
- * Copyright (C) 2010-2016 eBusiness Information, Excilys Group
3
- * <p>
4
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5
- * use this file except in compliance with the License. You may obtain a copy of
6
- * the License at
7
- * <p>
8
- * http://www.apache.org/licenses/LICENSE-2.0
9
- * <p>
10
- * Unless required by applicable law or agreed To in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
- * License for the specific language governing permissions and limitations under
14
- * the License.
15
- */
16
1
  package com.shakebug.thread;
17
2
 
18
3
  import android.util.Log;
@@ -39,24 +24,9 @@ public final class BackgroundExecutor {
39
24
  private BackgroundExecutor() {
40
25
  }
41
26
 
42
- /**
43
- * Execute a runnable after the given delay.
44
- *
45
- * @param runnable the task to execute
46
- * @param delay the time from now to delay execution, in milliseconds
47
- * <p>
48
- * if <code>delay</code> is strictly positive and the current
49
- * executor does not support scheduling (if
50
- * Executor has been called with such an
51
- * executor)
52
- * @return Future associated to the running task
53
- * @throws IllegalArgumentException if the current executor set by Executor
54
- * does not support scheduling
55
- */
56
27
  private static Future<?> directExecute(Runnable runnable, long delay) {
57
28
  Future<?> future = null;
58
29
  if (delay > 0) {
59
- /* no serial, but a delay: schedule the task */
60
30
  if (!(executor instanceof ScheduledExecutorService)) {
61
31
  throw new IllegalArgumentException("The executor set does not support scheduling");
62
32
  }
@@ -67,24 +37,12 @@ public final class BackgroundExecutor {
67
37
  ExecutorService executorService = (ExecutorService) executor;
68
38
  future = executorService.submit(runnable);
69
39
  } else {
70
- /* non-cancellable task */
71
40
  executor.execute(runnable);
72
41
  }
73
42
  }
74
43
  return future;
75
44
  }
76
45
 
77
- /**
78
- * Execute a task after (at least) its delay <strong>and</strong> after all
79
- * tasks added with the same non-null <code>serial</code> (if any) have
80
- * completed execution.
81
- *
82
- * @param task the task to execute
83
- * @throws IllegalArgumentException if <code>task.delay</code> is strictly positive and the
84
- * current executor does not support scheduling (if
85
- * Executor has been called with such an
86
- * executor)
87
- */
88
46
  public static synchronized void execute(Task task) {
89
47
  Future<?> future = null;
90
48
  if (task.serial == null || !hasSerialRunning(task.serial)) {
@@ -92,20 +50,11 @@ public final class BackgroundExecutor {
92
50
  future = directExecute(task, task.remainingDelay);
93
51
  }
94
52
  if ((task.id != null || task.serial != null) && !task.managed.get()) {
95
- /* keep task */
96
53
  task.future = future;
97
54
  TASKS.add(task);
98
55
  }
99
56
  }
100
57
 
101
- /**
102
- * Indicates whether a task with the specified <code>serial</code> has been
103
- * submitted to the executor.
104
- *
105
- * @param serial the serial queue
106
- * @return <code>true</code> if such a task has been submitted,
107
- * <code>false</code> otherwise
108
- */
109
58
  private static boolean hasSerialRunning(String serial) {
110
59
  for (Task task : TASKS) {
111
60
  if (task.executionAsked && serial.equals(task.serial)) {
@@ -115,13 +64,6 @@ public final class BackgroundExecutor {
115
64
  return false;
116
65
  }
117
66
 
118
- /**
119
- * Retrieve and remove the first task having the specified
120
- * <code>serial</code> (if any).
121
- *
122
- * @param serial the serial queue
123
- * @return task if found, <code>null</code> otherwise
124
- */
125
67
  private static Task take(String serial) {
126
68
  int len = TASKS.size();
127
69
  for (int i = 0; i < len; i++) {
@@ -132,14 +74,6 @@ public final class BackgroundExecutor {
132
74
  return null;
133
75
  }
134
76
 
135
- /**
136
- * Cancel all tasks having the specified <code>id</code>.
137
- *
138
- * @param id the cancellation identifier
139
- * @param mayInterruptIfRunning <code>true</code> if the thread executing this task should be
140
- * interrupted; otherwise, in-progress tasks are allowed to
141
- * complete
142
- */
143
77
  public static synchronized void cancelAll(String id, boolean mayInterruptIfRunning) {
144
78
  for (int i = TASKS.size() - 1; i >= 0; i--) {
145
79
  Task task = TASKS.get(i);
@@ -147,17 +81,11 @@ public final class BackgroundExecutor {
147
81
  if (task.future != null) {
148
82
  task.future.cancel(mayInterruptIfRunning);
149
83
  if (!task.managed.getAndSet(true)) {
150
- /*
151
- * the task has been submitted to the executor, but its
152
- * execution has not started yet, so that its run()
153
- * method will never call postExecute()
154
- */
155
84
  task.postExecute();
156
85
  }
157
86
  } else if (task.executionAsked) {
158
87
  Log.w(TAG, "A task with id " + task.id + " cannot be cancelled (the executor set does not support it)");
159
88
  } else {
160
- /* this task has not been submitted to the executor */
161
89
  TASKS.remove(i);
162
90
  }
163
91
  }
@@ -168,23 +96,10 @@ public final class BackgroundExecutor {
168
96
 
169
97
  private String id;
170
98
  private long remainingDelay;
171
- private long targetTimeMillis; /* since epoch */
99
+ private long targetTimeMillis;
172
100
  private String serial;
173
101
  private boolean executionAsked;
174
102
  private Future<?> future;
175
-
176
- /*
177
- * A task can be cancelled after it has been submitted to the executor
178
- * but before its run() method is called. In that case, run() will never
179
- * be called, hence neither will postExecute(): the tasks with the same
180
- * serial identifier (if any) will never be submitted.
181
- *
182
- * Therefore, cancelAll() *must* call postExecute() if run() is not
183
- * started.
184
- *
185
- * This flag guarantees that either cancelAll() or run() manages this
186
- * task post execution, but not both.
187
- */
188
103
  private AtomicBoolean managed = new AtomicBoolean();
189
104
 
190
105
  public Task(String id, long delay, String serial) {
@@ -203,7 +118,6 @@ public final class BackgroundExecutor {
203
118
  @Override
204
119
  public void run() {
205
120
  if (managed.getAndSet(true)) {
206
- /* cancelled and postExecute() already called */
207
121
  return;
208
122
  }
209
123
 
@@ -211,7 +125,6 @@ public final class BackgroundExecutor {
211
125
  CURRENT_SERIAL.set(serial);
212
126
  execute();
213
127
  } finally {
214
- /* handle next tasks */
215
128
  postExecute();
216
129
  }
217
130
  }
@@ -220,22 +133,18 @@ public final class BackgroundExecutor {
220
133
 
221
134
  private void postExecute() {
222
135
  if (id == null && serial == null) {
223
- /* nothing to do */
224
136
  return;
225
137
  }
226
138
  CURRENT_SERIAL.set(null);
227
139
  synchronized (BackgroundExecutor.class) {
228
- /* execution complete */
229
140
  TASKS.remove(this);
230
141
 
231
142
  if (serial != null) {
232
143
  Task next = take(serial);
233
144
  if (next != null) {
234
145
  if (next.remainingDelay != 0) {
235
- /* the delay may not have elapsed yet */
236
146
  next.remainingDelay = Math.max(0L, targetTimeMillis - System.currentTimeMillis());
237
147
  }
238
- /* a task having the same serial was queued, execute it */
239
148
  BackgroundExecutor.execute(next);
240
149
  }
241
150
  }
@@ -243,4 +152,3 @@ public final class BackgroundExecutor {
243
152
  }
244
153
  }
245
154
  }
246
-
@@ -1,18 +1,3 @@
1
- /**
2
- * Copyright (C) 2010-2016 eBusiness Information, Excilys Group
3
- * <p>
4
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5
- * use this file except in compliance with the License. You may obtain a copy of
6
- * the License at
7
- * <p>
8
- * http://www.apache.org/licenses/LICENSE-2.0
9
- * <p>
10
- * Unless required by applicable law or agreed To in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
- * License for the specific language governing permissions and limitations under
14
- * the License.
15
- */
16
1
  package com.shakebug.thread;
17
2
 
18
3
  import android.os.Handler;
@@ -23,10 +8,6 @@ import android.os.SystemClock;
23
8
  import java.util.HashMap;
24
9
  import java.util.Map;
25
10
 
26
- /**
27
- * This class provide operations for
28
- * UiThread tasks.
29
- */
30
11
  public final class UiThreadExecutor {
31
12
 
32
13
  private static final Handler HANDLER = new Handler(Looper.getMainLooper()) {
@@ -45,17 +26,8 @@ public final class UiThreadExecutor {
45
26
  private static final Map<String, Token> TOKENS = new HashMap<>();
46
27
 
47
28
  private UiThreadExecutor() {
48
- // should not be instantiated
49
29
  }
50
30
 
51
- /**
52
- * Store a new task in the map for providing cancellation. This method is
53
- * used by AndroidAnnotations and not intended to be called by clients.
54
- *
55
- * @param id the identifier of the task
56
- * @param task the task itself
57
- * @param delay the delay or zero to run immediately
58
- */
59
31
  public static void runTask(String id, Runnable task, long delay) {
60
32
  if ("".equals(id)) {
61
33
  HANDLER.postDelayed(task, delay);
@@ -83,26 +55,18 @@ public final class UiThreadExecutor {
83
55
  String id = token.id;
84
56
  Token old = TOKENS.remove(id);
85
57
  if (old != token) {
86
- // a runnable finished after cancelling, we just removed a
87
- // wrong token, lets put it back
88
58
  TOKENS.put(id, old);
89
59
  }
90
60
  }
91
61
  }
92
62
  }
93
63
 
94
- /**
95
- * Cancel all tasks having the specified <code>id</code>.
96
- *
97
- * @param id the cancellation identifier
98
- */
99
64
  public static void cancelAll(String id) {
100
65
  Token token;
101
66
  synchronized (TOKENS) {
102
67
  token = TOKENS.remove(id);
103
68
  }
104
69
  if (token == null) {
105
- // nothing to cancel
106
70
  return;
107
71
  }
108
72
  HANDLER.removeCallbacksAndMessages(token);
@@ -116,5 +80,4 @@ public final class UiThreadExecutor {
116
80
  this.id = id;
117
81
  }
118
82
  }
119
-
120
83
  }
@@ -0,0 +1,135 @@
1
+ package com.shakebug.utils;
2
+
3
+ import android.content.ContentValues;
4
+ import android.content.Context;
5
+ import android.media.MediaScannerConnection;
6
+ import android.net.Uri;
7
+ import android.os.Build;
8
+ import android.os.Environment;
9
+ import android.provider.MediaStore;
10
+ import android.text.TextUtils;
11
+ import com.facebook.react.bridge.ReactApplicationContext;
12
+ import java.io.File;
13
+ import java.io.FileInputStream;
14
+ import java.io.FileOutputStream;
15
+ import java.io.IOException;
16
+ import java.io.InputStream;
17
+ import java.io.OutputStream;
18
+ import java.util.ArrayList;
19
+ import java.util.List;
20
+
21
+ public class StorageUtil {
22
+ public static String getOutputPath(Context context) {
23
+ long timestamp = System.currentTimeMillis() / 1000;
24
+ File file = new File(context.getFilesDir(), VideoTrimmerUtil.FILE_PREFIX + "_" + timestamp + ".mp4");
25
+ return file.getAbsolutePath();
26
+ }
27
+
28
+ public static String[] listFiles(Context context) {
29
+ File filesDir = context.getFilesDir();
30
+ File[] files = filesDir.listFiles((dir, name) -> name.startsWith(VideoTrimmerUtil.FILE_PREFIX));
31
+
32
+ List<String> fileUrls = new ArrayList<>();
33
+ if (files != null) {
34
+ for (File file : files) {
35
+ fileUrls.add(file.getAbsolutePath());
36
+ }
37
+ }
38
+
39
+ return fileUrls.toArray(new String[0]);
40
+ }
41
+
42
+ public static boolean deleteFile(String path) {
43
+ if (TextUtils.isEmpty(path)) return true;
44
+ return deleteFile(new File(path));
45
+ }
46
+
47
+ public static boolean deleteFile(File file) {
48
+ if (file == null || !file.exists()) return true;
49
+
50
+ if (file.isFile()) {
51
+ return file.delete();
52
+ }
53
+
54
+ if (!file.isDirectory()) {
55
+ return false;
56
+ }
57
+
58
+ for (File f : file.listFiles()) {
59
+ if (f.isFile()) {
60
+ f.delete();
61
+ } else if (f.isDirectory()) {
62
+ deleteFile(f);
63
+ }
64
+ }
65
+ return file.delete();
66
+ }
67
+
68
+ public static void saveVideoToGallery(ReactApplicationContext context, String videoFilePath) throws IOException {
69
+ File videoFile = new File(videoFilePath);
70
+
71
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
72
+ saveVideoUsingMediaStore(context, videoFile);
73
+ } else {
74
+ try {
75
+ saveVideoUsingTraditionalStorage(context, videoFile);
76
+ } catch (IOException e) {
77
+ throw new RuntimeException(e);
78
+ }
79
+ }
80
+ }
81
+
82
+ private static void saveVideoUsingMediaStore(Context context, File videoFile) {
83
+ ContentValues values = new ContentValues();
84
+ values.put(MediaStore.Video.Media.TITLE, "My Video Title");
85
+ values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
86
+ values.put(MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM);
87
+ Uri uri = context.getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);
88
+
89
+ if (uri != null) {
90
+ try {
91
+ OutputStream outputStream = context.getContentResolver().openOutputStream(uri);
92
+ copyFile(videoFile, outputStream);
93
+ MediaScannerConnection.scanFile(context, new String[]{uri.toString()}, new String[]{"video/*"}, null);
94
+ } catch (IOException e) {
95
+ e.printStackTrace();
96
+ }
97
+ }
98
+ }
99
+
100
+ private static void saveVideoUsingTraditionalStorage(Context context, File videoFile) throws IOException {
101
+ File galleryDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
102
+ File destinationFile = new File(galleryDirectory, videoFile.getName());
103
+ copyFile(videoFile, destinationFile);
104
+ MediaScannerConnection.scanFile(context, new String[]{destinationFile.getAbsolutePath()}, new String[]{"video/*"}, null);
105
+ }
106
+
107
+ private static void copyFile(File sourceFile, OutputStream outputStream) throws IOException {
108
+ InputStream inputStream = new FileInputStream(sourceFile);
109
+ copyFile(inputStream, outputStream);
110
+ }
111
+
112
+ private static void copyFile(File sourceFile, File destFile) throws IOException {
113
+ InputStream inputStream = new FileInputStream(sourceFile);
114
+ OutputStream outputStream = new FileOutputStream(destFile);
115
+ copyFile(inputStream, outputStream);
116
+ }
117
+
118
+ private static void copyFile(InputStream inputStream, OutputStream outputStream) throws IOException {
119
+ try {
120
+ byte[] buffer = new byte[1024];
121
+ int length;
122
+ while ((length = inputStream.read(buffer)) > 0) {
123
+ outputStream.write(buffer, 0, length);
124
+ }
125
+
126
+ } finally {
127
+ if (inputStream != null) {
128
+ inputStream.close();
129
+ }
130
+ if (outputStream != null) {
131
+ outputStream.close();
132
+ }
133
+ }
134
+ }
135
+ }
@@ -0,0 +1,70 @@
1
+ package com.shakebug.utils;
2
+
3
+ import android.content.Context;
4
+ import android.graphics.Bitmap;
5
+ import android.media.MediaMetadataRetriever;
6
+ import android.net.Uri;
7
+ import com.shakebug.DeviceUtil;
8
+ import com.shakebug.UnitConverter;
9
+ import com.shakebug.VideoTrimUtil;
10
+ import com.shakebug.callback.SingleCallback;
11
+ import com.shakebug.interfaces.VideoTrimListener;
12
+ import com.shakebug.thread.BackgroundExecutor;
13
+
14
+ public class VideoTrimmerUtil {
15
+
16
+ private static final String TAG = VideoTrimmerUtil.class.getSimpleName();
17
+ public static final String FILE_PREFIX = "trimmedVideo";
18
+ public static final long MIN_SHOOT_DURATION = 1000L;// min 1 second for trimming
19
+ public static long maxShootDuration = 10 * 1000L;
20
+ public static int MAX_COUNT_RANGE = 10; // how many images in the highlight range of seek bar
21
+ public static int SCREEN_WIDTH_FULL = DeviceUtil.getDeviceWidth();
22
+ public static final int RECYCLER_VIEW_PADDING = UnitConverter.dpToPx(35);
23
+ public static int VIDEO_FRAMES_WIDTH = SCREEN_WIDTH_FULL - RECYCLER_VIEW_PADDING * 2;
24
+ public static int mThumbWidth = 0; // make it automatic
25
+ public static final int THUMB_HEIGHT = UnitConverter.dpToPx(50); // x2 for better resolution
26
+ private static final int THUMB_RESOLUTION_RES = 2; // double thumb resolution for better quality
27
+
28
+ public static void trim(String inputFile, String outputFile, int videoDuration, long startMs, long endMs, final VideoTrimListener callback) {
29
+ callback.onStartTrim();
30
+ BackgroundExecutor.execute(new BackgroundExecutor.Task("", 0L, "") {
31
+ @Override
32
+ public void execute() {
33
+ try {
34
+ VideoTrimUtil.trimVideo(inputFile, outputFile, startMs, endMs);
35
+ callback.onTrimmingProgress(100);
36
+ callback.onFinishTrim(outputFile);
37
+ } catch (Exception e) {
38
+ callback.onError(e.getMessage() != null ? e.getMessage() : "Video trim failed");
39
+ }
40
+ }
41
+ });
42
+ }
43
+
44
+ public static void shootVideoThumbInBackground(final Context context, final Uri videoUri, final int totalThumbsCount, final long startPosition,
45
+ final long endPosition, final SingleCallback<Bitmap, Integer> callback) {
46
+ BackgroundExecutor.execute(new BackgroundExecutor.Task("", 0L, "") {
47
+ @Override public void execute() {
48
+ try {
49
+ MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
50
+ mediaMetadataRetriever.setDataSource(context, videoUri);
51
+ long interval = (endPosition - startPosition) / Math.max(1, (totalThumbsCount - 1));
52
+ for (long i = 0; i < totalThumbsCount; ++i) {
53
+ long frameTime = startPosition + interval * i;
54
+ Bitmap bitmap = mediaMetadataRetriever.getFrameAtTime(frameTime * 1000, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
55
+ if(bitmap == null) continue;
56
+ try {
57
+ bitmap = Bitmap.createScaledBitmap(bitmap, mThumbWidth * THUMB_RESOLUTION_RES, THUMB_HEIGHT * THUMB_RESOLUTION_RES, false);
58
+ } catch (final Throwable t) {
59
+ t.printStackTrace();
60
+ }
61
+ callback.onSingleCallback(bitmap, (int) interval);
62
+ }
63
+ mediaMetadataRetriever.release();
64
+ } catch (final Throwable e) {
65
+ Thread.getDefaultUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
66
+ }
67
+ }
68
+ });
69
+ }
70
+ }