react-native-video-trim 1.0.1 → 1.0.2
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 +12 -2
- package/android/build.gradle +1 -2
- package/android/src/main/java/com/videotrim/VideoTrimModule.java +25 -43
- package/android/src/main/java/com/videotrim/adapters/VideoTrimmerAdapter.java +4 -10
- package/android/src/main/java/com/videotrim/utils/StorageUtil.java +0 -6
- package/android/src/main/java/com/videotrim/utils/VideoTrimmerUtil.java +3 -7
- package/android/src/main/java/com/videotrim/widgets/VideoTrimmerView.java +50 -6
- package/lib/commonjs/index.js +21 -2
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/index.js +22 -3
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/index.d.ts +1 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/index.tsx +29 -3
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# React Native Video Trim
|
|
2
2
|
<div align="center">
|
|
3
3
|
<h2>Video trimmer for your React Native app</h2>
|
|
4
4
|
|
|
@@ -16,13 +16,23 @@ npm install react-native-video-trim
|
|
|
16
16
|
yarn add react-native-video-trim
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
+
Next for Android, under `android/app/build.gradle`, add `jcenter` to `repositories` this:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
repositories {
|
|
23
|
+
... other repos if any
|
|
24
|
+
jcenter()
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
19
28
|
Next install CocoaPods deps:
|
|
20
29
|
|
|
21
30
|
```
|
|
22
|
-
|
|
31
|
+
npx pod-install ios
|
|
23
32
|
```
|
|
24
33
|
|
|
25
34
|
## Usage
|
|
35
|
+
> Note that for iOS you have to try on real device
|
|
26
36
|
|
|
27
37
|
```js
|
|
28
38
|
import { showEditor } from 'react-native-video-trim';
|
package/android/build.gradle
CHANGED
|
@@ -91,8 +91,7 @@ dependencies {
|
|
|
91
91
|
//noinspection GradleDynamicVersion
|
|
92
92
|
implementation "com.facebook.react:react-native:+"
|
|
93
93
|
implementation 'com.github.iknow4:android-utils-sdk:1.1.2'
|
|
94
|
-
implementation 'androidx.recyclerview:recyclerview:1.1
|
|
95
|
-
implementation 'com.guolindev.permissionx:permissionx:1.7.1'
|
|
94
|
+
implementation 'androidx.recyclerview:recyclerview:1.3.1'
|
|
96
95
|
implementation 'com.arthenica:ffmpeg-kit-full:5.1.LTS'
|
|
97
96
|
}
|
|
98
97
|
|
|
@@ -2,7 +2,6 @@ package com.videotrim;
|
|
|
2
2
|
|
|
3
3
|
import static com.facebook.react.bridge.UiThreadUtil.runOnUiThread;
|
|
4
4
|
|
|
5
|
-
import android.Manifest;
|
|
6
5
|
import android.app.Activity;
|
|
7
6
|
import android.content.res.ColorStateList;
|
|
8
7
|
import android.graphics.Color;
|
|
@@ -18,7 +17,6 @@ import android.widget.TextView;
|
|
|
18
17
|
import androidx.annotation.NonNull;
|
|
19
18
|
import androidx.annotation.Nullable;
|
|
20
19
|
import androidx.appcompat.app.AlertDialog;
|
|
21
|
-
import androidx.fragment.app.FragmentActivity;
|
|
22
20
|
|
|
23
21
|
import com.facebook.react.bridge.Arguments;
|
|
24
22
|
import com.facebook.react.bridge.LifecycleEventListener;
|
|
@@ -31,12 +29,10 @@ import com.facebook.react.bridge.ReadableMap;
|
|
|
31
29
|
import com.facebook.react.bridge.WritableMap;
|
|
32
30
|
import com.facebook.react.module.annotations.ReactModule;
|
|
33
31
|
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
|
34
|
-
import com.permissionx.guolindev.PermissionX;
|
|
35
32
|
import com.videotrim.interfaces.VideoTrimListener;
|
|
36
33
|
import com.videotrim.utils.StorageUtil;
|
|
37
34
|
import com.videotrim.widgets.VideoTrimmerView;
|
|
38
35
|
|
|
39
|
-
import java.io.File;
|
|
40
36
|
import java.io.IOException;
|
|
41
37
|
import iknow.android.utils.BaseUtils;
|
|
42
38
|
|
|
@@ -52,6 +48,8 @@ public class VideoTrimModule extends ReactContextBaseJavaModule implements Video
|
|
|
52
48
|
private int mMaxDuration = 0;
|
|
53
49
|
private int listenerCount = 0;
|
|
54
50
|
|
|
51
|
+
private Promise showEditorPromise;
|
|
52
|
+
|
|
55
53
|
public VideoTrimModule(ReactApplicationContext reactContext) {
|
|
56
54
|
super(reactContext);
|
|
57
55
|
}
|
|
@@ -64,7 +62,8 @@ public class VideoTrimModule extends ReactContextBaseJavaModule implements Video
|
|
|
64
62
|
|
|
65
63
|
|
|
66
64
|
@ReactMethod
|
|
67
|
-
public void showEditor(String videoPath, ReadableMap config) {
|
|
65
|
+
public void showEditor(String videoPath, ReadableMap config, Promise promise) {
|
|
66
|
+
showEditorPromise = promise;
|
|
68
67
|
if (trimmerView != null || alertDialog != null) {
|
|
69
68
|
return;
|
|
70
69
|
}
|
|
@@ -103,17 +102,6 @@ public class VideoTrimModule extends ReactContextBaseJavaModule implements Video
|
|
|
103
102
|
alertDialog.setView(trimmerView);
|
|
104
103
|
alertDialog.show();
|
|
105
104
|
|
|
106
|
-
|
|
107
|
-
if (this.mSaveToPhoto) {
|
|
108
|
-
// some how this is not fired when user first install app and tap Allow
|
|
109
|
-
// so that we just request permission first, and later it'll be able to save to Gallery immediately
|
|
110
|
-
PermissionX.init((FragmentActivity) getReactApplicationContext().getCurrentActivity())
|
|
111
|
-
.permissions(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
112
|
-
.request((allGranted, grantedList, deniedList) -> {
|
|
113
|
-
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
|
|
117
105
|
// this is to ensure to release resource if dialog is dismissed in unexpected way (Eg. open control/notification center by dragging from top of screen)
|
|
118
106
|
alertDialog.setOnDismissListener(dialog -> {
|
|
119
107
|
// This is called in same thread as the trimmer view -> UI thread
|
|
@@ -170,35 +158,13 @@ public class VideoTrimModule extends ReactContextBaseJavaModule implements Video
|
|
|
170
158
|
|
|
171
159
|
@Override public void onFinishTrim(String in) {
|
|
172
160
|
runOnUiThread(() -> {
|
|
161
|
+
WritableMap map = Arguments.createMap();
|
|
162
|
+
map.putString("outputPath", in);
|
|
163
|
+
sendEvent(getReactApplicationContext(), "onFinishTrimming", map);
|
|
173
164
|
if (mSaveToPhoto) {
|
|
174
|
-
|
|
175
|
-
.permissions(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
176
|
-
.request((allGranted, grantedList, deniedList) -> {
|
|
177
|
-
// some how this is not fired when user first tap Allow
|
|
178
|
-
if (allGranted) {
|
|
179
|
-
try {
|
|
180
|
-
StorageUtil.saveVideoToGallery(getReactApplicationContext(), in);
|
|
181
|
-
WritableMap map = Arguments.createMap();
|
|
182
|
-
map.putString("outputPath", in);
|
|
183
|
-
sendEvent(getReactApplicationContext(), "onFinishTrimming", map);
|
|
184
|
-
} catch (IOException e) {
|
|
185
|
-
e.printStackTrace();
|
|
186
|
-
WritableMap mapE = Arguments.createMap();
|
|
187
|
-
mapE.putString("message", "Fail while copying file to Gallery");
|
|
188
|
-
sendEvent(getReactApplicationContext(), "onError", mapE);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
this.hideDialog();
|
|
192
|
-
} else {
|
|
193
|
-
WritableMap mapE = Arguments.createMap();
|
|
194
|
-
mapE.putString("message", "Fail to save to Gallery. Please check if you have correct permission");
|
|
195
|
-
sendEvent(getReactApplicationContext(), "onError", mapE);
|
|
196
|
-
}
|
|
197
|
-
});
|
|
165
|
+
showEditorPromise.resolve(in);
|
|
198
166
|
} else {
|
|
199
|
-
|
|
200
|
-
map.putString("outputPath", in);
|
|
201
|
-
sendEvent(getReactApplicationContext(), "onFinishTrimming", map);
|
|
167
|
+
hideDialog();
|
|
202
168
|
}
|
|
203
169
|
});
|
|
204
170
|
}
|
|
@@ -215,6 +181,7 @@ public class VideoTrimModule extends ReactContextBaseJavaModule implements Video
|
|
|
215
181
|
this.hideDialog();
|
|
216
182
|
}
|
|
217
183
|
|
|
184
|
+
@ReactMethod
|
|
218
185
|
private void hideDialog() {
|
|
219
186
|
if (mProgressDialog != null) {
|
|
220
187
|
if (mProgressDialog.isShowing()) mProgressDialog.dismiss();
|
|
@@ -312,4 +279,19 @@ public class VideoTrimModule extends ReactContextBaseJavaModule implements Video
|
|
|
312
279
|
private void isValidVideo(String filePath, Promise promise) {
|
|
313
280
|
promise.resolve(_isValidVideo(filePath));
|
|
314
281
|
}
|
|
282
|
+
|
|
283
|
+
@ReactMethod
|
|
284
|
+
private void saveVideo(String filePath, Promise promise) {
|
|
285
|
+
try {
|
|
286
|
+
StorageUtil.saveVideoToGallery(getReactApplicationContext(), filePath);
|
|
287
|
+
} catch (IOException e) {
|
|
288
|
+
e.printStackTrace();
|
|
289
|
+
WritableMap mapE = Arguments.createMap();
|
|
290
|
+
mapE.putString("message", "Fail while copying file to Gallery");
|
|
291
|
+
sendEvent(getReactApplicationContext(), "onError", mapE);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
this.hideDialog();
|
|
295
|
+
promise.resolve(null);
|
|
296
|
+
}
|
|
315
297
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
package com.videotrim.adapters;
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
import android.content.Context;
|
|
5
4
|
import android.graphics.Bitmap;
|
|
6
5
|
import android.view.LayoutInflater;
|
|
@@ -8,23 +7,18 @@ import android.view.View;
|
|
|
8
7
|
import android.view.ViewGroup;
|
|
9
8
|
import android.widget.ImageView;
|
|
10
9
|
import android.widget.LinearLayout;
|
|
11
|
-
|
|
12
10
|
import androidx.annotation.NonNull;
|
|
13
11
|
import androidx.recyclerview.widget.RecyclerView;
|
|
14
|
-
|
|
15
12
|
import com.videotrim.R;
|
|
16
13
|
import com.videotrim.utils.VideoTrimmerUtil;
|
|
17
|
-
|
|
18
14
|
import java.util.ArrayList;
|
|
19
15
|
import java.util.List;
|
|
20
16
|
|
|
21
17
|
public class VideoTrimmerAdapter extends RecyclerView.Adapter {
|
|
22
|
-
private List<Bitmap> mBitmaps = new ArrayList<>();
|
|
23
|
-
private LayoutInflater mInflater;
|
|
24
|
-
private Context context;
|
|
18
|
+
private final List<Bitmap> mBitmaps = new ArrayList<>();
|
|
19
|
+
private final LayoutInflater mInflater;
|
|
25
20
|
|
|
26
21
|
public VideoTrimmerAdapter(Context context) {
|
|
27
|
-
this.context = context;
|
|
28
22
|
this.mInflater = LayoutInflater.from(context);
|
|
29
23
|
}
|
|
30
24
|
|
|
@@ -42,10 +36,10 @@ public class VideoTrimmerAdapter extends RecyclerView.Adapter {
|
|
|
42
36
|
|
|
43
37
|
public void addBitmaps(Bitmap bitmap) {
|
|
44
38
|
mBitmaps.add(bitmap);
|
|
45
|
-
|
|
39
|
+
notifyItemInserted(mBitmaps.size() - 1);
|
|
46
40
|
}
|
|
47
41
|
|
|
48
|
-
private final class TrimmerViewHolder extends RecyclerView.ViewHolder {
|
|
42
|
+
private static final class TrimmerViewHolder extends RecyclerView.ViewHolder {
|
|
49
43
|
ImageView thumbImageView;
|
|
50
44
|
|
|
51
45
|
TrimmerViewHolder(View itemView) {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
package com.videotrim.utils;
|
|
2
2
|
|
|
3
|
-
import android.Manifest;
|
|
4
3
|
import android.annotation.SuppressLint;
|
|
5
4
|
import android.content.ContentValues;
|
|
6
5
|
import android.content.Context;
|
|
@@ -11,12 +10,7 @@ import android.os.Environment;
|
|
|
11
10
|
import android.provider.MediaStore;
|
|
12
11
|
import android.text.TextUtils;
|
|
13
12
|
import android.util.Log;
|
|
14
|
-
|
|
15
|
-
import androidx.fragment.app.FragmentActivity;
|
|
16
|
-
|
|
17
13
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
18
|
-
import com.permissionx.guolindev.PermissionX;
|
|
19
|
-
|
|
20
14
|
import java.io.File;
|
|
21
15
|
import java.io.FileInputStream;
|
|
22
16
|
import java.io.FileOutputStream;
|
|
@@ -32,10 +32,10 @@ public class VideoTrimmerUtil {
|
|
|
32
32
|
// public static final int VIDEO_MAX_TIME = 10;// max 10 seconds for trimming
|
|
33
33
|
// public static final long MAX_SHOOT_DURATION = VIDEO_MAX_TIME * 1000L;
|
|
34
34
|
public static long maxShootDuration = 10 * 1000L;
|
|
35
|
-
public static
|
|
36
|
-
public static
|
|
35
|
+
public static int MAX_COUNT_RANGE = 10; // how many images in the highlight range of seek bar
|
|
36
|
+
public static int SCREEN_WIDTH_FULL = DeviceUtil.getDeviceWidth();
|
|
37
37
|
public static final int RECYCLER_VIEW_PADDING = UnitConverter.dpToPx(35);
|
|
38
|
-
public static
|
|
38
|
+
public static int VIDEO_FRAMES_WIDTH = SCREEN_WIDTH_FULL - RECYCLER_VIEW_PADDING * 2;
|
|
39
39
|
// public static final int THUMB_WIDTH = (SCREEN_WIDTH_FULL - RECYCLER_VIEW_PADDING * 2) / VIDEO_MAX_TIME;
|
|
40
40
|
public static int mThumbWidth = 0; // make it automatic
|
|
41
41
|
public static final int THUMB_HEIGHT = UnitConverter.dpToPx(50); // x2 for better resolution
|
|
@@ -88,10 +88,6 @@ public class VideoTrimmerUtil {
|
|
|
88
88
|
Bitmap bitmap = mediaMetadataRetriever.getFrameAtTime(frameTime * 1000, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
|
|
89
89
|
if(bitmap == null) continue;
|
|
90
90
|
try {
|
|
91
|
-
if (mThumbWidth <= 0) { // only take first item
|
|
92
|
-
int width = THUMB_HEIGHT * bitmap.getWidth() / bitmap.getHeight();
|
|
93
|
-
mThumbWidth = width;
|
|
94
|
-
}
|
|
95
91
|
bitmap = Bitmap.createScaledBitmap(bitmap, mThumbWidth * THUMB_RESOLUTION_RES, THUMB_HEIGHT * THUMB_RESOLUTION_RES, false);
|
|
96
92
|
} catch (final Throwable t) {
|
|
97
93
|
t.printStackTrace();
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
package com.videotrim.widgets;
|
|
2
2
|
|
|
3
|
-
import static com.videotrim.utils.VideoTrimmerUtil.MAX_COUNT_RANGE;
|
|
4
3
|
import static com.videotrim.utils.VideoTrimmerUtil.RECYCLER_VIEW_PADDING;
|
|
5
4
|
import static com.videotrim.utils.VideoTrimmerUtil.VIDEO_FRAMES_WIDTH;
|
|
6
5
|
|
|
7
6
|
import android.animation.ValueAnimator;
|
|
8
7
|
import android.content.Context;
|
|
8
|
+
import android.content.pm.ActivityInfo;
|
|
9
|
+
import android.content.res.Configuration;
|
|
10
|
+
import android.graphics.Bitmap;
|
|
11
|
+
import android.media.MediaMetadataRetriever;
|
|
9
12
|
import android.media.MediaPlayer;
|
|
10
13
|
import android.net.Uri;
|
|
11
14
|
import android.os.Handler;
|
|
@@ -33,6 +36,7 @@ import com.videotrim.interfaces.VideoTrimListener;
|
|
|
33
36
|
import com.videotrim.utils.StorageUtil;
|
|
34
37
|
import com.videotrim.utils.VideoTrimmerUtil;
|
|
35
38
|
|
|
39
|
+
import iknow.android.utils.DeviceUtil;
|
|
36
40
|
import iknow.android.utils.thread.BackgroundExecutor;
|
|
37
41
|
import iknow.android.utils.thread.UiThreadExecutor;
|
|
38
42
|
|
|
@@ -76,6 +80,7 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
76
80
|
private Boolean mIsPrepared = false;
|
|
77
81
|
private int mMaxDuration = 0;
|
|
78
82
|
|
|
83
|
+
|
|
79
84
|
public VideoTrimmerView(ReactApplicationContext context, AttributeSet attrs) {
|
|
80
85
|
this(context, attrs, 0);
|
|
81
86
|
}
|
|
@@ -91,6 +96,9 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
91
96
|
|
|
92
97
|
private void init(ReactApplicationContext context) {
|
|
93
98
|
this.mContext = context;
|
|
99
|
+
|
|
100
|
+
// listen to onConfigurationChanged doesn't work for this, it runs too soon
|
|
101
|
+
context.getCurrentActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
|
94
102
|
LayoutInflater.from(context).inflate(R.layout.video_trimmer_view, this, true);
|
|
95
103
|
|
|
96
104
|
mLinearVideo = findViewById(R.id.layout_surface_view);
|
|
@@ -110,13 +118,19 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
110
118
|
private void initRangeSeekBarView() {
|
|
111
119
|
if(mRangeSeekBarView != null) return;
|
|
112
120
|
mLeftProgressPos = 0;
|
|
121
|
+
|
|
122
|
+
VideoTrimmerUtil.SCREEN_WIDTH_FULL = this.getScreenWidthInPortraitMode();
|
|
123
|
+
VideoTrimmerUtil.VIDEO_FRAMES_WIDTH = VideoTrimmerUtil.SCREEN_WIDTH_FULL - RECYCLER_VIEW_PADDING * 2;
|
|
124
|
+
VideoTrimmerUtil.MAX_COUNT_RANGE = Math.max(((int) VIDEO_FRAMES_WIDTH / VideoTrimmerUtil.mThumbWidth), VideoTrimmerUtil.MAX_COUNT_RANGE);
|
|
125
|
+
|
|
113
126
|
if (mDuration <= VideoTrimmerUtil.maxShootDuration) {
|
|
114
|
-
mThumbsTotalCount = MAX_COUNT_RANGE;
|
|
127
|
+
mThumbsTotalCount = VideoTrimmerUtil.MAX_COUNT_RANGE;
|
|
115
128
|
mRightProgressPos = mDuration;
|
|
116
129
|
} else {
|
|
117
|
-
mThumbsTotalCount = (int) (mDuration * 1.0f / (VideoTrimmerUtil.maxShootDuration * 1.0f) * MAX_COUNT_RANGE);
|
|
130
|
+
mThumbsTotalCount = (int) (mDuration * 1.0f / (VideoTrimmerUtil.maxShootDuration * 1.0f) * VideoTrimmerUtil.MAX_COUNT_RANGE);
|
|
118
131
|
mRightProgressPos = VideoTrimmerUtil.maxShootDuration;
|
|
119
132
|
}
|
|
133
|
+
|
|
120
134
|
mVideoThumbRecyclerView.addItemDecoration(new SpacesItemDecoration2(RECYCLER_VIEW_PADDING, mThumbsTotalCount));
|
|
121
135
|
mRangeSeekBarView = new RangeSeekBarView(mContext, mLeftProgressPos, mRightProgressPos);
|
|
122
136
|
mRangeSeekBarView.setSelectedMinValue(mLeftProgressPos);
|
|
@@ -126,8 +140,8 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
126
140
|
mRangeSeekBarView.setNotifyWhileDragging(true);
|
|
127
141
|
mRangeSeekBarView.setOnRangeSeekBarChangeListener(mOnRangeSeekBarChangeListener);
|
|
128
142
|
mSeekBarLayout.addView(mRangeSeekBarView);
|
|
129
|
-
if(mThumbsTotalCount - MAX_COUNT_RANGE > 0) {
|
|
130
|
-
mAverageMsPx = (mDuration - VideoTrimmerUtil.maxShootDuration) / (float) (mThumbsTotalCount - MAX_COUNT_RANGE);
|
|
143
|
+
if(mThumbsTotalCount - VideoTrimmerUtil.MAX_COUNT_RANGE > 0) {
|
|
144
|
+
mAverageMsPx = (mDuration - VideoTrimmerUtil.maxShootDuration) / (float) (mThumbsTotalCount - VideoTrimmerUtil.MAX_COUNT_RANGE);
|
|
131
145
|
} else {
|
|
132
146
|
mAverageMsPx = 0f;
|
|
133
147
|
}
|
|
@@ -174,6 +188,14 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
174
188
|
mDuration = mVideoView.getDuration();
|
|
175
189
|
|
|
176
190
|
VideoTrimmerUtil.maxShootDuration = mMaxDuration > 0 ? Math.min(mMaxDuration * 1000L, mDuration) : mDuration;
|
|
191
|
+
|
|
192
|
+
MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
|
|
193
|
+
mediaMetadataRetriever.setDataSource(mContext, mSourceUri);
|
|
194
|
+
// take first frame
|
|
195
|
+
Bitmap bitmap = mediaMetadataRetriever.getFrameAtTime(0, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
|
|
196
|
+
int width = VideoTrimmerUtil.THUMB_HEIGHT * bitmap.getWidth() / bitmap.getHeight();
|
|
197
|
+
VideoTrimmerUtil.mThumbWidth = width;
|
|
198
|
+
|
|
177
199
|
if (!getRestoreState()) {
|
|
178
200
|
seekTo((int) mRedProgressBarPos);
|
|
179
201
|
} else {
|
|
@@ -434,11 +456,33 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
434
456
|
}
|
|
435
457
|
}
|
|
436
458
|
|
|
459
|
+
@Override
|
|
460
|
+
protected void onDetachedFromWindow() {
|
|
461
|
+
super.onDetachedFromWindow();
|
|
462
|
+
mContext.getCurrentActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
|
|
463
|
+
}
|
|
464
|
+
|
|
437
465
|
/**
|
|
438
|
-
* Cancel trim thread
|
|
466
|
+
* Cancel trim thread execute action when finish
|
|
439
467
|
*/
|
|
440
468
|
@Override public void onDestroy() {
|
|
441
469
|
BackgroundExecutor.cancelAll("", true);
|
|
442
470
|
UiThreadExecutor.cancelAll("");
|
|
471
|
+
mContext.getCurrentActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
private int getScreenWidthInPortraitMode() {
|
|
475
|
+
int screenWidth = DeviceUtil.getDeviceWidth();
|
|
476
|
+
int screenHeight = DeviceUtil.getDeviceHeight();
|
|
477
|
+
|
|
478
|
+
// Check the current orientation
|
|
479
|
+
int currentOrientation = getResources().getConfiguration().orientation;
|
|
480
|
+
|
|
481
|
+
// Swap width and height if the current orientation is landscape
|
|
482
|
+
if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
|
|
483
|
+
return screenHeight;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
return screenWidth;
|
|
443
487
|
}
|
|
444
488
|
}
|
package/lib/commonjs/index.js
CHANGED
|
@@ -15,16 +15,35 @@ const VideoTrim = _reactNative.NativeModules.VideoTrim ? _reactNative.NativeModu
|
|
|
15
15
|
throw new Error(LINKING_ERROR);
|
|
16
16
|
}
|
|
17
17
|
});
|
|
18
|
-
function showEditor(videoPath) {
|
|
18
|
+
async function showEditor(videoPath) {
|
|
19
19
|
let config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
20
20
|
const {
|
|
21
21
|
maxDuration,
|
|
22
22
|
saveToPhoto = true
|
|
23
23
|
} = config;
|
|
24
|
-
VideoTrim.showEditor(videoPath, {
|
|
24
|
+
const outputPath = await VideoTrim.showEditor(videoPath, {
|
|
25
25
|
saveToPhoto,
|
|
26
26
|
maxDuration
|
|
27
27
|
});
|
|
28
|
+
if (_reactNative.Platform.OS === 'android' && saveToPhoto) {
|
|
29
|
+
try {
|
|
30
|
+
const granted = await _reactNative.PermissionsAndroid.request(_reactNative.PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE, {
|
|
31
|
+
title: 'Video Trimmer Camera Access Required',
|
|
32
|
+
message: 'Grant access to your Camera to write output Video',
|
|
33
|
+
buttonNeutral: 'Ask Me Later',
|
|
34
|
+
buttonNegative: 'Cancel',
|
|
35
|
+
buttonPositive: 'OK'
|
|
36
|
+
});
|
|
37
|
+
if (granted === _reactNative.PermissionsAndroid.RESULTS.GRANTED) {
|
|
38
|
+
await VideoTrim.saveVideo(outputPath);
|
|
39
|
+
} else {
|
|
40
|
+
VideoTrim.hideDialog();
|
|
41
|
+
throw new Error('Camera permission denied');
|
|
42
|
+
}
|
|
43
|
+
} catch (err) {
|
|
44
|
+
throw err;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
28
47
|
}
|
|
29
48
|
function isValidVideo(videoPath) {
|
|
30
49
|
return VideoTrim.isValidVideo(videoPath);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_reactNative","require","LINKING_ERROR","Platform","select","ios","default","VideoTrim","NativeModules","Proxy","get","Error","showEditor","videoPath","config","arguments","length","undefined","maxDuration","saveToPhoto","isValidVideo"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAEA,MAAMC,aAAa,GAChB,kFAAiF,GAClFC,qBAAQ,CAACC,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,+BAA+B;AAEjC,MAAMC,SAAS,GAAGC,0BAAa,CAACD,SAAS,GACrCC,0BAAa,CAACD,SAAS,GACvB,IAAIE,KAAK,CACP,CAAC,CAAC,EACF;EACEC,GAAGA,CAAA,EAAG;IACJ,MAAM,IAAIC,KAAK,CAACT,aAAa,CAAC;EAChC;AACF,CACF,CAAC;AAOE,
|
|
1
|
+
{"version":3,"names":["_reactNative","require","LINKING_ERROR","Platform","select","ios","default","VideoTrim","NativeModules","Proxy","get","Error","showEditor","videoPath","config","arguments","length","undefined","maxDuration","saveToPhoto","outputPath","OS","granted","PermissionsAndroid","request","PERMISSIONS","WRITE_EXTERNAL_STORAGE","title","message","buttonNeutral","buttonNegative","buttonPositive","RESULTS","GRANTED","saveVideo","hideDialog","err","isValidVideo"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAEA,MAAMC,aAAa,GAChB,kFAAiF,GAClFC,qBAAQ,CAACC,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,+BAA+B;AAEjC,MAAMC,SAAS,GAAGC,0BAAa,CAACD,SAAS,GACrCC,0BAAa,CAACD,SAAS,GACvB,IAAIE,KAAK,CACP,CAAC,CAAC,EACF;EACEC,GAAGA,CAAA,EAAG;IACJ,MAAM,IAAIC,KAAK,CAACT,aAAa,CAAC;EAChC;AACF,CACF,CAAC;AAOE,eAAeU,UAAUA,CAC9BC,SAAiB,EAEF;EAAA,IADfC,MAAoB,GAAAC,SAAA,CAAAC,MAAA,QAAAD,SAAA,QAAAE,SAAA,GAAAF,SAAA,MAAG,CAAC,CAAC;EAEzB,MAAM;IAAEG,WAAW;IAAEC,WAAW,GAAG;EAAK,CAAC,GAAGL,MAAM;EAClD,MAAMM,UAAU,GAAG,MAAMb,SAAS,CAACK,UAAU,CAACC,SAAS,EAAE;IACvDM,WAAW;IACXD;EACF,CAAC,CAAC;EAEF,IAAIf,qBAAQ,CAACkB,EAAE,KAAK,SAAS,IAAIF,WAAW,EAAE;IAC5C,IAAI;MACF,MAAMG,OAAO,GAAG,MAAMC,+BAAkB,CAACC,OAAO,CAC9CD,+BAAkB,CAACE,WAAW,CAACC,sBAAsB,EACrD;QACEC,KAAK,EAAE,sCAAsC;QAC7CC,OAAO,EAAE,mDAAmD;QAC5DC,aAAa,EAAE,cAAc;QAC7BC,cAAc,EAAE,QAAQ;QACxBC,cAAc,EAAE;MAClB,CACF,CAAC;MACD,IAAIT,OAAO,KAAKC,+BAAkB,CAACS,OAAO,CAACC,OAAO,EAAE;QAClD,MAAM1B,SAAS,CAAC2B,SAAS,CAACd,UAAU,CAAC;MACvC,CAAC,MAAM;QACLb,SAAS,CAAC4B,UAAU,CAAC,CAAC;QACtB,MAAM,IAAIxB,KAAK,CAAC,0BAA0B,CAAC;MAC7C;IACF,CAAC,CAAC,OAAOyB,GAAG,EAAE;MACZ,MAAMA,GAAG;IACX;EACF;AACF;AAEO,SAASC,YAAYA,CAACxB,SAAiB,EAAoB;EAChE,OAAON,SAAS,CAAC8B,YAAY,CAACxB,SAAS,CAAC;AAC1C"}
|
package/lib/module/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NativeModules, Platform } from 'react-native';
|
|
1
|
+
import { NativeModules, PermissionsAndroid, Platform } from 'react-native';
|
|
2
2
|
const LINKING_ERROR = `The package 'react-native-video-trim' doesn't seem to be linked. Make sure: \n\n` + Platform.select({
|
|
3
3
|
ios: "- You have run 'pod install'\n",
|
|
4
4
|
default: ''
|
|
@@ -8,16 +8,35 @@ const VideoTrim = NativeModules.VideoTrim ? NativeModules.VideoTrim : new Proxy(
|
|
|
8
8
|
throw new Error(LINKING_ERROR);
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
|
-
export function showEditor(videoPath) {
|
|
11
|
+
export async function showEditor(videoPath) {
|
|
12
12
|
let config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
13
13
|
const {
|
|
14
14
|
maxDuration,
|
|
15
15
|
saveToPhoto = true
|
|
16
16
|
} = config;
|
|
17
|
-
VideoTrim.showEditor(videoPath, {
|
|
17
|
+
const outputPath = await VideoTrim.showEditor(videoPath, {
|
|
18
18
|
saveToPhoto,
|
|
19
19
|
maxDuration
|
|
20
20
|
});
|
|
21
|
+
if (Platform.OS === 'android' && saveToPhoto) {
|
|
22
|
+
try {
|
|
23
|
+
const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE, {
|
|
24
|
+
title: 'Video Trimmer Camera Access Required',
|
|
25
|
+
message: 'Grant access to your Camera to write output Video',
|
|
26
|
+
buttonNeutral: 'Ask Me Later',
|
|
27
|
+
buttonNegative: 'Cancel',
|
|
28
|
+
buttonPositive: 'OK'
|
|
29
|
+
});
|
|
30
|
+
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
|
|
31
|
+
await VideoTrim.saveVideo(outputPath);
|
|
32
|
+
} else {
|
|
33
|
+
VideoTrim.hideDialog();
|
|
34
|
+
throw new Error('Camera permission denied');
|
|
35
|
+
}
|
|
36
|
+
} catch (err) {
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
21
40
|
}
|
|
22
41
|
export function isValidVideo(videoPath) {
|
|
23
42
|
return VideoTrim.isValidVideo(videoPath);
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["NativeModules","Platform","LINKING_ERROR","select","ios","default","VideoTrim","Proxy","get","Error","showEditor","videoPath","config","arguments","length","undefined","maxDuration","saveToPhoto","isValidVideo"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"AAAA,SAASA,aAAa,EAAEC,QAAQ,QAAQ,cAAc;
|
|
1
|
+
{"version":3,"names":["NativeModules","PermissionsAndroid","Platform","LINKING_ERROR","select","ios","default","VideoTrim","Proxy","get","Error","showEditor","videoPath","config","arguments","length","undefined","maxDuration","saveToPhoto","outputPath","OS","granted","request","PERMISSIONS","WRITE_EXTERNAL_STORAGE","title","message","buttonNeutral","buttonNegative","buttonPositive","RESULTS","GRANTED","saveVideo","hideDialog","err","isValidVideo"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"AAAA,SAASA,aAAa,EAAEC,kBAAkB,EAAEC,QAAQ,QAAQ,cAAc;AAE1E,MAAMC,aAAa,GAChB,kFAAiF,GAClFD,QAAQ,CAACE,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,+BAA+B;AAEjC,MAAMC,SAAS,GAAGP,aAAa,CAACO,SAAS,GACrCP,aAAa,CAACO,SAAS,GACvB,IAAIC,KAAK,CACP,CAAC,CAAC,EACF;EACEC,GAAGA,CAAA,EAAG;IACJ,MAAM,IAAIC,KAAK,CAACP,aAAa,CAAC;EAChC;AACF,CACF,CAAC;AAOL,OAAO,eAAeQ,UAAUA,CAC9BC,SAAiB,EAEF;EAAA,IADfC,MAAoB,GAAAC,SAAA,CAAAC,MAAA,QAAAD,SAAA,QAAAE,SAAA,GAAAF,SAAA,MAAG,CAAC,CAAC;EAEzB,MAAM;IAAEG,WAAW;IAAEC,WAAW,GAAG;EAAK,CAAC,GAAGL,MAAM;EAClD,MAAMM,UAAU,GAAG,MAAMZ,SAAS,CAACI,UAAU,CAACC,SAAS,EAAE;IACvDM,WAAW;IACXD;EACF,CAAC,CAAC;EAEF,IAAIf,QAAQ,CAACkB,EAAE,KAAK,SAAS,IAAIF,WAAW,EAAE;IAC5C,IAAI;MACF,MAAMG,OAAO,GAAG,MAAMpB,kBAAkB,CAACqB,OAAO,CAC9CrB,kBAAkB,CAACsB,WAAW,CAACC,sBAAsB,EACrD;QACEC,KAAK,EAAE,sCAAsC;QAC7CC,OAAO,EAAE,mDAAmD;QAC5DC,aAAa,EAAE,cAAc;QAC7BC,cAAc,EAAE,QAAQ;QACxBC,cAAc,EAAE;MAClB,CACF,CAAC;MACD,IAAIR,OAAO,KAAKpB,kBAAkB,CAAC6B,OAAO,CAACC,OAAO,EAAE;QAClD,MAAMxB,SAAS,CAACyB,SAAS,CAACb,UAAU,CAAC;MACvC,CAAC,MAAM;QACLZ,SAAS,CAAC0B,UAAU,CAAC,CAAC;QACtB,MAAM,IAAIvB,KAAK,CAAC,0BAA0B,CAAC;MAC7C;IACF,CAAC,CAAC,OAAOwB,GAAG,EAAE;MACZ,MAAMA,GAAG;IACX;EACF;AACF;AAEA,OAAO,SAASC,YAAYA,CAACvB,SAAiB,EAAoB;EAChE,OAAOL,SAAS,CAAC4B,YAAY,CAACvB,SAAS,CAAC;AAC1C"}
|
|
@@ -2,6 +2,6 @@ export interface EditorConfig {
|
|
|
2
2
|
saveToPhoto?: boolean;
|
|
3
3
|
maxDuration?: number;
|
|
4
4
|
}
|
|
5
|
-
export declare function showEditor(videoPath: string, config?: EditorConfig): void
|
|
5
|
+
export declare function showEditor(videoPath: string, config?: EditorConfig): Promise<void>;
|
|
6
6
|
export declare function isValidVideo(videoPath: string): Promise<boolean>;
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAmBA,MAAM,WAAW,YAAY;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAmBA,MAAM,WAAW,YAAY;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,UAAU,CAC9B,SAAS,EAAE,MAAM,EACjB,MAAM,GAAE,YAAiB,GACxB,OAAO,CAAC,IAAI,CAAC,CA6Bf;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAEhE"}
|
package/package.json
CHANGED
package/src/index.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NativeModules, Platform } from 'react-native';
|
|
1
|
+
import { NativeModules, PermissionsAndroid, Platform } from 'react-native';
|
|
2
2
|
|
|
3
3
|
const LINKING_ERROR =
|
|
4
4
|
`The package 'react-native-video-trim' doesn't seem to be linked. Make sure: \n\n` +
|
|
@@ -22,12 +22,38 @@ export interface EditorConfig {
|
|
|
22
22
|
maxDuration?: number;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
export function showEditor(
|
|
25
|
+
export async function showEditor(
|
|
26
|
+
videoPath: string,
|
|
27
|
+
config: EditorConfig = {}
|
|
28
|
+
): Promise<void> {
|
|
26
29
|
const { maxDuration, saveToPhoto = true } = config;
|
|
27
|
-
VideoTrim.showEditor(videoPath, {
|
|
30
|
+
const outputPath = await VideoTrim.showEditor(videoPath, {
|
|
28
31
|
saveToPhoto,
|
|
29
32
|
maxDuration,
|
|
30
33
|
});
|
|
34
|
+
|
|
35
|
+
if (Platform.OS === 'android' && saveToPhoto) {
|
|
36
|
+
try {
|
|
37
|
+
const granted = await PermissionsAndroid.request(
|
|
38
|
+
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE!,
|
|
39
|
+
{
|
|
40
|
+
title: 'Video Trimmer Camera Access Required',
|
|
41
|
+
message: 'Grant access to your Camera to write output Video',
|
|
42
|
+
buttonNeutral: 'Ask Me Later',
|
|
43
|
+
buttonNegative: 'Cancel',
|
|
44
|
+
buttonPositive: 'OK',
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
|
|
48
|
+
await VideoTrim.saveVideo(outputPath);
|
|
49
|
+
} else {
|
|
50
|
+
VideoTrim.hideDialog();
|
|
51
|
+
throw new Error('Camera permission denied');
|
|
52
|
+
}
|
|
53
|
+
} catch (err) {
|
|
54
|
+
throw err;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
31
57
|
}
|
|
32
58
|
|
|
33
59
|
export function isValidVideo(videoPath: string): Promise<boolean> {
|