react-native-video-trim 1.0.24 → 2.0.0
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 +22 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/videotrim/VideoTrimModule.java +8 -1
- package/android/src/main/java/com/videotrim/interfaces/VideoTrimListener.java +4 -0
- package/android/src/main/java/com/videotrim/utils/VideoTrimmerUtil.java +21 -0
- package/android/src/main/java/com/videotrim/widgets/VideoTrimmerView.java +406 -295
- package/android/src/main/res/drawable/chevron_compact_left.xml +15 -0
- package/android/src/main/res/drawable/chevron_compact_right.xml +15 -0
- package/android/src/main/res/drawable/chevron_right_with_bg.xml +13 -0
- package/android/src/main/res/drawable/pause_fill.xml +15 -0
- package/android/src/main/res/drawable/play_fill.xml +15 -0
- package/android/src/main/res/drawable/rounded_progress_indicator.xml +16 -0
- package/android/src/main/res/drawable/rounded_yellow_left_background.xml +8 -0
- package/android/src/main/res/drawable/rounded_yellow_right_background.xml +8 -0
- package/android/src/main/res/drawable/yellow_border.xml +9 -0
- package/android/src/main/res/layout/video_trimmer_view.xml +148 -76
- package/android/src/main/res/values/colors.xml +15 -13
- package/ios/VideoTrim.swift +26 -2
- package/ios/VideoTrimmerViewController.swift +1 -1
- package/package.json +1 -1
- package/android/src/main/java/com/videotrim/adapters/VideoTrimmerAdapter.java +0 -54
- package/android/src/main/java/com/videotrim/widgets/RangeSeekBarView.java +0 -534
- package/android/src/main/java/com/videotrim/widgets/SpacesItemDecoration2.java +0 -33
- package/android/src/main/java/com/videotrim/widgets/ZVideoView.java +0 -48
- package/android/src/main/res/drawable/ic_video_pause_black.png +0 -0
- package/android/src/main/res/drawable/ic_video_play_black.png +0 -0
- package/android/src/main/res/drawable/ic_video_thumb_handle.png +0 -0
- package/android/src/main/res/drawable/icon_seek_bar.png +0 -0
- package/android/src/main/res/layout/video_thumb_item_layout.xml +0 -16
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
package com.videotrim.widgets;
|
|
2
2
|
|
|
3
|
+
import static com.facebook.react.bridge.UiThreadUtil.runOnUiThread;
|
|
3
4
|
import static com.videotrim.utils.VideoTrimmerUtil.RECYCLER_VIEW_PADDING;
|
|
4
5
|
import static com.videotrim.utils.VideoTrimmerUtil.VIDEO_FRAMES_WIDTH;
|
|
5
6
|
|
|
6
|
-
import android.animation.ValueAnimator;
|
|
7
7
|
import android.content.Context;
|
|
8
8
|
import android.content.pm.ActivityInfo;
|
|
9
9
|
import android.content.res.Configuration;
|
|
@@ -11,80 +11,77 @@ import android.graphics.Bitmap;
|
|
|
11
11
|
import android.media.MediaMetadataRetriever;
|
|
12
12
|
import android.media.MediaPlayer;
|
|
13
13
|
import android.net.Uri;
|
|
14
|
+
import android.os.Build;
|
|
14
15
|
import android.os.Handler;
|
|
16
|
+
import android.os.VibrationEffect;
|
|
17
|
+
import android.os.Vibrator;
|
|
15
18
|
import android.util.AttributeSet;
|
|
19
|
+
import android.util.Log;
|
|
16
20
|
import android.view.LayoutInflater;
|
|
17
21
|
import android.view.MotionEvent;
|
|
18
22
|
import android.view.View;
|
|
19
|
-
import android.view.ViewGroup;
|
|
20
|
-
import android.view.animation.LinearInterpolator;
|
|
21
23
|
import android.widget.FrameLayout;
|
|
22
24
|
import android.widget.ImageView;
|
|
23
25
|
import android.widget.LinearLayout;
|
|
24
26
|
import android.widget.RelativeLayout;
|
|
25
27
|
import android.widget.TextView;
|
|
26
|
-
import android.widget.
|
|
27
|
-
|
|
28
|
-
import androidx.annotation.NonNull;
|
|
29
|
-
import androidx.recyclerview.widget.LinearLayoutManager;
|
|
30
|
-
import androidx.recyclerview.widget.RecyclerView;
|
|
28
|
+
import android.widget.VideoView;
|
|
31
29
|
|
|
32
30
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
33
31
|
import com.facebook.react.bridge.ReadableMap;
|
|
34
32
|
import com.videotrim.R;
|
|
35
|
-
import com.videotrim.adapters.VideoTrimmerAdapter;
|
|
36
33
|
import com.videotrim.interfaces.IVideoTrimmerView;
|
|
37
34
|
import com.videotrim.interfaces.VideoTrimListener;
|
|
38
35
|
import com.videotrim.utils.StorageUtil;
|
|
39
36
|
import com.videotrim.utils.VideoTrimmerUtil;
|
|
40
37
|
|
|
38
|
+
import java.util.Locale;
|
|
39
|
+
|
|
41
40
|
import iknow.android.utils.DeviceUtil;
|
|
42
41
|
import iknow.android.utils.thread.BackgroundExecutor;
|
|
43
42
|
import iknow.android.utils.thread.UiThreadExecutor;
|
|
44
43
|
|
|
45
|
-
/**
|
|
46
|
-
* Author:J.Chou
|
|
47
|
-
* Date: 2016.08.01 2:23 PM
|
|
48
|
-
* Email: who_know_me@163.com
|
|
49
|
-
* Describe:
|
|
50
|
-
*/
|
|
51
44
|
public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
52
45
|
|
|
53
46
|
private static final String TAG = VideoTrimmerView.class.getSimpleName();
|
|
54
47
|
|
|
55
|
-
private final int mMaxWidth = VIDEO_FRAMES_WIDTH;
|
|
56
48
|
private ReactApplicationContext mContext;
|
|
57
|
-
private
|
|
58
|
-
private ZVideoView mVideoView;
|
|
49
|
+
private VideoView mVideoView;
|
|
59
50
|
private ImageView mPlayView;
|
|
60
|
-
private
|
|
61
|
-
private RangeSeekBarView mRangeSeekBarView;
|
|
62
|
-
private LinearLayout mSeekBarLayout;
|
|
63
|
-
private ImageView mRedProgressIcon;
|
|
64
|
-
private float mAverageMsPx;//每毫秒所占的px
|
|
65
|
-
private float averagePxMs;//每px所占用的ms毫秒
|
|
51
|
+
private LinearLayout mThumbnailContainer;
|
|
66
52
|
private Uri mSourceUri;
|
|
67
53
|
private VideoTrimListener mOnTrimVideoListener;
|
|
68
54
|
private int mDuration = 0;
|
|
69
|
-
private VideoTrimmerAdapter mVideoThumbAdapter;
|
|
70
|
-
private boolean isFromRestore = false;
|
|
71
|
-
//new
|
|
72
|
-
private long mLeftProgressPos, mRightProgressPos;
|
|
73
|
-
private long mRedProgressBarPos = 0;
|
|
74
|
-
private long scrollPos = 0;
|
|
75
|
-
private int mScaledTouchSlop;
|
|
76
|
-
private int lastScrollX;
|
|
77
|
-
private int mThumbsTotalCount;
|
|
78
|
-
private ValueAnimator mRedProgressAnimator;
|
|
79
|
-
private final Handler mAnimationHandler = new Handler();
|
|
80
55
|
private Boolean mIsPrepared = false;
|
|
81
|
-
private long mMaxDuration =
|
|
56
|
+
private long mMaxDuration = (long) Double.POSITIVE_INFINITY;
|
|
82
57
|
private long mMinDuration = VideoTrimmerUtil.MIN_SHOOT_DURATION;
|
|
83
58
|
|
|
59
|
+
private final Handler mTimingHandler = new Handler();
|
|
60
|
+
private Runnable mTimingRunnable;
|
|
61
|
+
private static final long TIMING_UPDATE_INTERVAL = 30; // Update every 30 milliseconds
|
|
62
|
+
private TextView currentTimeText;
|
|
63
|
+
private TextView startTimeText;
|
|
64
|
+
private TextView endTimeText;
|
|
65
|
+
private View progressIndicator;
|
|
66
|
+
private View trimmerContainer;
|
|
67
|
+
// background of the trimmer container, its width never changes
|
|
68
|
+
// this is to make sure when we calculate position of the progress indicator, we don't need to consider the width of the trimmer container
|
|
69
|
+
private View trimmerContainerBg;
|
|
70
|
+
private FrameLayout leadingHandle;
|
|
71
|
+
private View trailingHandle;
|
|
72
|
+
private View leadingOverlay;
|
|
73
|
+
private View trailingOverlay;
|
|
74
|
+
private RelativeLayout trimmerContainerWrapper;
|
|
75
|
+
|
|
76
|
+
private long startTime = 0, endTime = 0;
|
|
77
|
+
private Vibrator vibrator;
|
|
78
|
+
private boolean didClampWhilePanning = false;
|
|
79
|
+
|
|
80
|
+
// zoom
|
|
81
|
+
private boolean isZoomedIn = false;
|
|
82
|
+
private final Handler zoomWaitTimer = new Handler();
|
|
83
|
+
private Runnable zoomRunnable;
|
|
84
84
|
|
|
85
|
-
public VideoTrimmerView(ReactApplicationContext context, AttributeSet attrs) {
|
|
86
|
-
this(context, attrs, 0, null);
|
|
87
|
-
}
|
|
88
85
|
public VideoTrimmerView(ReactApplicationContext context, ReadableMap config, AttributeSet attrs) {
|
|
89
86
|
this(context, attrs, 0, config);
|
|
90
87
|
}
|
|
@@ -97,56 +94,33 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
97
94
|
private void init(ReactApplicationContext context, ReadableMap config) {
|
|
98
95
|
this.mContext = context;
|
|
99
96
|
|
|
100
|
-
// listen to onConfigurationChanged doesn't work for this, it runs too soon
|
|
101
97
|
context.getCurrentActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
|
102
98
|
LayoutInflater.from(context).inflate(R.layout.video_trimmer_view, this, true);
|
|
99
|
+
vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
|
|
103
100
|
|
|
104
|
-
|
|
105
|
-
mVideoView = findViewById(R.id.video_loader);
|
|
106
|
-
mPlayView = findViewById(R.id.icon_video_play);
|
|
107
|
-
mSeekBarLayout = findViewById(R.id.seekBarLayout);
|
|
108
|
-
mRedProgressIcon = findViewById(R.id.positionIcon);
|
|
109
|
-
mVideoThumbRecyclerView = findViewById(R.id.video_frames_recyclerView);
|
|
110
|
-
mVideoThumbRecyclerView.setLayoutManager(new LinearLayoutManager(mContext, LinearLayoutManager.HORIZONTAL, false));
|
|
111
|
-
mVideoThumbAdapter = new VideoTrimmerAdapter(mContext);
|
|
112
|
-
mVideoThumbRecyclerView.setAdapter(mVideoThumbAdapter);
|
|
113
|
-
mVideoThumbRecyclerView.addOnScrollListener(mOnScrollListener);
|
|
114
|
-
|
|
101
|
+
initializeViews();
|
|
115
102
|
configure(config);
|
|
116
103
|
setUpListeners();
|
|
104
|
+
setProgressIndicatorTouchListener();
|
|
117
105
|
}
|
|
118
106
|
|
|
119
|
-
private void
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
mRangeSeekBarView = new RangeSeekBarView(mContext, mLeftProgressPos, mRightProgressPos);
|
|
137
|
-
mRangeSeekBarView.setSelectedMinValue(mLeftProgressPos);
|
|
138
|
-
mRangeSeekBarView.setSelectedMaxValue(mRightProgressPos);
|
|
139
|
-
mRangeSeekBarView.setStartEndTime(mLeftProgressPos, mRightProgressPos);
|
|
140
|
-
mRangeSeekBarView.setMinShootTime(mMinDuration);
|
|
141
|
-
mRangeSeekBarView.setNotifyWhileDragging(true);
|
|
142
|
-
mRangeSeekBarView.setOnRangeSeekBarChangeListener(mOnRangeSeekBarChangeListener);
|
|
143
|
-
mSeekBarLayout.addView(mRangeSeekBarView);
|
|
144
|
-
if(mThumbsTotalCount - VideoTrimmerUtil.MAX_COUNT_RANGE > 0) {
|
|
145
|
-
mAverageMsPx = (mDuration - mMaxDuration) / (float) (mThumbsTotalCount - VideoTrimmerUtil.MAX_COUNT_RANGE);
|
|
146
|
-
} else {
|
|
147
|
-
mAverageMsPx = 0f;
|
|
148
|
-
}
|
|
149
|
-
averagePxMs = (mMaxWidth * 1.0f / (mRightProgressPos - mLeftProgressPos));
|
|
107
|
+
private void initializeViews() {
|
|
108
|
+
mThumbnailContainer = findViewById(R.id.thumbnailContainer);
|
|
109
|
+
mVideoView = findViewById(R.id.video_loader);
|
|
110
|
+
mPlayView = findViewById(R.id.icon_video_play);
|
|
111
|
+
startTimeText = findViewById(R.id.startTime);
|
|
112
|
+
currentTimeText = findViewById(R.id.currentTime);
|
|
113
|
+
endTimeText = findViewById(R.id.endTime);
|
|
114
|
+
progressIndicator = findViewById(R.id.progressIndicator);
|
|
115
|
+
trimmerContainer = findViewById(R.id.trimmerContainer);
|
|
116
|
+
trimmerContainerBg = findViewById(R.id.trimmerContainerBg);
|
|
117
|
+
leadingHandle = findViewById(R.id.leadingHandle);
|
|
118
|
+
trailingHandle = findViewById(R.id.trailingHandle);
|
|
119
|
+
leadingOverlay = findViewById(R.id.leadingOverlay);
|
|
120
|
+
trailingOverlay = findViewById(R.id.trailingOverlay);
|
|
121
|
+
trimmerContainerWrapper = findViewById(R.id.trimmerContainerWrapper);
|
|
122
|
+
trimmerContainerWrapper.setVisibility(View.INVISIBLE);
|
|
123
|
+
trimmerContainerWrapper.setAlpha(0f);
|
|
150
124
|
}
|
|
151
125
|
|
|
152
126
|
public void initVideoByURI(final Uri videoURI) {
|
|
@@ -156,32 +130,24 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
156
130
|
}
|
|
157
131
|
|
|
158
132
|
private void startShootVideoThumbs(final Context context, final Uri videoUri, int totalThumbsCount, long startPosition, long endPosition) {
|
|
133
|
+
mThumbnailContainer.removeAllViews();
|
|
159
134
|
VideoTrimmerUtil.shootVideoThumbInBackground(context, videoUri, totalThumbsCount, startPosition, endPosition,
|
|
160
135
|
(bitmap, interval) -> {
|
|
161
136
|
if (bitmap != null) {
|
|
162
|
-
|
|
137
|
+
runOnUiThread(() -> {
|
|
138
|
+
ImageView thumbImageView = new ImageView(context);
|
|
139
|
+
thumbImageView.setImageBitmap(bitmap);
|
|
140
|
+
thumbImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
|
141
|
+
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(100, LayoutParams.MATCH_PARENT);
|
|
142
|
+
layoutParams.width = VideoTrimmerUtil.VIDEO_FRAMES_WIDTH / VideoTrimmerUtil.MAX_COUNT_RANGE;
|
|
143
|
+
thumbImageView.setLayoutParams(layoutParams);
|
|
144
|
+
mThumbnailContainer.addView(thumbImageView);
|
|
145
|
+
});
|
|
163
146
|
}
|
|
164
147
|
});
|
|
165
148
|
}
|
|
166
149
|
|
|
167
150
|
private void videoPrepared(MediaPlayer mp) {
|
|
168
|
-
ViewGroup.LayoutParams lp = mVideoView.getLayoutParams();
|
|
169
|
-
int videoWidth = mp.getVideoWidth();
|
|
170
|
-
int videoHeight = mp.getVideoHeight();
|
|
171
|
-
|
|
172
|
-
int screenWidth = mLinearVideo.getWidth();
|
|
173
|
-
int screenHeight = mLinearVideo.getHeight();
|
|
174
|
-
|
|
175
|
-
if (videoHeight > videoWidth) {
|
|
176
|
-
lp.height = screenHeight;
|
|
177
|
-
float r = videoWidth / (float) videoHeight;
|
|
178
|
-
lp.width = (int) (lp.height * r);
|
|
179
|
-
} else {
|
|
180
|
-
lp.width = screenWidth;
|
|
181
|
-
float r = videoHeight / (float) videoWidth;
|
|
182
|
-
lp.height = (int) (lp.width * r);
|
|
183
|
-
}
|
|
184
|
-
mVideoView.setLayoutParams(lp);
|
|
185
151
|
mDuration = mVideoView.getDuration();
|
|
186
152
|
|
|
187
153
|
mMaxDuration = Math.min(mMaxDuration, mDuration);
|
|
@@ -190,46 +156,74 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
190
156
|
mediaMetadataRetriever.setDataSource(mContext, mSourceUri);
|
|
191
157
|
// take first frame
|
|
192
158
|
Bitmap bitmap = mediaMetadataRetriever.getFrameAtTime(0, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
|
|
193
|
-
VideoTrimmerUtil.mThumbWidth = VideoTrimmerUtil.THUMB_HEIGHT * bitmap.getWidth() / bitmap.getHeight();
|
|
194
159
|
|
|
195
|
-
if (
|
|
196
|
-
|
|
197
|
-
} else {
|
|
198
|
-
setRestoreState(false);
|
|
199
|
-
seekTo((int) mRedProgressBarPos);
|
|
160
|
+
if (bitmap != null) {
|
|
161
|
+
VideoTrimmerUtil.mThumbWidth = VideoTrimmerUtil.THUMB_HEIGHT * bitmap.getWidth() / bitmap.getHeight();
|
|
200
162
|
}
|
|
201
|
-
|
|
163
|
+
|
|
164
|
+
VideoTrimmerUtil.SCREEN_WIDTH_FULL = this.getScreenWidthInPortraitMode();
|
|
165
|
+
VideoTrimmerUtil.VIDEO_FRAMES_WIDTH = VideoTrimmerUtil.SCREEN_WIDTH_FULL - RECYCLER_VIEW_PADDING * 2;
|
|
166
|
+
VideoTrimmerUtil.MAX_COUNT_RANGE = Math.max((VIDEO_FRAMES_WIDTH / VideoTrimmerUtil.mThumbWidth), VideoTrimmerUtil.MAX_COUNT_RANGE);
|
|
167
|
+
|
|
168
|
+
int mThumbsTotalCount;
|
|
169
|
+
mThumbsTotalCount = (int) (mDuration * 1.0f / (mMaxDuration * 1.0f) * VideoTrimmerUtil.MAX_COUNT_RANGE);
|
|
202
170
|
startShootVideoThumbs(mContext, mSourceUri, mThumbsTotalCount, 0, mDuration);
|
|
171
|
+
|
|
172
|
+
// Set initial handle positions if mMaxDuration < video duration
|
|
173
|
+
if (mMaxDuration < mDuration) {
|
|
174
|
+
endTime = mMaxDuration;
|
|
175
|
+
} else {
|
|
176
|
+
endTime = mDuration;
|
|
177
|
+
}
|
|
178
|
+
updateHandlePositions();
|
|
203
179
|
}
|
|
204
180
|
|
|
205
|
-
private void
|
|
206
|
-
|
|
207
|
-
|
|
181
|
+
private void updateHandlePositions() {
|
|
182
|
+
float startPercent = (float) startTime / mVideoView.getDuration();
|
|
183
|
+
float endPercent = (float) endTime / mVideoView.getDuration();
|
|
184
|
+
|
|
185
|
+
float containerWidth = trimmerContainerBg.getWidth();
|
|
186
|
+
float leadingHandleX = startPercent * containerWidth;
|
|
187
|
+
float trailingHandleX = endPercent * containerWidth;
|
|
188
|
+
|
|
189
|
+
leadingHandle.setX(leadingHandleX);
|
|
190
|
+
trailingHandle.setX(trailingHandleX + trailingHandle.getWidth());
|
|
191
|
+
|
|
192
|
+
updateTrimmerContainerWidth();
|
|
193
|
+
updateCurrentTime(false);
|
|
194
|
+
|
|
195
|
+
trimmerContainerWrapper.setVisibility(View.VISIBLE);
|
|
196
|
+
trimmerContainerWrapper.animate().alpha(1f).setDuration(250).start();
|
|
197
|
+
|
|
198
|
+
// because on load video will not start and just display black screen
|
|
199
|
+
// here we'll seek to first frame to make it more friendly
|
|
200
|
+
mVideoView.seekTo(1);
|
|
208
201
|
}
|
|
209
202
|
|
|
210
|
-
private void
|
|
211
|
-
mVideoView.pause();
|
|
203
|
+
private void videoCompleted() {
|
|
212
204
|
setPlayPauseViewIcon(false);
|
|
205
|
+
mTimingHandler.removeCallbacks(mTimingRunnable);
|
|
213
206
|
}
|
|
214
207
|
|
|
215
|
-
private void
|
|
216
|
-
mRedProgressBarPos = mVideoView.getCurrentPosition();
|
|
208
|
+
private void playOrPause() {
|
|
217
209
|
if (mVideoView.isPlaying()) {
|
|
218
|
-
|
|
219
|
-
pauseRedProgressAnimation();
|
|
210
|
+
onVideoPause();
|
|
220
211
|
} else {
|
|
212
|
+
// if current video time >= end time, seek to start time
|
|
213
|
+
if (mVideoView.getCurrentPosition() >= endTime) {
|
|
214
|
+
seekTo(startTime, true);
|
|
215
|
+
}
|
|
221
216
|
mVideoView.start();
|
|
222
|
-
|
|
217
|
+
startTimingRunnable();
|
|
223
218
|
}
|
|
224
219
|
setPlayPauseViewIcon(mVideoView.isPlaying());
|
|
225
220
|
}
|
|
226
221
|
|
|
227
222
|
public void onVideoPause() {
|
|
228
223
|
if (mVideoView.isPlaying()) {
|
|
229
|
-
|
|
224
|
+
mTimingHandler.removeCallbacks(mTimingRunnable);
|
|
230
225
|
mVideoView.pause();
|
|
231
226
|
setPlayPauseViewIcon(false);
|
|
232
|
-
mRedProgressIcon.setVisibility(GONE);
|
|
233
227
|
}
|
|
234
228
|
}
|
|
235
229
|
|
|
@@ -238,237 +232,354 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
238
232
|
}
|
|
239
233
|
|
|
240
234
|
private void setUpListeners() {
|
|
241
|
-
findViewById(R.id.cancelBtn).setOnClickListener(view ->
|
|
242
|
-
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
findViewById(R.id.saveBtn).setOnClickListener(view -> {
|
|
246
|
-
mOnTrimVideoListener.onSave();
|
|
247
|
-
});
|
|
235
|
+
findViewById(R.id.cancelBtn).setOnClickListener(view -> mOnTrimVideoListener.onCancel());
|
|
236
|
+
findViewById(R.id.saveBtn).setOnClickListener(view -> mOnTrimVideoListener.onSave());
|
|
248
237
|
|
|
249
238
|
mVideoView.setOnPreparedListener(mp -> {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
239
|
+
if (!mIsPrepared) {
|
|
240
|
+
mp.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT);
|
|
241
|
+
videoPrepared(mp);
|
|
242
|
+
mIsPrepared = true;
|
|
254
243
|
}
|
|
255
|
-
mp.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT);
|
|
256
|
-
videoPrepared(mp);
|
|
257
|
-
mIsPrepared = true;
|
|
258
|
-
});
|
|
259
|
-
mVideoView.setOnCompletionListener(mp -> {
|
|
260
|
-
videoCompleted();
|
|
261
|
-
});
|
|
262
|
-
mPlayView.setOnClickListener(view -> {
|
|
263
|
-
playVideoOrPause();
|
|
264
244
|
});
|
|
245
|
+
|
|
246
|
+
mVideoView.setOnCompletionListener(mp -> videoCompleted());
|
|
247
|
+
mPlayView.setOnClickListener(view -> playOrPause());
|
|
248
|
+
setHandleTouchListener(leadingHandle, true);
|
|
249
|
+
setHandleTouchListener(trailingHandle, false);
|
|
265
250
|
}
|
|
266
251
|
|
|
267
252
|
public void onSaveClicked() {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
mLeftProgressPos,
|
|
277
|
-
mRightProgressPos,
|
|
278
|
-
mOnTrimVideoListener);
|
|
279
|
-
}
|
|
253
|
+
onVideoPause();
|
|
254
|
+
VideoTrimmerUtil.trim(
|
|
255
|
+
mSourceUri.getPath(),
|
|
256
|
+
StorageUtil.getOutputPath(mContext),
|
|
257
|
+
mDuration,
|
|
258
|
+
startTime,
|
|
259
|
+
endTime,
|
|
260
|
+
mOnTrimVideoListener);
|
|
280
261
|
}
|
|
281
262
|
|
|
282
|
-
private void seekTo(long msec) {
|
|
263
|
+
private void seekTo(long msec, boolean needUpdateProgress) {
|
|
283
264
|
mVideoView.seekTo((int) msec);
|
|
265
|
+
updateCurrentTime(needUpdateProgress);
|
|
284
266
|
}
|
|
285
267
|
|
|
286
|
-
private boolean
|
|
287
|
-
|
|
268
|
+
private void setPlayPauseViewIcon(boolean isPlaying) {
|
|
269
|
+
// note: icons imported from SF symbols have 0.85 opacity we should change to 1 here
|
|
270
|
+
mPlayView.setImageResource(isPlaying ? R.drawable.pause_fill : R.drawable.play_fill);
|
|
288
271
|
}
|
|
289
272
|
|
|
290
|
-
|
|
291
|
-
|
|
273
|
+
@Override
|
|
274
|
+
protected void onDetachedFromWindow() {
|
|
275
|
+
super.onDetachedFromWindow();
|
|
276
|
+
mContext.getCurrentActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
|
|
292
277
|
}
|
|
293
278
|
|
|
294
|
-
|
|
295
|
-
|
|
279
|
+
@Override
|
|
280
|
+
public void onDestroy() {
|
|
281
|
+
BackgroundExecutor.cancelAll("", true);
|
|
282
|
+
UiThreadExecutor.cancelAll("");
|
|
283
|
+
mContext.getCurrentActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
|
|
284
|
+
mTimingHandler.removeCallbacks(mTimingRunnable);
|
|
285
|
+
zoomWaitTimer.removeCallbacks(zoomRunnable);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
private int getScreenWidthInPortraitMode() {
|
|
289
|
+
int screenWidth = DeviceUtil.getDeviceWidth();
|
|
290
|
+
int screenHeight = DeviceUtil.getDeviceHeight();
|
|
291
|
+
int currentOrientation = getResources().getConfiguration().orientation;
|
|
292
|
+
if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
|
|
293
|
+
return screenHeight;
|
|
294
|
+
}
|
|
295
|
+
return screenWidth;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
private void configure(ReadableMap config) {
|
|
299
|
+
if (config.hasKey("maxDuration")) {
|
|
300
|
+
mMaxDuration = Math.max(0, config.getInt("maxDuration") * 1000L);
|
|
301
|
+
}
|
|
302
|
+
if (config.hasKey("minDuration")) {
|
|
303
|
+
mMinDuration = Math.max(1000L, config.getInt("minDuration") * 1000L);
|
|
304
|
+
}
|
|
305
|
+
if (config.hasKey("cancelButtonText")) {
|
|
306
|
+
TextView tv = findViewById(R.id.cancelBtn);
|
|
307
|
+
tv.setText(config.getString("cancelButtonText"));
|
|
308
|
+
}
|
|
309
|
+
if (config.hasKey("saveButtonText")) {
|
|
310
|
+
TextView tv = findViewById(R.id.saveBtn);
|
|
311
|
+
tv.setText(config.getString("saveButtonText"));
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
private void startTimingRunnable() {
|
|
316
|
+
mTimingRunnable = new Runnable() {
|
|
317
|
+
@Override
|
|
318
|
+
public void run() {
|
|
319
|
+
int currentPosition = mVideoView.getCurrentPosition();
|
|
320
|
+
|
|
321
|
+
if (currentPosition >= endTime) {
|
|
322
|
+
onVideoPause();
|
|
323
|
+
seekTo(endTime, true); // Ensure exact end time display
|
|
324
|
+
} else {
|
|
325
|
+
updateCurrentTime(true);
|
|
326
|
+
mTimingHandler.postDelayed(this, TIMING_UPDATE_INTERVAL);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
mTimingHandler.postDelayed(mTimingRunnable, TIMING_UPDATE_INTERVAL);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
private void updateCurrentTime(boolean needUpdateProgress) {
|
|
334
|
+
// TODO: check the case after drag the progress indicator and hit play, it'll play a little bit earlier than the progress indicator
|
|
335
|
+
int currentPosition = mVideoView.getCurrentPosition();
|
|
336
|
+
int duration = mVideoView.getDuration();
|
|
337
|
+
|
|
338
|
+
if (currentPosition >= duration - 100) {
|
|
339
|
+
currentPosition = duration;
|
|
340
|
+
} else if (currentPosition >= endTime - 100) {
|
|
341
|
+
currentPosition = (int) endTime;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
String currentTime = formatTime(currentPosition);
|
|
345
|
+
currentTimeText.setText(currentTime);
|
|
346
|
+
|
|
347
|
+
String startTime = formatTime((int) this.startTime);
|
|
348
|
+
startTimeText.setText(startTime);
|
|
349
|
+
|
|
350
|
+
String endTime = formatTime((int) this.endTime);
|
|
351
|
+
endTimeText.setText(endTime);
|
|
352
|
+
|
|
353
|
+
if (needUpdateProgress) {
|
|
354
|
+
// Update progressIndicator position
|
|
355
|
+
float indicatorPosition = (float) currentPosition / duration * (trimmerContainerBg.getWidth() - progressIndicator.getWidth()) + leadingHandle.getWidth();
|
|
356
|
+
|
|
357
|
+
float rightBoundary = trimmerContainer.getX() + trimmerContainer.getWidth() - progressIndicator.getWidth();
|
|
358
|
+
|
|
359
|
+
progressIndicator.setX(Math.min(rightBoundary, indicatorPosition));
|
|
360
|
+
}
|
|
296
361
|
}
|
|
297
362
|
|
|
298
|
-
private
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
363
|
+
private String formatTime(int milliseconds) {
|
|
364
|
+
int totalSeconds = milliseconds / 1000;
|
|
365
|
+
int minutes = totalSeconds / 60;
|
|
366
|
+
int seconds = totalSeconds % 60;
|
|
367
|
+
int millis = milliseconds % 1000;
|
|
368
|
+
return String.format(Locale.getDefault(), "%d:%02d.%03d", minutes, seconds, millis);
|
|
369
|
+
}
|
|
303
370
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
switch (action) {
|
|
371
|
+
private void setProgressIndicatorTouchListener() {
|
|
372
|
+
trimmerContainerBg.setOnTouchListener((view, event) -> {
|
|
373
|
+
switch (event.getAction()) {
|
|
308
374
|
case MotionEvent.ACTION_DOWN:
|
|
375
|
+
didClampWhilePanning = false;
|
|
376
|
+
onVideoPause();
|
|
377
|
+
onTrimmerContainerPanned(event);
|
|
378
|
+
playHapticFeedback(true);
|
|
309
379
|
break;
|
|
310
380
|
case MotionEvent.ACTION_MOVE:
|
|
311
|
-
|
|
381
|
+
onTrimmerContainerPanned(event);
|
|
312
382
|
break;
|
|
313
383
|
case MotionEvent.ACTION_UP:
|
|
314
|
-
|
|
384
|
+
view.performClick();
|
|
315
385
|
break;
|
|
316
386
|
default:
|
|
317
|
-
|
|
387
|
+
return false;
|
|
318
388
|
}
|
|
389
|
+
return true;
|
|
390
|
+
});
|
|
391
|
+
}
|
|
319
392
|
|
|
320
|
-
|
|
321
|
-
|
|
393
|
+
private void onTrimmerContainerPanned(MotionEvent event) {
|
|
394
|
+
float newX = event.getRawX();
|
|
395
|
+
boolean didClamp = false;
|
|
396
|
+
// Ensure newX is within valid range
|
|
397
|
+
float leftBoundary = trimmerContainer.getX();
|
|
398
|
+
float rightBoundary = trimmerContainer.getX() + trimmerContainer.getWidth() - progressIndicator.getWidth();
|
|
399
|
+
newX = Math.max(leftBoundary, newX);
|
|
400
|
+
newX = Math.min(rightBoundary, newX);
|
|
401
|
+
|
|
402
|
+
// check play haptic feedback
|
|
403
|
+
if (newX <= leftBoundary) {
|
|
404
|
+
didClamp = true;
|
|
322
405
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
private final RecyclerView.OnScrollListener mOnScrollListener = new RecyclerView.OnScrollListener() {
|
|
326
|
-
@Override
|
|
327
|
-
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
|
|
328
|
-
super.onScrollStateChanged(recyclerView, newState);
|
|
406
|
+
else if (newX >= rightBoundary) {
|
|
407
|
+
didClamp = true;
|
|
329
408
|
}
|
|
330
409
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
if (Math.abs(lastScrollX - scrollX) < mScaledTouchSlop) {
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
//初始状态,why ? 因为默认的时候有35dp的空白!
|
|
340
|
-
if (scrollX == -RECYCLER_VIEW_PADDING) {
|
|
341
|
-
scrollPos = 0;
|
|
342
|
-
mLeftProgressPos = mRangeSeekBarView.getSelectedMinValue() + scrollPos;
|
|
343
|
-
|
|
344
|
-
// when scrolling the highlighted section mRightProgressPos in some cases can bigger than mDuration
|
|
345
|
-
// Eg: mDuration=62006, then mRightProgressPos can be 63000
|
|
346
|
-
mRightProgressPos = Math.min(mRangeSeekBarView.getSelectedMaxValue() + scrollPos, mDuration);
|
|
347
|
-
mRedProgressBarPos = mLeftProgressPos;
|
|
348
|
-
} else {
|
|
349
|
-
scrollPos = (long) (mAverageMsPx * (RECYCLER_VIEW_PADDING + scrollX) / VideoTrimmerUtil.mThumbWidth);
|
|
350
|
-
mLeftProgressPos = mRangeSeekBarView.getSelectedMinValue() + scrollPos;
|
|
351
|
-
|
|
352
|
-
// when scrolling the highlighted section mRightProgressPos in some cases can bigger than mDuration
|
|
353
|
-
// Eg: mDuration=62006, then mRightProgressPos can be 63000
|
|
354
|
-
mRightProgressPos = Math.min(mRangeSeekBarView.getSelectedMaxValue() + scrollPos, mDuration);
|
|
355
|
-
mRedProgressBarPos = mLeftProgressPos;
|
|
356
|
-
if (mVideoView.isPlaying()) {
|
|
357
|
-
mVideoView.pause();
|
|
358
|
-
setPlayPauseViewIcon(false);
|
|
359
|
-
}
|
|
360
|
-
mRedProgressIcon.setVisibility(GONE);
|
|
361
|
-
seekTo(mLeftProgressPos);
|
|
410
|
+
if (didClamp && !didClampWhilePanning) {
|
|
411
|
+
playHapticFeedback(false);
|
|
412
|
+
}
|
|
413
|
+
didClampWhilePanning = didClamp;
|
|
362
414
|
|
|
363
|
-
|
|
364
|
-
mRangeSeekBarView.invalidate();
|
|
365
|
-
}
|
|
415
|
+
progressIndicator.setX(newX);
|
|
366
416
|
|
|
367
|
-
|
|
368
|
-
}
|
|
369
|
-
};
|
|
417
|
+
float indicatorPosition = newX - (trimmerContainerBg.getX());
|
|
370
418
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
private int calcScrollXDistance() {
|
|
375
|
-
LinearLayoutManager layoutManager = (LinearLayoutManager) mVideoThumbRecyclerView.getLayoutManager();
|
|
376
|
-
int position = layoutManager.findFirstVisibleItemPosition();
|
|
377
|
-
View firstVisibleChildView = layoutManager.findViewByPosition(position);
|
|
378
|
-
int itemWidth = firstVisibleChildView.getWidth();
|
|
379
|
-
return (position) * itemWidth - firstVisibleChildView.getLeft();
|
|
380
|
-
}
|
|
419
|
+
// TODO: check this
|
|
420
|
+
float indicatorPositionPercent = indicatorPosition / (trimmerContainerBg.getWidth() - progressIndicator.getWidth());
|
|
421
|
+
long newVideoPosition = (long) (indicatorPositionPercent * mVideoView.getDuration());
|
|
381
422
|
|
|
382
|
-
|
|
383
|
-
pauseRedProgressAnimation();
|
|
384
|
-
playingAnimation();
|
|
385
|
-
mAnimationHandler.post(mAnimationRunnable);
|
|
423
|
+
seekTo(newVideoPosition, false);
|
|
386
424
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
425
|
+
private void setHandleTouchListener(View handle, boolean isLeading) {
|
|
426
|
+
handle.setOnTouchListener((view, event) -> {
|
|
427
|
+
switch (event.getAction()) {
|
|
428
|
+
case MotionEvent.ACTION_DOWN:
|
|
429
|
+
didClampWhilePanning = false;
|
|
430
|
+
onVideoPause();
|
|
431
|
+
fadeOutProgressIndicator();
|
|
432
|
+
seekTo(isLeading ? startTime : endTime, true);
|
|
433
|
+
playHapticFeedback(true);
|
|
434
|
+
break;
|
|
435
|
+
case MotionEvent.ACTION_MOVE:
|
|
436
|
+
boolean didClamp = false;
|
|
437
|
+
float newX = event.getRawX() - ((float) view.getWidth() / 2);
|
|
438
|
+
if (isLeading) {
|
|
439
|
+
newX = Math.max(0, Math.min(newX, trailingHandle.getX() - view.getWidth()));
|
|
440
|
+
} else {
|
|
441
|
+
newX = Math.min(trimmerContainerBg.getWidth() + view.getWidth(), Math.max(newX, leadingHandle.getX() + view.getWidth()));
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
view.setX(newX);
|
|
445
|
+
|
|
446
|
+
// Calculate new startTime or endTime
|
|
447
|
+
if (isLeading) {
|
|
448
|
+
// Calculate the new startTime based on the handle's new position
|
|
449
|
+
long newStartTime = (long) ((newX / trimmerContainerBg.getWidth()) * mVideoView.getDuration());
|
|
450
|
+
// Calculate the duration between the new startTime and the current endTime
|
|
451
|
+
long duration = endTime - newStartTime;
|
|
452
|
+
if (duration >= mMinDuration && duration <= mMaxDuration) {
|
|
453
|
+
// If the duration is within the allowed range, update startTime and move the progress indicator
|
|
454
|
+
startTime = newStartTime;
|
|
455
|
+
progressIndicator.setX(newX + view.getWidth());
|
|
456
|
+
} else if (duration < mMinDuration) {
|
|
457
|
+
didClamp = true;
|
|
458
|
+
// If the duration is less than the minimum, set startTime to the maximum possible to maintain the minimum duration
|
|
459
|
+
startTime = endTime - mMinDuration;
|
|
460
|
+
// Adjust the handle position accordingly
|
|
461
|
+
view.setX((float) startTime / mVideoView.getDuration() * trimmerContainerBg.getWidth());
|
|
462
|
+
progressIndicator.setX(view.getX() + view.getWidth());
|
|
463
|
+
} else {
|
|
464
|
+
didClamp = true;
|
|
465
|
+
// If the duration is greater than the maximum, set startTime to the minimum possible to maintain the maximum duration
|
|
466
|
+
startTime = endTime - mMaxDuration;
|
|
467
|
+
// Adjust the handle position accordingly
|
|
468
|
+
view.setX((float) startTime / mVideoView.getDuration() * trimmerContainerBg.getWidth());
|
|
469
|
+
progressIndicator.setX(view.getX() + view.getWidth());
|
|
470
|
+
}
|
|
471
|
+
} else {
|
|
472
|
+
// Calculate the new endTime based on the handle's new position
|
|
473
|
+
long newEndTime = (long) (((newX - view.getWidth()) / trimmerContainerBg.getWidth()) * mVideoView.getDuration());
|
|
474
|
+
// Calculate the duration between the new endTime and the current startTime
|
|
475
|
+
long duration = newEndTime - startTime;
|
|
476
|
+
if (duration >= mMinDuration && duration <= mMaxDuration) {
|
|
477
|
+
// If the duration is within the allowed range, update endTime and move the progress indicator
|
|
478
|
+
endTime = newEndTime;
|
|
479
|
+
progressIndicator.setX(newX - progressIndicator.getWidth());
|
|
480
|
+
} else if (duration < mMinDuration) {
|
|
481
|
+
didClamp = true;
|
|
482
|
+
// If the duration is less than the minimum, set endTime to the minimum possible to maintain the minimum duration
|
|
483
|
+
endTime = startTime + mMinDuration;
|
|
484
|
+
// Adjust the handle position accordingly
|
|
485
|
+
view.setX((float) endTime / mVideoView.getDuration() * trimmerContainerBg.getWidth() + view.getWidth());
|
|
486
|
+
progressIndicator.setX(view.getX() - progressIndicator.getWidth());
|
|
487
|
+
} else {
|
|
488
|
+
didClamp = true;
|
|
489
|
+
// If the duration is greater than the maximum, set endTime to the maximum possible to maintain the maximum duration
|
|
490
|
+
endTime = startTime + mMaxDuration;
|
|
491
|
+
// Adjust the handle position accordingly
|
|
492
|
+
view.setX((float) endTime / mVideoView.getDuration() * trimmerContainerBg.getWidth() + view.getWidth());
|
|
493
|
+
progressIndicator.setX(view.getX() - progressIndicator.getWidth());
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
if (didClamp && !didClampWhilePanning) {
|
|
498
|
+
playHapticFeedback(false);
|
|
499
|
+
}
|
|
500
|
+
didClampWhilePanning = didClamp;
|
|
501
|
+
|
|
502
|
+
updateTrimmerContainerWidth();
|
|
503
|
+
seekTo(isLeading ? startTime : endTime, false);
|
|
504
|
+
|
|
505
|
+
// TODO: create zoom feature like iOS
|
|
506
|
+
// startZoomWaitTimer();
|
|
507
|
+
break;
|
|
508
|
+
case MotionEvent.ACTION_UP:
|
|
509
|
+
// stopZoomIfNeeded();
|
|
510
|
+
fadeInProgressIndicator();
|
|
511
|
+
view.performClick();
|
|
512
|
+
break;
|
|
513
|
+
default:
|
|
514
|
+
return false;
|
|
515
|
+
}
|
|
516
|
+
return true;
|
|
400
517
|
});
|
|
401
|
-
mRedProgressAnimator.start();
|
|
402
518
|
}
|
|
403
519
|
|
|
404
|
-
private void
|
|
405
|
-
|
|
406
|
-
if (mRedProgressAnimator != null && mRedProgressAnimator.isRunning()) {
|
|
407
|
-
mAnimationHandler.removeCallbacks(mAnimationRunnable);
|
|
408
|
-
mRedProgressAnimator.cancel();
|
|
409
|
-
}
|
|
520
|
+
private void fadeOutProgressIndicator() {
|
|
521
|
+
progressIndicator.animate().alpha(0f).setDuration(250).withEndAction(() -> progressIndicator.setVisibility(View.INVISIBLE)).start();
|
|
410
522
|
}
|
|
411
523
|
|
|
412
|
-
private
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
long currentPosition = mVideoView.getCurrentPosition();
|
|
416
|
-
if (currentPosition >= (mRightProgressPos)) {
|
|
417
|
-
mRedProgressBarPos = mLeftProgressPos;
|
|
418
|
-
pauseRedProgressAnimation();
|
|
419
|
-
onVideoPause();
|
|
420
|
-
} else {
|
|
421
|
-
mAnimationHandler.post(mAnimationRunnable);
|
|
422
|
-
}
|
|
524
|
+
private void fadeInProgressIndicator() {
|
|
525
|
+
progressIndicator.setVisibility(View.VISIBLE);
|
|
526
|
+
progressIndicator.animate().alpha(1f).setDuration(250).start();
|
|
423
527
|
}
|
|
424
528
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
529
|
+
private void updateTrimmerContainerWidth() {
|
|
530
|
+
int left = (int) leadingHandle.getX() + leadingHandle.getWidth();
|
|
531
|
+
int right = trimmerContainerBg.getWidth() - (int) trailingHandle.getX() + 2 * trailingHandle.getWidth();
|
|
532
|
+
|
|
533
|
+
RelativeLayout.LayoutParams leadingOverlayParams = (RelativeLayout.LayoutParams) leadingOverlay.getLayoutParams();
|
|
534
|
+
leadingOverlayParams.width = left;
|
|
535
|
+
leadingOverlayParams.height = RelativeLayout.LayoutParams.MATCH_PARENT;
|
|
536
|
+
leadingOverlayParams.addRule(RelativeLayout.ALIGN_PARENT_START);
|
|
537
|
+
leadingOverlay.setLayoutParams(leadingOverlayParams);
|
|
538
|
+
|
|
539
|
+
RelativeLayout.LayoutParams trailingOverlayParams = (RelativeLayout.LayoutParams) trailingOverlay.getLayoutParams();
|
|
540
|
+
trailingOverlayParams.width = right;
|
|
541
|
+
trailingOverlayParams.height = RelativeLayout.LayoutParams.MATCH_PARENT;
|
|
542
|
+
trailingOverlayParams.addRule(RelativeLayout.ALIGN_PARENT_END);
|
|
543
|
+
trailingOverlay.setLayoutParams(trailingOverlayParams);
|
|
429
544
|
}
|
|
430
545
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
BackgroundExecutor.cancelAll("", true);
|
|
436
|
-
UiThreadExecutor.cancelAll("");
|
|
437
|
-
mContext.getCurrentActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
|
|
546
|
+
private void playHapticFeedback(boolean isLight) {
|
|
547
|
+
if (vibrator != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
548
|
+
vibrator.vibrate(VibrationEffect.createOneShot(isLight ? 10 : 25, VibrationEffect.DEFAULT_AMPLITUDE)); // Light vibration
|
|
549
|
+
}
|
|
438
550
|
}
|
|
439
551
|
|
|
440
|
-
private
|
|
441
|
-
|
|
442
|
-
|
|
552
|
+
private void startZoomWaitTimer() {
|
|
553
|
+
stopZoomWaitTimer();
|
|
554
|
+
if (isZoomedIn) {
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
443
557
|
|
|
444
|
-
|
|
445
|
-
|
|
558
|
+
zoomRunnable = () -> {
|
|
559
|
+
Log.i("tag","A Kiss after 500ms");
|
|
560
|
+
stopZoomWaitTimer();
|
|
561
|
+
zoomIfNeeded();
|
|
562
|
+
};
|
|
446
563
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
return screenHeight;
|
|
450
|
-
}
|
|
564
|
+
zoomWaitTimer.postDelayed(zoomRunnable, 500);
|
|
565
|
+
}
|
|
451
566
|
|
|
452
|
-
|
|
567
|
+
private void stopZoomWaitTimer() {
|
|
568
|
+
zoomWaitTimer.removeCallbacks(zoomRunnable);
|
|
453
569
|
}
|
|
454
570
|
|
|
455
|
-
private void
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
571
|
+
private void stopZoomIfNeeded() {
|
|
572
|
+
stopZoomWaitTimer();
|
|
573
|
+
isZoomedIn = false;
|
|
574
|
+
}
|
|
459
575
|
|
|
460
|
-
|
|
461
|
-
|
|
576
|
+
private void zoomIfNeeded() {
|
|
577
|
+
if (isZoomedIn) {
|
|
578
|
+
return;
|
|
462
579
|
}
|
|
463
580
|
|
|
464
|
-
|
|
465
|
-
TextView tv = findViewById(R.id.cancelBtn);
|
|
466
|
-
tv.setText(config.getString("cancelButtonText"));
|
|
467
|
-
}
|
|
581
|
+
startShootVideoThumbs(mContext, mSourceUri, 10, 5000, 10000);
|
|
468
582
|
|
|
469
|
-
|
|
470
|
-
TextView tv = findViewById(R.id.saveBtn);
|
|
471
|
-
tv.setText(config.getString("saveButtonText"));
|
|
472
|
-
}
|
|
583
|
+
isZoomedIn = true;
|
|
473
584
|
}
|
|
474
585
|
}
|