react-native-video-trim 6.0.3 → 6.0.5
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 +59 -6
- package/android/src/main/java/com/videotrim/BaseVideoTrimModule.kt +7 -19
- package/android/src/main/java/com/videotrim/widgets/VideoTrimmerView.java +426 -45
- package/ios/VideoTrim.mm +7 -10
- package/ios/VideoTrim.swift +33 -6
- package/lib/module/index.js +6 -6
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/NativeVideoTrim.d.ts +6 -6
- package/lib/typescript/src/NativeVideoTrim.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/NativeVideoTrim.ts +6 -6
- package/src/index.tsx +6 -6
package/README.md
CHANGED
|
@@ -254,7 +254,7 @@ All configuration options are optional. Here are the most commonly used ones:
|
|
|
254
254
|
|--------|------|---------|-------------|
|
|
255
255
|
| `type` | `'video' \| 'audio'` | `'video'` | Media type to trim |
|
|
256
256
|
| `outputExt` | `string` | `'mp4'` | Output file extension |
|
|
257
|
-
| `maxDuration` | `number` |
|
|
257
|
+
| `maxDuration` | `number` | `video duration` | Maximum duration in milliseconds |
|
|
258
258
|
| `minDuration` | `number` | `1000` | Minimum duration in milliseconds |
|
|
259
259
|
| `autoplay` | `boolean` | `false` | Auto-play media on load |
|
|
260
260
|
| `jumpToPositionOnLoad` | `number` | - | Initial position in milliseconds |
|
|
@@ -267,7 +267,11 @@ All configuration options are optional. Here are the most commonly used ones:
|
|
|
267
267
|
| `openDocumentsOnFinish` | `boolean` | `false` | Open document picker when done |
|
|
268
268
|
| `openShareSheetOnFinish` | `boolean` | `false` | Open share sheet when done |
|
|
269
269
|
| `removeAfterSavedToPhoto` | `boolean` | `false` | Delete file after saving to photos |
|
|
270
|
+
| `removeAfterFailedToSavePhoto` | `boolean` | `false` | Delete file if saving to photos fails |
|
|
271
|
+
| `removeAfterSavedToDocuments` | `boolean` | `false` | Delete file after saving to documents |
|
|
272
|
+
| `removeAfterFailedToSaveDocuments` | `boolean` | `false` | Delete file if saving to documents fails |
|
|
270
273
|
| `removeAfterShared` | `boolean` | `false` | Delete file after sharing (iOS only) |
|
|
274
|
+
| `removeAfterFailedToShare` | `boolean` | `false` | Delete file if sharing fails (iOS only) |
|
|
271
275
|
|
|
272
276
|
### UI Customization
|
|
273
277
|
|
|
@@ -278,11 +282,62 @@ All configuration options are optional. Here are the most commonly used ones:
|
|
|
278
282
|
| `trimmingText` | `string` | `"Trimming video..."` | Progress dialog text |
|
|
279
283
|
| `headerText` | `string` | - | Header text |
|
|
280
284
|
| `headerTextSize` | `number` | `16` | Header text size |
|
|
281
|
-
| `headerTextColor` | `string` |
|
|
285
|
+
| `headerTextColor` | `string` | - | Header text color |
|
|
282
286
|
| `trimmerColor` | `string` | - | Trimmer bar color |
|
|
283
|
-
| `handleIconColor` | `string` | - | Trimmer
|
|
287
|
+
| `handleIconColor` | `string` | - | Trimmer left/right handles color |
|
|
284
288
|
| `fullScreenModalIOS` | `boolean` | `false` | Use fullscreen modal on iOS |
|
|
285
289
|
|
|
290
|
+
### Dialog Options
|
|
291
|
+
|
|
292
|
+
<details>
|
|
293
|
+
<summary><strong>Cancel Dialog</strong></summary>
|
|
294
|
+
|
|
295
|
+
| Option | Type | Default | Description |
|
|
296
|
+
|--------|------|---------|-------------|
|
|
297
|
+
| `enableCancelDialog` | `boolean` | `true` | Show confirmation dialog on cancel |
|
|
298
|
+
| `cancelDialogTitle` | `string` | `"Warning!"` | Cancel dialog title |
|
|
299
|
+
| `cancelDialogMessage` | `string` | `"Are you sure want to cancel?"` | Cancel dialog message |
|
|
300
|
+
| `cancelDialogCancelText` | `string` | `"Close"` | Cancel dialog cancel button text |
|
|
301
|
+
| `cancelDialogConfirmText` | `string` | `"Proceed"` | Cancel dialog confirm button text |
|
|
302
|
+
</details>
|
|
303
|
+
|
|
304
|
+
<details>
|
|
305
|
+
<summary><strong>Save Dialog</strong></summary>
|
|
306
|
+
|
|
307
|
+
| Option | Type | Default | Description |
|
|
308
|
+
|--------|------|---------|-------------|
|
|
309
|
+
| `enableSaveDialog` | `boolean` | `true` | Show confirmation dialog on save |
|
|
310
|
+
| `saveDialogTitle` | `string` | `"Confirmation!"` | Save dialog title |
|
|
311
|
+
| `saveDialogMessage` | `string` | `"Are you sure want to save?"` | Save dialog message |
|
|
312
|
+
| `saveDialogCancelText` | `string` | `"Close"` | Save dialog cancel button text |
|
|
313
|
+
| `saveDialogConfirmText` | `string` | `"Proceed"` | Save dialog confirm button text |
|
|
314
|
+
</details>
|
|
315
|
+
|
|
316
|
+
<details>
|
|
317
|
+
<summary><strong>Trimming Cancel Dialog</strong></summary>
|
|
318
|
+
|
|
319
|
+
| Option | Type | Default | Description |
|
|
320
|
+
|--------|------|---------|-------------|
|
|
321
|
+
| `enableCancelTrimming` | `boolean` | `true` | Enable cancel during trimming |
|
|
322
|
+
| `cancelTrimmingButtonText` | `string` | `"Cancel"` | Cancel trimming button text |
|
|
323
|
+
| `enableCancelTrimmingDialog` | `boolean` | `true` | Show cancel trimming confirmation |
|
|
324
|
+
| `cancelTrimmingDialogTitle` | `string` | `"Warning!"` | Cancel trimming dialog title |
|
|
325
|
+
| `cancelTrimmingDialogMessage` | `string` | `"Are you sure want to cancel trimming?"` | Cancel trimming dialog message |
|
|
326
|
+
| `cancelTrimmingDialogCancelText` | `string` | `"Close"` | Cancel trimming dialog cancel button |
|
|
327
|
+
| `cancelTrimmingDialogConfirmText` | `string` | `"Proceed"` | Cancel trimming dialog confirm button |
|
|
328
|
+
</details>
|
|
329
|
+
|
|
330
|
+
<details>
|
|
331
|
+
<summary><strong>Error Dialog</strong></summary>
|
|
332
|
+
|
|
333
|
+
| Option | Type | Default | Description |
|
|
334
|
+
|--------|------|---------|-------------|
|
|
335
|
+
| `alertOnFailToLoad` | `boolean` | `true` | Show alert dialog on load failure |
|
|
336
|
+
| `alertOnFailTitle` | `string` | `"Error"` | Error dialog title |
|
|
337
|
+
| `alertOnFailMessage` | `string` | `"Fail to load media..."` | Error dialog message |
|
|
338
|
+
| `alertOnFailCloseText` | `string` | `"Close"` | Error dialog close button text |
|
|
339
|
+
</details>
|
|
340
|
+
|
|
286
341
|
### Advanced Options
|
|
287
342
|
|
|
288
343
|
| Option | Type | Default | Description |
|
|
@@ -291,8 +346,7 @@ All configuration options are optional. Here are the most commonly used ones:
|
|
|
291
346
|
| `closeWhenFinish` | `boolean` | `true` | Close editor when done |
|
|
292
347
|
| `enableRotation` | `boolean` | `false` | Enable video rotation |
|
|
293
348
|
| `rotationAngle` | `number` | `0` | Rotation angle in degrees |
|
|
294
|
-
| `changeStatusBarColorOnOpen` | `boolean` | `false` | Change status bar color (Android) |
|
|
295
|
-
| `alertOnFailToLoad` | `boolean` | `true` | Show alert on load failure |
|
|
349
|
+
| `changeStatusBarColorOnOpen` | `boolean` | `false` | Change status bar color (Android only) |
|
|
296
350
|
|
|
297
351
|
### Example Configuration
|
|
298
352
|
|
|
@@ -567,7 +621,6 @@ export default function VideoTrimmer() {
|
|
|
567
621
|
- Use `trim()` for batch processing without UI
|
|
568
622
|
- Clean up generated files regularly with `cleanFiles()`
|
|
569
623
|
- Consider file compression for large videos
|
|
570
|
-
- Use appropriate `maxDuration` limits
|
|
571
624
|
|
|
572
625
|
## Credits
|
|
573
626
|
|
|
@@ -70,7 +70,6 @@ open class BaseVideoTrimModule internal constructor(
|
|
|
70
70
|
private var outputFile: String? = null
|
|
71
71
|
private var isVideoType = true
|
|
72
72
|
private var editorConfig: ReadableMap? = null
|
|
73
|
-
private var trimOptions: ReadableMap? = null
|
|
74
73
|
private var originalStatusBarColor: Int = Color.TRANSPARENT
|
|
75
74
|
private val shouldChangeStatusBarColorOnOpen: Boolean
|
|
76
75
|
get() = editorConfig?.hasKey("changeStatusBarColorOnOpen") == true && editorConfig?.getBoolean("changeStatusBarColorOnOpen") == true
|
|
@@ -101,8 +100,7 @@ open class BaseVideoTrimModule internal constructor(
|
|
|
101
100
|
Log.d(TAG, "File saved successfully to $uri")
|
|
102
101
|
|
|
103
102
|
if (
|
|
104
|
-
editorConfig?.getBoolean("removeAfterSavedToDocuments") == true
|
|
105
|
-
trimOptions?.getBoolean("removeAfterFailedToSaveDocuments") == true
|
|
103
|
+
editorConfig?.getBoolean("removeAfterSavedToDocuments") == true
|
|
106
104
|
) {
|
|
107
105
|
StorageUtil.deleteFile(outputFile)
|
|
108
106
|
}
|
|
@@ -114,7 +112,7 @@ open class BaseVideoTrimModule internal constructor(
|
|
|
114
112
|
"Failed to save edited video to Documents: ${e.localizedMessage}",
|
|
115
113
|
ErrorCode.FAIL_TO_SAVE_TO_DOCUMENTS
|
|
116
114
|
)
|
|
117
|
-
if (editorConfig?.getBoolean("removeAfterFailedToSaveDocuments") == true
|
|
115
|
+
if (editorConfig?.getBoolean("removeAfterFailedToSaveDocuments") == true) {
|
|
118
116
|
StorageUtil.deleteFile(outputFile)
|
|
119
117
|
}
|
|
120
118
|
} finally {
|
|
@@ -574,8 +572,6 @@ open class BaseVideoTrimModule internal constructor(
|
|
|
574
572
|
}
|
|
575
573
|
|
|
576
574
|
fun trim(url: String, options: ReadableMap?, promise: Promise) {
|
|
577
|
-
trimOptions = options
|
|
578
|
-
|
|
579
575
|
val currentDate = Date()
|
|
580
576
|
val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
|
|
581
577
|
|
|
@@ -593,7 +589,7 @@ open class BaseVideoTrimModule internal constructor(
|
|
|
593
589
|
cmds += arrayOf("-display_rotation", "${options.getDouble("rotationAngle")}")
|
|
594
590
|
}
|
|
595
591
|
|
|
596
|
-
outputFile = StorageUtil.getOutputPath(reactApplicationContext, options?.getString("outputExt") ?: "mp4")
|
|
592
|
+
val outputFile = StorageUtil.getOutputPath(reactApplicationContext, options?.getString("outputExt") ?: "mp4")
|
|
597
593
|
|
|
598
594
|
cmds += arrayOf(
|
|
599
595
|
"-i",
|
|
@@ -633,27 +629,19 @@ open class BaseVideoTrimModule internal constructor(
|
|
|
633
629
|
Exception("Failed to save edited video to Photo Library: " + e.localizedMessage)
|
|
634
630
|
)
|
|
635
631
|
}
|
|
636
|
-
} else {
|
|
637
|
-
if (options?.getBoolean("openDocumentsOnFinish") == true) {
|
|
638
|
-
saveFileToExternalStorage(File(outputFile!!))
|
|
639
|
-
} else if (options?.getBoolean("openShareSheetOnFinish") == true) {
|
|
640
|
-
shareFile(reactApplicationContext, File(outputFile!!))
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
promise.resolve(outputFile)
|
|
644
632
|
}
|
|
645
633
|
}
|
|
646
634
|
ReturnCode.isCancel(returnCode) -> {
|
|
647
635
|
// CANCEL
|
|
648
636
|
println("FFmpeg command was cancelled")
|
|
649
637
|
promise.reject(
|
|
650
|
-
Exception("FFmpeg command was cancelled")
|
|
638
|
+
Exception("FFmpeg command was cancelled with code $returnCode")
|
|
651
639
|
)
|
|
652
640
|
}
|
|
653
641
|
else -> {
|
|
654
642
|
// FAILURE
|
|
655
643
|
val errorMessage = String.format("Command failed with state %s and rc %s.%s", state, returnCode, session.getFailStackTrace());
|
|
656
|
-
|
|
644
|
+
Log.d(TAG, errorMessage)
|
|
657
645
|
promise.reject(
|
|
658
646
|
Exception(errorMessage)
|
|
659
647
|
)
|
|
@@ -669,7 +657,7 @@ open class BaseVideoTrimModule internal constructor(
|
|
|
669
657
|
private fun saveFileToExternalStorage(file: File) {
|
|
670
658
|
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
|
|
671
659
|
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
|
672
|
-
intent.
|
|
660
|
+
intent.type = "*/*" // Change MIME type as needed
|
|
673
661
|
intent.putExtra(Intent.EXTRA_TITLE, file.name)
|
|
674
662
|
reactApplicationContext.currentActivity?.startActivityForResult(intent, REQUEST_CODE_SAVE_FILE)
|
|
675
663
|
}
|
|
@@ -678,7 +666,7 @@ open class BaseVideoTrimModule internal constructor(
|
|
|
678
666
|
val fileUri = FileProvider.getUriForFile(context, context.packageName + ".provider", file)
|
|
679
667
|
|
|
680
668
|
val shareIntent = Intent(Intent.ACTION_SEND)
|
|
681
|
-
shareIntent.
|
|
669
|
+
shareIntent.type = "*/*"
|
|
682
670
|
shareIntent.putExtra(Intent.EXTRA_STREAM, fileUri)
|
|
683
671
|
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
684
672
|
|
|
@@ -99,6 +99,13 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
99
99
|
private boolean isZoomedIn = false;
|
|
100
100
|
private final Handler zoomWaitTimer = new Handler();
|
|
101
101
|
private Runnable zoomRunnable;
|
|
102
|
+
private long zoomedInRangeStart = 0;
|
|
103
|
+
private long zoomedInRangeDuration = 0;
|
|
104
|
+
private boolean isTrimmingLeading = false;
|
|
105
|
+
|
|
106
|
+
// thumbnail caching for zoom functionality
|
|
107
|
+
private final java.util.List<ImageView> cachedFullViewThumbnails = new java.util.ArrayList<>();
|
|
108
|
+
private volatile boolean isGeneratingThumbnails = false;
|
|
102
109
|
|
|
103
110
|
private MediaMetadataRetriever mediaMetadataRetriever;
|
|
104
111
|
private ProgressBar loadingIndicator;
|
|
@@ -244,6 +251,8 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
244
251
|
|
|
245
252
|
private void startShootVideoThumbs(final Context context, int totalThumbsCount, long startPosition, long endPosition) {
|
|
246
253
|
mThumbnailContainer.removeAllViews();
|
|
254
|
+
cachedFullViewThumbnails.clear(); // Clear previous cache
|
|
255
|
+
|
|
247
256
|
VideoTrimmerUtil.shootVideoThumbInBackground(mediaMetadataRetriever, totalThumbsCount, startPosition, endPosition,
|
|
248
257
|
(bitmap, interval) -> {
|
|
249
258
|
if (bitmap != null) {
|
|
@@ -255,6 +264,13 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
255
264
|
layoutParams.width = VideoTrimmerUtil.VIDEO_FRAMES_WIDTH / VideoTrimmerUtil.MAX_COUNT_RANGE;
|
|
256
265
|
thumbImageView.setLayoutParams(layoutParams);
|
|
257
266
|
mThumbnailContainer.addView(thumbImageView);
|
|
267
|
+
|
|
268
|
+
// Cache the thumbnail for zoom functionality
|
|
269
|
+
ImageView cachedView = new ImageView(context);
|
|
270
|
+
cachedView.setImageBitmap(bitmap);
|
|
271
|
+
cachedView.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
|
272
|
+
cachedView.setLayoutParams(layoutParams);
|
|
273
|
+
cachedFullViewThumbnails.add(cachedView);
|
|
258
274
|
});
|
|
259
275
|
}
|
|
260
276
|
});
|
|
@@ -327,12 +343,9 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
327
343
|
}
|
|
328
344
|
|
|
329
345
|
private void updateHandlePositions() {
|
|
330
|
-
|
|
331
|
-
float
|
|
332
|
-
|
|
333
|
-
float containerWidth = trimmerContainerBg.getWidth();
|
|
334
|
-
float leadingHandleX = startPercent * containerWidth;
|
|
335
|
-
float trailingHandleX = endPercent * containerWidth;
|
|
346
|
+
// Use zoom-aware position calculation
|
|
347
|
+
float leadingHandleX = positionForTime(startTime);
|
|
348
|
+
float trailingHandleX = positionForTime(endTime);
|
|
336
349
|
|
|
337
350
|
leadingHandle.setX(leadingHandleX);
|
|
338
351
|
trailingHandle.setX(trailingHandleX + trailingHandle.getWidth());
|
|
@@ -429,12 +442,17 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
429
442
|
|
|
430
443
|
@Override
|
|
431
444
|
public void onDestroy() {
|
|
445
|
+
// Stop any ongoing operations
|
|
446
|
+
isGeneratingThumbnails = false;
|
|
432
447
|
BackgroundExecutor.cancelAll("", true);
|
|
433
448
|
UiThreadExecutor.cancelAll("");
|
|
434
449
|
mContext.getCurrentActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
|
|
435
450
|
mTimingHandler.removeCallbacks(mTimingRunnable);
|
|
436
451
|
zoomWaitTimer.removeCallbacks(zoomRunnable);
|
|
437
452
|
|
|
453
|
+
// Clear cached thumbnails to prevent memory leaks
|
|
454
|
+
cachedFullViewThumbnails.clear();
|
|
455
|
+
|
|
438
456
|
try {
|
|
439
457
|
if (mediaMetadataRetriever != null) {
|
|
440
458
|
mediaMetadataRetriever.release();
|
|
@@ -590,15 +608,41 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
590
608
|
endTimeText.setText(endTime);
|
|
591
609
|
|
|
592
610
|
if (needUpdateProgress) {
|
|
593
|
-
// Update progressIndicator position
|
|
594
|
-
float indicatorPosition
|
|
611
|
+
// Update progressIndicator position using zoom-aware calculation
|
|
612
|
+
float indicatorPosition;
|
|
613
|
+
|
|
614
|
+
if (isZoomedIn) {
|
|
615
|
+
// Calculate position relative to zoomed range
|
|
616
|
+
long visibleRangeStart = getVisibleRangeStart();
|
|
617
|
+
long visibleRangeDuration = getVisibleRangeDuration();
|
|
618
|
+
|
|
619
|
+
// Ensure current position is within visible range for proper calculation
|
|
620
|
+
if (currentPosition < visibleRangeStart || currentPosition > visibleRangeStart + visibleRangeDuration) {
|
|
621
|
+
// If current position is outside visible range, clamp it
|
|
622
|
+
currentPosition = (int) Math.max(visibleRangeStart, Math.min(visibleRangeStart + visibleRangeDuration, currentPosition));
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
float ratio = visibleRangeDuration > 0 ? (float) (currentPosition - visibleRangeStart) / visibleRangeDuration : 0;
|
|
626
|
+
indicatorPosition = ratio * (trimmerContainerBg.getWidth() - progressIndicator.getWidth()) + leadingHandle.getWidth();
|
|
627
|
+
} else {
|
|
628
|
+
// Calculate position relative to full duration (original logic)
|
|
629
|
+
indicatorPosition = mDuration > 0 ? (float) currentPosition / mDuration * (trimmerContainerBg.getWidth() - progressIndicator.getWidth()) + leadingHandle.getWidth() : leadingHandle.getWidth();
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// Ensure indicator stays within handle bounds using actual handle positions
|
|
633
|
+
float leftBoundary = leadingHandle.getX() + leadingHandle.getWidth();
|
|
634
|
+
float rightBoundary = trailingHandle.getX() - progressIndicator.getWidth();
|
|
635
|
+
|
|
636
|
+
// Apply bounds checking based on actual handle positions
|
|
637
|
+
indicatorPosition = Math.max(leftBoundary, Math.min(rightBoundary, indicatorPosition));
|
|
595
638
|
|
|
596
639
|
if (currentSelectedhandle == leadingHandle) {
|
|
597
|
-
float leftBoundary = trimmerContainer.getX();
|
|
598
640
|
progressIndicator.setX(Math.max(leftBoundary, indicatorPosition));
|
|
599
|
-
} else {
|
|
600
|
-
float rightBoundary = trimmerContainer.getX() + trimmerContainer.getWidth() - progressIndicator.getWidth();
|
|
641
|
+
} else if (currentSelectedhandle == trailingHandle) {
|
|
601
642
|
progressIndicator.setX(Math.min(rightBoundary, indicatorPosition));
|
|
643
|
+
} else {
|
|
644
|
+
// Normal playback - use calculated position with handle bounds
|
|
645
|
+
progressIndicator.setX(indicatorPosition);
|
|
602
646
|
}
|
|
603
647
|
}
|
|
604
648
|
}
|
|
@@ -636,9 +680,11 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
636
680
|
private void onTrimmerContainerPanned(MotionEvent event) {
|
|
637
681
|
float newX = event.getRawX();
|
|
638
682
|
boolean didClamp = false;
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
float
|
|
683
|
+
|
|
684
|
+
// Use handle positions for boundaries instead of trimmer container
|
|
685
|
+
float leftBoundary = leadingHandle.getX() + leadingHandle.getWidth();
|
|
686
|
+
float rightBoundary = trailingHandle.getX() - progressIndicator.getWidth();
|
|
687
|
+
|
|
642
688
|
newX = Math.max(leftBoundary, newX);
|
|
643
689
|
newX = Math.min(rightBoundary, newX);
|
|
644
690
|
|
|
@@ -658,9 +704,21 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
658
704
|
|
|
659
705
|
float indicatorPosition = newX - (trimmerContainerBg.getX());
|
|
660
706
|
|
|
661
|
-
//
|
|
662
|
-
float indicatorPositionPercent
|
|
663
|
-
long newVideoPosition
|
|
707
|
+
// Calculate video position based on zoom state
|
|
708
|
+
float indicatorPositionPercent;
|
|
709
|
+
long newVideoPosition;
|
|
710
|
+
|
|
711
|
+
if (isZoomedIn) {
|
|
712
|
+
// Calculate relative to zoomed range
|
|
713
|
+
indicatorPositionPercent = indicatorPosition / (trimmerContainerBg.getWidth() - progressIndicator.getWidth());
|
|
714
|
+
long visibleStart = getVisibleRangeStart();
|
|
715
|
+
long visibleDuration = getVisibleRangeDuration();
|
|
716
|
+
newVideoPosition = visibleStart + (long) (indicatorPositionPercent * visibleDuration);
|
|
717
|
+
} else {
|
|
718
|
+
// Calculate relative to full duration
|
|
719
|
+
indicatorPositionPercent = indicatorPosition / (trimmerContainerBg.getWidth() - progressIndicator.getWidth());
|
|
720
|
+
newVideoPosition = (long) (indicatorPositionPercent * mDuration);
|
|
721
|
+
}
|
|
664
722
|
|
|
665
723
|
seekTo(newVideoPosition, false);
|
|
666
724
|
}
|
|
@@ -676,6 +734,7 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
676
734
|
fadeOutProgressIndicator();
|
|
677
735
|
seekTo(isLeading ? startTime : endTime, true);
|
|
678
736
|
playHapticFeedback(true);
|
|
737
|
+
isTrimmingLeading = isLeading;
|
|
679
738
|
break;
|
|
680
739
|
case MotionEvent.ACTION_MOVE:
|
|
681
740
|
if (draggingDisabled) {
|
|
@@ -684,6 +743,8 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
684
743
|
|
|
685
744
|
boolean didClamp = false;
|
|
686
745
|
float newX = event.getRawX() - ((float) view.getWidth() / 2);
|
|
746
|
+
|
|
747
|
+
// Handle constraints need to consider zoom state
|
|
687
748
|
if (isLeading) {
|
|
688
749
|
newX = Math.max(0, Math.min(newX, trailingHandle.getX() - view.getWidth()));
|
|
689
750
|
} else {
|
|
@@ -692,54 +753,114 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
692
753
|
|
|
693
754
|
view.setX(newX);
|
|
694
755
|
|
|
695
|
-
// Calculate new startTime or endTime
|
|
756
|
+
// Calculate new startTime or endTime based on zoom state
|
|
696
757
|
if (isLeading) {
|
|
697
758
|
// Calculate the new startTime based on the handle's new position
|
|
698
|
-
long newStartTime = (
|
|
759
|
+
long newStartTime = timeForPosition(newX);
|
|
699
760
|
// Calculate the duration between the new startTime and the current endTime
|
|
700
761
|
long duration = endTime - newStartTime;
|
|
701
762
|
if (duration >= mMinDuration && duration <= mMaxDuration) {
|
|
702
763
|
// If the duration is within the allowed range, update startTime and move the progress indicator
|
|
703
764
|
startTime = newStartTime;
|
|
704
|
-
|
|
765
|
+
float indicatorX = newX + view.getWidth();
|
|
766
|
+
// Ensure progress indicator stays within handle bounds
|
|
767
|
+
float leftBoundary = leadingHandle.getX() + leadingHandle.getWidth();
|
|
768
|
+
float rightBoundary = trailingHandle.getX() - progressIndicator.getWidth();
|
|
769
|
+
progressIndicator.setX(Math.max(leftBoundary, Math.min(rightBoundary, indicatorX)));
|
|
705
770
|
} else if (duration < mMinDuration) {
|
|
706
771
|
didClamp = true;
|
|
707
|
-
// If the duration is less than the minimum,
|
|
772
|
+
// If the duration is less than the minimum, calculate maximum startTime to maintain minimum duration
|
|
708
773
|
startTime = endTime - mMinDuration;
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
774
|
+
|
|
775
|
+
// In zoom mode, don't recalculate position - keep handle where it is but update times
|
|
776
|
+
if (isZoomedIn) {
|
|
777
|
+
// Keep handle at current position but clamp times
|
|
778
|
+
float indicatorX = newX + view.getWidth();
|
|
779
|
+
float leftBoundary = leadingHandle.getX() + leadingHandle.getWidth();
|
|
780
|
+
float rightBoundary = trailingHandle.getX() - progressIndicator.getWidth();
|
|
781
|
+
progressIndicator.setX(Math.max(leftBoundary, Math.min(rightBoundary, indicatorX)));
|
|
782
|
+
} else {
|
|
783
|
+
// Normal mode - adjust handle position
|
|
784
|
+
view.setX(positionForTime(startTime));
|
|
785
|
+
float indicatorX = view.getX() + view.getWidth();
|
|
786
|
+
float leftBoundary = leadingHandle.getX() + leadingHandle.getWidth();
|
|
787
|
+
float rightBoundary = trailingHandle.getX() - progressIndicator.getWidth();
|
|
788
|
+
progressIndicator.setX(Math.max(leftBoundary, Math.min(rightBoundary, indicatorX)));
|
|
789
|
+
}
|
|
712
790
|
} else {
|
|
713
791
|
didClamp = true;
|
|
714
|
-
// If the duration is greater than the maximum,
|
|
792
|
+
// If the duration is greater than the maximum, calculate minimum startTime to maintain maximum duration
|
|
715
793
|
startTime = endTime - mMaxDuration;
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
794
|
+
|
|
795
|
+
// In zoom mode, don't recalculate position - keep handle where it is but update times
|
|
796
|
+
if (isZoomedIn) {
|
|
797
|
+
// Keep handle at current position but clamp times
|
|
798
|
+
float indicatorX = newX + view.getWidth();
|
|
799
|
+
float leftBoundary = leadingHandle.getX() + leadingHandle.getWidth();
|
|
800
|
+
float rightBoundary = trailingHandle.getX() - progressIndicator.getWidth();
|
|
801
|
+
progressIndicator.setX(Math.max(leftBoundary, Math.min(rightBoundary, indicatorX)));
|
|
802
|
+
} else {
|
|
803
|
+
// Normal mode - adjust handle position
|
|
804
|
+
view.setX(positionForTime(startTime));
|
|
805
|
+
float indicatorX = view.getX() + view.getWidth();
|
|
806
|
+
float leftBoundary = leadingHandle.getX() + leadingHandle.getWidth();
|
|
807
|
+
float rightBoundary = trailingHandle.getX() - progressIndicator.getWidth();
|
|
808
|
+
progressIndicator.setX(Math.max(leftBoundary, Math.min(rightBoundary, indicatorX)));
|
|
809
|
+
}
|
|
719
810
|
}
|
|
720
811
|
} else {
|
|
721
812
|
// Calculate the new endTime based on the handle's new position
|
|
722
|
-
long newEndTime = (
|
|
813
|
+
long newEndTime = timeForPosition(newX - view.getWidth());
|
|
723
814
|
// Calculate the duration between the new endTime and the current startTime
|
|
724
815
|
long duration = newEndTime - startTime;
|
|
725
816
|
if (duration >= mMinDuration && duration <= mMaxDuration) {
|
|
726
817
|
// If the duration is within the allowed range, update endTime and move the progress indicator
|
|
727
818
|
endTime = newEndTime;
|
|
728
|
-
|
|
819
|
+
float indicatorX = newX - progressIndicator.getWidth();
|
|
820
|
+
// Ensure progress indicator stays within handle bounds
|
|
821
|
+
float leftBoundary = leadingHandle.getX() + leadingHandle.getWidth();
|
|
822
|
+
float rightBoundary = trailingHandle.getX() - progressIndicator.getWidth();
|
|
823
|
+
progressIndicator.setX(Math.max(leftBoundary, Math.min(rightBoundary, indicatorX)));
|
|
729
824
|
} else if (duration < mMinDuration) {
|
|
730
825
|
didClamp = true;
|
|
731
|
-
// If the duration is less than the minimum,
|
|
826
|
+
// If the duration is less than the minimum, calculate minimum endTime to maintain minimum duration
|
|
732
827
|
endTime = startTime + mMinDuration;
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
828
|
+
|
|
829
|
+
// In zoom mode, don't recalculate position - keep handle where it is but update times
|
|
830
|
+
if (isZoomedIn) {
|
|
831
|
+
// Keep handle at current position but clamp times
|
|
832
|
+
float indicatorX = newX - progressIndicator.getWidth();
|
|
833
|
+
float leftBoundary = leadingHandle.getX() + leadingHandle.getWidth();
|
|
834
|
+
float rightBoundary = trailingHandle.getX() - progressIndicator.getWidth();
|
|
835
|
+
progressIndicator.setX(Math.max(leftBoundary, Math.min(rightBoundary, indicatorX)));
|
|
836
|
+
} else {
|
|
837
|
+
// Normal mode - adjust handle position
|
|
838
|
+
view.setX(positionForTime(endTime) + view.getWidth());
|
|
839
|
+
float indicatorX = view.getX() - progressIndicator.getWidth();
|
|
840
|
+
float leftBoundary = leadingHandle.getX() + leadingHandle.getWidth();
|
|
841
|
+
float rightBoundary = trailingHandle.getX() - progressIndicator.getWidth();
|
|
842
|
+
progressIndicator.setX(Math.max(leftBoundary, Math.min(rightBoundary, indicatorX)));
|
|
843
|
+
}
|
|
736
844
|
} else {
|
|
737
845
|
didClamp = true;
|
|
738
|
-
// If the duration is greater than the maximum,
|
|
846
|
+
// If the duration is greater than the maximum, calculate maximum endTime to maintain maximum duration
|
|
739
847
|
endTime = startTime + mMaxDuration;
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
848
|
+
|
|
849
|
+
// In zoom mode, don't recalculate position - keep handle where it is but update times
|
|
850
|
+
if (isZoomedIn) {
|
|
851
|
+
// Keep handle at current position but clamp times
|
|
852
|
+
float indicatorX = newX - progressIndicator.getWidth();
|
|
853
|
+
float leftBoundary = leadingHandle.getX() + leadingHandle.getWidth();
|
|
854
|
+
float rightBoundary = trailingHandle.getX() - progressIndicator.getWidth();
|
|
855
|
+
progressIndicator.setX(Math.max(leftBoundary, Math.min(rightBoundary, indicatorX)));
|
|
856
|
+
} else {
|
|
857
|
+
// Normal mode - adjust handle position
|
|
858
|
+
view.setX(positionForTime(endTime) + view.getWidth());
|
|
859
|
+
float indicatorX = view.getX() - progressIndicator.getWidth();
|
|
860
|
+
float leftBoundary = leadingHandle.getX() + leadingHandle.getWidth();
|
|
861
|
+
float rightBoundary = trailingHandle.getX() - progressIndicator.getWidth();
|
|
862
|
+
progressIndicator.setX(Math.max(leftBoundary, Math.min(rightBoundary, indicatorX)));
|
|
863
|
+
}
|
|
743
864
|
}
|
|
744
865
|
}
|
|
745
866
|
|
|
@@ -751,11 +872,11 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
751
872
|
updateTrimmerContainerWidth();
|
|
752
873
|
seekTo(isLeading ? startTime : endTime, false);
|
|
753
874
|
|
|
754
|
-
//
|
|
755
|
-
|
|
875
|
+
// Start zoom wait timer when dragging handles
|
|
876
|
+
startZoomWaitTimer();
|
|
756
877
|
break;
|
|
757
878
|
case MotionEvent.ACTION_UP:
|
|
758
|
-
|
|
879
|
+
stopZoomIfNeeded();
|
|
759
880
|
fadeInProgressIndicator();
|
|
760
881
|
view.performClick();
|
|
761
882
|
break;
|
|
@@ -805,7 +926,6 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
805
926
|
}
|
|
806
927
|
|
|
807
928
|
zoomRunnable = () -> {
|
|
808
|
-
Log.i("tag", "A Kiss after 500ms");
|
|
809
929
|
stopZoomWaitTimer();
|
|
810
930
|
zoomIfNeeded();
|
|
811
931
|
};
|
|
@@ -814,12 +934,32 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
814
934
|
}
|
|
815
935
|
|
|
816
936
|
private void stopZoomWaitTimer() {
|
|
817
|
-
|
|
937
|
+
if (zoomRunnable != null) {
|
|
938
|
+
zoomWaitTimer.removeCallbacks(zoomRunnable);
|
|
939
|
+
}
|
|
818
940
|
}
|
|
819
941
|
|
|
820
942
|
private void stopZoomIfNeeded() {
|
|
821
943
|
stopZoomWaitTimer();
|
|
822
|
-
isZoomedIn
|
|
944
|
+
if (isZoomedIn) {
|
|
945
|
+
// Stop any ongoing thumbnail generation immediately
|
|
946
|
+
isGeneratingThumbnails = false;
|
|
947
|
+
|
|
948
|
+
// Cancel any ongoing background tasks for thumbnail generation
|
|
949
|
+
BackgroundExecutor.cancelAll("progressive_thumbs", true);
|
|
950
|
+
|
|
951
|
+
isZoomedIn = false;
|
|
952
|
+
|
|
953
|
+
// Immediately restore cached thumbnails without waiting for animation
|
|
954
|
+
restoreCachedThumbnails();
|
|
955
|
+
|
|
956
|
+
// Then apply smooth transition animation
|
|
957
|
+
animateZoomTransition(() -> {
|
|
958
|
+
updateHandlePositions();
|
|
959
|
+
// Force update progress indicator position after exiting zoom
|
|
960
|
+
updateCurrentTime(true);
|
|
961
|
+
});
|
|
962
|
+
}
|
|
823
963
|
}
|
|
824
964
|
|
|
825
965
|
private void zoomIfNeeded() {
|
|
@@ -827,9 +967,200 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
827
967
|
return;
|
|
828
968
|
}
|
|
829
969
|
|
|
830
|
-
|
|
970
|
+
// Store current handle positions to maintain visual continuity
|
|
971
|
+
float currentLeadingX = leadingHandle.getX();
|
|
972
|
+
float currentTrailingX = trailingHandle.getX();
|
|
973
|
+
|
|
974
|
+
// Calculate zoom range similar to iOS implementation
|
|
975
|
+
long newDuration = mDuration > 4000 ? 2000 : Math.max(1000, mDuration / 2); // At least 1 second, max 2 seconds or half duration
|
|
976
|
+
|
|
977
|
+
// Ensure zoom duration doesn't exceed video duration
|
|
978
|
+
newDuration = Math.min(newDuration, mDuration);
|
|
979
|
+
|
|
980
|
+
long rangeStart;
|
|
981
|
+
if (isTrimmingLeading) {
|
|
982
|
+
// Zoom around the start time, but ensure we don't go before video start
|
|
983
|
+
rangeStart = Math.max(0, startTime - (newDuration / 2));
|
|
984
|
+
// If range would extend past video end, adjust start
|
|
985
|
+
if (rangeStart + newDuration > mDuration) {
|
|
986
|
+
rangeStart = Math.max(0, mDuration - newDuration);
|
|
987
|
+
}
|
|
988
|
+
} else {
|
|
989
|
+
// Zoom around the end time
|
|
990
|
+
rangeStart = Math.max(0, endTime - (newDuration / 2));
|
|
991
|
+
// If range would extend past video end, adjust start
|
|
992
|
+
if (rangeStart + newDuration > mDuration) {
|
|
993
|
+
rangeStart = Math.max(0, mDuration - newDuration);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
// Final bounds check
|
|
998
|
+
zoomedInRangeStart = Math.max(0, rangeStart);
|
|
999
|
+
zoomedInRangeDuration = Math.min(newDuration, mDuration - zoomedInRangeStart);
|
|
831
1000
|
|
|
832
1001
|
isZoomedIn = true;
|
|
1002
|
+
|
|
1003
|
+
// Start progressive thumbnail generation immediately
|
|
1004
|
+
startProgressiveThumbnailGeneration();
|
|
1005
|
+
|
|
1006
|
+
// Update handle positions immediately without delay
|
|
1007
|
+
updateHandlePositionsForZoom(currentLeadingX, currentTrailingX);
|
|
1008
|
+
|
|
1009
|
+
// Provide haptic feedback
|
|
1010
|
+
playHapticFeedback(true);
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
private void updateHandlePositionsForZoom(float previousLeadingX, float previousTrailingX) {
|
|
1014
|
+
// During zoom, we want to keep handles at their current visual positions
|
|
1015
|
+
// Don't recalculate based on zoom range - this causes jumping
|
|
1016
|
+
|
|
1017
|
+
Log.d(TAG, "Maintaining handle positions during zoom - Leading: " + previousLeadingX + ", Trailing: " + previousTrailingX);
|
|
1018
|
+
|
|
1019
|
+
// Keep handles exactly where they were visually
|
|
1020
|
+
leadingHandle.setX(previousLeadingX);
|
|
1021
|
+
trailingHandle.setX(previousTrailingX);
|
|
1022
|
+
|
|
1023
|
+
// Don't update times here - let the individual handle drag logic handle that
|
|
1024
|
+
// This prevents unwanted changes to startTime/endTime during zoom transition
|
|
1025
|
+
|
|
1026
|
+
// Update trimmer container width based on current handle positions
|
|
1027
|
+
updateTrimmerContainerWidth();
|
|
1028
|
+
|
|
1029
|
+
// Ensure progress indicator is positioned correctly within the handle bounds
|
|
1030
|
+
float leftBoundary = leadingHandle.getX() + leadingHandle.getWidth();
|
|
1031
|
+
float rightBoundary = trailingHandle.getX() - progressIndicator.getWidth();
|
|
1032
|
+
float currentX = progressIndicator.getX();
|
|
1033
|
+
|
|
1034
|
+
// If progress indicator is out of bounds, position it properly
|
|
1035
|
+
if (currentX < leftBoundary || currentX > rightBoundary) {
|
|
1036
|
+
// Position it based on the current media position
|
|
1037
|
+
updateCurrentTime(true);
|
|
1038
|
+
} else {
|
|
1039
|
+
updateCurrentTime(false);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
trimmerContainerWrapper.setVisibility(View.VISIBLE);
|
|
1043
|
+
if (trimmerContainerWrapper.getAlpha() == 0f) {
|
|
1044
|
+
trimmerContainerWrapper.animate().alpha(1f).setDuration(250).start();
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
private void startProgressiveThumbnailGeneration() {
|
|
1049
|
+
if (isGeneratingThumbnails || mediaMetadataRetriever == null) {
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
isGeneratingThumbnails = true;
|
|
1054
|
+
|
|
1055
|
+
// Immediately create placeholder thumbnails with subtle animation
|
|
1056
|
+
UiThreadExecutor.runTask("", () -> {
|
|
1057
|
+
mThumbnailContainer.removeAllViews();
|
|
1058
|
+
|
|
1059
|
+
// Calculate proper number of thumbnails based on container width
|
|
1060
|
+
final int thumbnailWidth = VideoTrimmerUtil.VIDEO_FRAMES_WIDTH / VideoTrimmerUtil.MAX_COUNT_RANGE;
|
|
1061
|
+
final int numberOfThumbnails = Math.max(8, mThumbnailContainer.getWidth() / thumbnailWidth);
|
|
1062
|
+
|
|
1063
|
+
// Create placeholder thumbnails first
|
|
1064
|
+
for (int i = 0; i < numberOfThumbnails; i++) {
|
|
1065
|
+
ImageView placeholder = new ImageView(getContext());
|
|
1066
|
+
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(thumbnailWidth, LinearLayout.LayoutParams.MATCH_PARENT);
|
|
1067
|
+
placeholder.setLayoutParams(layoutParams);
|
|
1068
|
+
placeholder.setBackgroundColor(Color.parseColor("#F0F0F0")); // Light gray placeholder
|
|
1069
|
+
placeholder.setAlpha(0.2f);
|
|
1070
|
+
mThumbnailContainer.addView(placeholder);
|
|
1071
|
+
}
|
|
1072
|
+
}, 0);
|
|
1073
|
+
|
|
1074
|
+
// Start background thumbnail generation
|
|
1075
|
+
BackgroundExecutor.execute(new BackgroundExecutor.Task("progressive_thumbs", 0L, "") {
|
|
1076
|
+
@Override
|
|
1077
|
+
public void execute() {
|
|
1078
|
+
try {
|
|
1079
|
+
final int thumbnailWidth = VideoTrimmerUtil.VIDEO_FRAMES_WIDTH / VideoTrimmerUtil.MAX_COUNT_RANGE;
|
|
1080
|
+
final int numberOfThumbnails = Math.max(8, mThumbnailContainer.getWidth() / thumbnailWidth);
|
|
1081
|
+
final long visibleDuration = isZoomedIn ? zoomedInRangeDuration : mDuration;
|
|
1082
|
+
final long visibleStart = isZoomedIn ? zoomedInRangeStart : 0;
|
|
1083
|
+
final long interval = visibleDuration > 0 ? visibleDuration / numberOfThumbnails : 0;
|
|
1084
|
+
|
|
1085
|
+
// Generate thumbnails progressively
|
|
1086
|
+
for (int i = 0; i < numberOfThumbnails && isGeneratingThumbnails && isZoomedIn; i++) {
|
|
1087
|
+
// Check if we should continue generating
|
|
1088
|
+
if (!isGeneratingThumbnails || !isZoomedIn) {
|
|
1089
|
+
Log.d(TAG, "Thumbnail generation cancelled at index " + i);
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
final int index = i;
|
|
1094
|
+
final long timeUs = (visibleStart + (i * interval)) * 1000; // Convert to microseconds
|
|
1095
|
+
final long clampedTimeUs = Math.max(0, Math.min(timeUs, mDuration * 1000L));
|
|
1096
|
+
|
|
1097
|
+
try {
|
|
1098
|
+
Bitmap bitmap = mediaMetadataRetriever.getFrameAtTime(clampedTimeUs, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
|
|
1099
|
+
if (bitmap != null && isGeneratingThumbnails && isZoomedIn) {
|
|
1100
|
+
// Update UI immediately for each thumbnail
|
|
1101
|
+
UiThreadExecutor.runTask("", () -> {
|
|
1102
|
+
// Double-check zoom state before updating UI
|
|
1103
|
+
if (isZoomedIn && index < mThumbnailContainer.getChildCount()) {
|
|
1104
|
+
ImageView thumbnailView = (ImageView) mThumbnailContainer.getChildAt(index);
|
|
1105
|
+
if (thumbnailView != null) {
|
|
1106
|
+
thumbnailView.setImageBitmap(bitmap);
|
|
1107
|
+
thumbnailView.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
|
1108
|
+
thumbnailView.setBackground(null); // Remove placeholder background
|
|
1109
|
+
|
|
1110
|
+
// Smooth fade-in animation for each thumbnail
|
|
1111
|
+
thumbnailView.animate()
|
|
1112
|
+
.alpha(1.0f)
|
|
1113
|
+
.setDuration(150)
|
|
1114
|
+
.setStartDelay(index * 50L) // Stagger animations
|
|
1115
|
+
.start();
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
}, 0);
|
|
1119
|
+
|
|
1120
|
+
// Small delay between generations to avoid blocking
|
|
1121
|
+
Thread.sleep(10);
|
|
1122
|
+
}
|
|
1123
|
+
} catch (Exception e) {
|
|
1124
|
+
Log.w(TAG, "Error generating progressive thumbnail at " + clampedTimeUs, e);
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
// Only finalize if we're still in zoom mode
|
|
1129
|
+
if (isGeneratingThumbnails && isZoomedIn) {
|
|
1130
|
+
isGeneratingThumbnails = false;
|
|
1131
|
+
} else {
|
|
1132
|
+
isGeneratingThumbnails = false;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
} catch (Exception e) {
|
|
1136
|
+
Log.e(TAG, "Error in progressive thumbnail generation", e);
|
|
1137
|
+
isGeneratingThumbnails = false;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
private void animateZoomTransition(Runnable onComplete) {
|
|
1144
|
+
// Only animate if we're still transitioning
|
|
1145
|
+
if (mThumbnailContainer != null) {
|
|
1146
|
+
mThumbnailContainer.animate()
|
|
1147
|
+
.alpha(0.7f)
|
|
1148
|
+
.setDuration(200) // Shorter duration for better responsiveness
|
|
1149
|
+
.withEndAction(() -> {
|
|
1150
|
+
if (onComplete != null) {
|
|
1151
|
+
onComplete.run();
|
|
1152
|
+
}
|
|
1153
|
+
if (mThumbnailContainer != null) {
|
|
1154
|
+
mThumbnailContainer.animate()
|
|
1155
|
+
.alpha(1.0f)
|
|
1156
|
+
.setDuration(200)
|
|
1157
|
+
.start();
|
|
1158
|
+
}
|
|
1159
|
+
})
|
|
1160
|
+
.start();
|
|
1161
|
+
} else if (onComplete != null) {
|
|
1162
|
+
onComplete.run();
|
|
1163
|
+
}
|
|
833
1164
|
}
|
|
834
1165
|
|
|
835
1166
|
private void ignoreSystemGestureForView(View v) {
|
|
@@ -850,4 +1181,54 @@ public class VideoTrimmerView extends FrameLayout implements IVideoTrimmerView {
|
|
|
850
1181
|
);
|
|
851
1182
|
}
|
|
852
1183
|
}
|
|
1184
|
+
|
|
1185
|
+
// Helper methods for position/time conversion considering zoom state
|
|
1186
|
+
private long timeForPosition(float position) {
|
|
1187
|
+
if (trimmerContainerBg.getWidth() <= 0) return 0;
|
|
1188
|
+
|
|
1189
|
+
if (isZoomedIn) {
|
|
1190
|
+
// Convert position to time within zoomed range
|
|
1191
|
+
float ratio = position / trimmerContainerBg.getWidth();
|
|
1192
|
+
return zoomedInRangeStart + (long) (ratio * zoomedInRangeDuration);
|
|
1193
|
+
} else {
|
|
1194
|
+
// Convert position to time within full duration
|
|
1195
|
+
return (long) ((position / trimmerContainerBg.getWidth()) * mDuration);
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
private float positionForTime(long time) {
|
|
1200
|
+
if (isZoomedIn) {
|
|
1201
|
+
// Convert time to position within zoomed range
|
|
1202
|
+
if (zoomedInRangeDuration <= 0) return 0;
|
|
1203
|
+
float ratio = (float) (time - zoomedInRangeStart) / zoomedInRangeDuration;
|
|
1204
|
+
return Math.max(0, Math.min(trimmerContainerBg.getWidth(), ratio * trimmerContainerBg.getWidth()));
|
|
1205
|
+
} else {
|
|
1206
|
+
// Convert time to position within full duration
|
|
1207
|
+
if (mDuration <= 0) return 0;
|
|
1208
|
+
return Math.max(0, Math.min(trimmerContainerBg.getWidth(), ((float) time / mDuration) * trimmerContainerBg.getWidth()));
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
private long getVisibleRangeStart() {
|
|
1213
|
+
return isZoomedIn ? zoomedInRangeStart : 0;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
private long getVisibleRangeDuration() {
|
|
1217
|
+
return isZoomedIn ? zoomedInRangeDuration : mDuration;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
private void restoreCachedThumbnails() {
|
|
1221
|
+
// Clear current thumbnails
|
|
1222
|
+
mThumbnailContainer.removeAllViews();
|
|
1223
|
+
|
|
1224
|
+
// Restore cached thumbnails efficiently
|
|
1225
|
+
for (ImageView cachedThumbnail : cachedFullViewThumbnails) {
|
|
1226
|
+
// Create a new ImageView with the same bitmap to avoid view reuse issues
|
|
1227
|
+
ImageView restoredView = new ImageView(getContext());
|
|
1228
|
+
restoredView.setImageBitmap(((android.graphics.drawable.BitmapDrawable) cachedThumbnail.getDrawable()).getBitmap());
|
|
1229
|
+
restoredView.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
|
1230
|
+
restoredView.setLayoutParams(cachedThumbnail.getLayoutParams());
|
|
1231
|
+
mThumbnailContainer.addView(restoredView);
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
853
1234
|
}
|
package/ios/VideoTrim.mm
CHANGED
|
@@ -61,24 +61,21 @@ RCT_EXPORT_MODULE()
|
|
|
61
61
|
dict[@"saveToPhoto"] = @(options.saveToPhoto());
|
|
62
62
|
dict[@"type"] = options.type();
|
|
63
63
|
dict[@"outputExt"] = options.outputExt();
|
|
64
|
-
dict[@"openDocumentsOnFinish"] = @(options.openDocumentsOnFinish());
|
|
65
|
-
dict[@"openShareSheetOnFinish"] = @(options.openShareSheetOnFinish());
|
|
66
64
|
dict[@"removeAfterSavedToPhoto"] = @(options.removeAfterSavedToPhoto());
|
|
67
65
|
dict[@"removeAfterFailedToSavePhoto"] = @(options.removeAfterFailedToSavePhoto());
|
|
68
|
-
dict[@"removeAfterSavedToDocuments"] = @(options.removeAfterSavedToDocuments());
|
|
69
|
-
dict[@"removeAfterFailedToSaveDocuments"] = @(options.removeAfterFailedToSaveDocuments());
|
|
70
|
-
dict[@"removeAfterShared"] = @(options.removeAfterShared());
|
|
71
|
-
dict[@"removeAfterFailedToShare"] = @(options.removeAfterFailedToShare());
|
|
72
66
|
dict[@"enableRotation"] = @(options.enableRotation());
|
|
73
67
|
dict[@"rotationAngle"] = @(options.rotationAngle());
|
|
74
68
|
dict[@"startTime"] = @(options.startTime());
|
|
75
69
|
dict[@"endTime"] = @(options.endTime());
|
|
76
70
|
|
|
77
|
-
[self->videoTrim trim:url url:dict config:^(NSDictionary<NSString *,id> *
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
} else {
|
|
71
|
+
[self->videoTrim trim:url url:dict config:^(NSDictionary<NSString *,id> * _Nonnull result) {
|
|
72
|
+
BOOL success = [result[@"success"] boolValue];
|
|
73
|
+
if (success) {
|
|
81
74
|
resolve(result);
|
|
75
|
+
} else {
|
|
76
|
+
NSString *message = result[@"message"];
|
|
77
|
+
NSError *error = [NSError errorWithDomain:@"" code:200 userInfo:nil];
|
|
78
|
+
reject(@"ERR_TRIM_FAILED", message, error);
|
|
82
79
|
}
|
|
83
80
|
}];
|
|
84
81
|
}
|
package/ios/VideoTrim.swift
CHANGED
|
@@ -466,7 +466,7 @@ public class VideoTrim: RCTEventEmitter, AssetLoaderDelegate, UIDocumentPickerDe
|
|
|
466
466
|
|
|
467
467
|
// New Arch
|
|
468
468
|
@objc(trim:url:config:)
|
|
469
|
-
public func _trim(inputFile: String, config: NSDictionary, completion: @escaping ([String: Any]
|
|
469
|
+
public func _trim(inputFile: String, config: NSDictionary, completion: @escaping ([String: Any]) -> Void) {
|
|
470
470
|
var destPath: URL?
|
|
471
471
|
|
|
472
472
|
if inputFile.hasPrefix("http://") || inputFile.hasPrefix("https://") {
|
|
@@ -476,7 +476,13 @@ public class VideoTrim: RCTEventEmitter, AssetLoaderDelegate, UIDocumentPickerDe
|
|
|
476
476
|
}
|
|
477
477
|
|
|
478
478
|
guard let destPath = destPath else {
|
|
479
|
-
|
|
479
|
+
let result = [
|
|
480
|
+
"success": false,
|
|
481
|
+
"message": "Invalid input file path",
|
|
482
|
+
] as [String : Any]
|
|
483
|
+
|
|
484
|
+
completion(result)
|
|
485
|
+
|
|
480
486
|
return
|
|
481
487
|
}
|
|
482
488
|
|
|
@@ -521,15 +527,30 @@ public class VideoTrim: RCTEventEmitter, AssetLoaderDelegate, UIDocumentPickerDe
|
|
|
521
527
|
let returnCode = session?.getReturnCode()
|
|
522
528
|
|
|
523
529
|
if ReturnCode.isSuccess(returnCode) {
|
|
524
|
-
let result = [
|
|
530
|
+
let result = [
|
|
531
|
+
"success": true,
|
|
532
|
+
"outputPath": outputFile.absoluteString,
|
|
533
|
+
"startTime": startTime,
|
|
534
|
+
"endTime": endTime
|
|
535
|
+
] as [String : Any]
|
|
525
536
|
|
|
526
537
|
completion(result)
|
|
527
538
|
} else if ReturnCode.isCancel(returnCode) {
|
|
528
539
|
// CANCEL
|
|
529
|
-
|
|
540
|
+
let result = [
|
|
541
|
+
"success": false,
|
|
542
|
+
"message": "FFmpeg command was cancelled with code \(returnCode?.getValue() ?? -1)",
|
|
543
|
+
] as [String : Any]
|
|
544
|
+
|
|
545
|
+
completion(result)
|
|
530
546
|
} else {
|
|
531
547
|
// FAILURE
|
|
532
|
-
|
|
548
|
+
let result = [
|
|
549
|
+
"success": false,
|
|
550
|
+
"message": "Command failed with rc \(String(describing: returnCode)).\(String(describing: session?.getFailStackTrace()))",
|
|
551
|
+
] as [String : Any]
|
|
552
|
+
|
|
553
|
+
completion(result)
|
|
533
554
|
}
|
|
534
555
|
}, withLogCallback: nil, withStatisticsCallback: nil)
|
|
535
556
|
}
|
|
@@ -538,7 +559,13 @@ public class VideoTrim: RCTEventEmitter, AssetLoaderDelegate, UIDocumentPickerDe
|
|
|
538
559
|
@objc(trim:withConfig:withResolver:withRejecter:)
|
|
539
560
|
func _trim(inputFile: String, config: NSDictionary, resolve: @escaping RCTPromiseResolveBlock,reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
540
561
|
_trim(inputFile: inputFile, config: config, completion: { payload in
|
|
541
|
-
|
|
562
|
+
if let success = payload["success"] as? Bool, success {
|
|
563
|
+
resolve(payload)
|
|
564
|
+
} else {
|
|
565
|
+
let message = payload["message"] as? String ?? "Unknown error"
|
|
566
|
+
let error = NSError(domain: "", code: 200, userInfo: nil)
|
|
567
|
+
reject("ERR_TRIM_FAILED", message, error)
|
|
568
|
+
}
|
|
542
569
|
})
|
|
543
570
|
}
|
|
544
571
|
|
package/lib/module/index.js
CHANGED
|
@@ -12,14 +12,8 @@ function createBaseOptions(overrides = {}) {
|
|
|
12
12
|
saveToPhoto: false,
|
|
13
13
|
type: 'video',
|
|
14
14
|
outputExt: 'mp4',
|
|
15
|
-
openDocumentsOnFinish: false,
|
|
16
|
-
openShareSheetOnFinish: false,
|
|
17
15
|
removeAfterSavedToPhoto: false,
|
|
18
16
|
removeAfterFailedToSavePhoto: false,
|
|
19
|
-
removeAfterSavedToDocuments: false,
|
|
20
|
-
removeAfterFailedToSaveDocuments: false,
|
|
21
|
-
removeAfterShared: false,
|
|
22
|
-
removeAfterFailedToShare: false,
|
|
23
17
|
enableRotation: false,
|
|
24
18
|
rotationAngle: 0,
|
|
25
19
|
...overrides
|
|
@@ -30,6 +24,12 @@ function createEditorConfig(overrides = {}) {
|
|
|
30
24
|
enableHapticFeedback: true,
|
|
31
25
|
maxDuration: -1,
|
|
32
26
|
minDuration: -1,
|
|
27
|
+
openDocumentsOnFinish: false,
|
|
28
|
+
openShareSheetOnFinish: false,
|
|
29
|
+
removeAfterSavedToDocuments: false,
|
|
30
|
+
removeAfterFailedToSaveDocuments: false,
|
|
31
|
+
removeAfterShared: false,
|
|
32
|
+
removeAfterFailedToShare: false,
|
|
33
33
|
cancelButtonText: 'Cancel',
|
|
34
34
|
saveButtonText: 'Save',
|
|
35
35
|
enableCancelDialog: true,
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["VideoTrimNewArch","VideoTrimOldArch","processColor","isFabric","global","nativeFabricUIManager","VideoTrim","createBaseOptions","overrides","saveToPhoto","type","outputExt","
|
|
1
|
+
{"version":3,"names":["VideoTrimNewArch","VideoTrimOldArch","processColor","isFabric","global","nativeFabricUIManager","VideoTrim","createBaseOptions","overrides","saveToPhoto","type","outputExt","removeAfterSavedToPhoto","removeAfterFailedToSavePhoto","enableRotation","rotationAngle","createEditorConfig","enableHapticFeedback","maxDuration","minDuration","openDocumentsOnFinish","openShareSheetOnFinish","removeAfterSavedToDocuments","removeAfterFailedToSaveDocuments","removeAfterShared","removeAfterFailedToShare","cancelButtonText","saveButtonText","enableCancelDialog","cancelDialogTitle","cancelDialogMessage","cancelDialogCancelText","cancelDialogConfirmText","enableSaveDialog","saveDialogTitle","saveDialogMessage","saveDialogCancelText","saveDialogConfirmText","trimmingText","fullScreenModalIOS","autoplay","jumpToPositionOnLoad","closeWhenFinish","enableCancelTrimming","cancelTrimmingButtonText","enableCancelTrimmingDialog","cancelTrimmingDialogTitle","cancelTrimmingDialogMessage","cancelTrimmingDialogCancelText","cancelTrimmingDialogConfirmText","headerText","headerTextSize","headerTextColor","trimmerColor","handleIconColor","alertOnFailToLoad","alertOnFailTitle","alertOnFailMessage","alertOnFailCloseText","createTrimOptions","startTime","endTime","showEditor","filePath","config","_headerTextColor","_trimmerColor","_handleIconColor","listFiles","cleanFiles","deleteFile","trim","length","Error","closeEditor","isValidFile","url","options"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -4,14 +4,8 @@ export interface BaseOptions {
|
|
|
4
4
|
saveToPhoto: boolean;
|
|
5
5
|
type: string;
|
|
6
6
|
outputExt: string;
|
|
7
|
-
openDocumentsOnFinish: boolean;
|
|
8
|
-
openShareSheetOnFinish: boolean;
|
|
9
7
|
removeAfterSavedToPhoto: boolean;
|
|
10
8
|
removeAfterFailedToSavePhoto: boolean;
|
|
11
|
-
removeAfterSavedToDocuments: boolean;
|
|
12
|
-
removeAfterFailedToSaveDocuments: boolean;
|
|
13
|
-
removeAfterShared: boolean;
|
|
14
|
-
removeAfterFailedToShare: boolean;
|
|
15
9
|
enableRotation: boolean;
|
|
16
10
|
rotationAngle: number;
|
|
17
11
|
}
|
|
@@ -19,6 +13,12 @@ export interface EditorConfig extends BaseOptions {
|
|
|
19
13
|
enableHapticFeedback: boolean;
|
|
20
14
|
maxDuration: number;
|
|
21
15
|
minDuration: number;
|
|
16
|
+
openDocumentsOnFinish: boolean;
|
|
17
|
+
openShareSheetOnFinish: boolean;
|
|
18
|
+
removeAfterSavedToDocuments: boolean;
|
|
19
|
+
removeAfterFailedToSaveDocuments: boolean;
|
|
20
|
+
removeAfterShared: boolean;
|
|
21
|
+
removeAfterFailedToShare: boolean;
|
|
22
22
|
cancelButtonText: string;
|
|
23
23
|
saveButtonText: string;
|
|
24
24
|
enableCancelDialog: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativeVideoTrim.d.ts","sourceRoot":"","sources":["../../../src/NativeVideoTrim.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2CAA2C,CAAC;AAE9E,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,
|
|
1
|
+
{"version":3,"file":"NativeVideoTrim.d.ts","sourceRoot":"","sources":["../../../src/NativeVideoTrim.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/NativeVideoTrim.ts
CHANGED
|
@@ -6,14 +6,8 @@ export interface BaseOptions {
|
|
|
6
6
|
saveToPhoto: boolean;
|
|
7
7
|
type: string;
|
|
8
8
|
outputExt: string;
|
|
9
|
-
openDocumentsOnFinish: boolean;
|
|
10
|
-
openShareSheetOnFinish: boolean;
|
|
11
9
|
removeAfterSavedToPhoto: boolean;
|
|
12
10
|
removeAfterFailedToSavePhoto: boolean;
|
|
13
|
-
removeAfterSavedToDocuments: boolean;
|
|
14
|
-
removeAfterFailedToSaveDocuments: boolean;
|
|
15
|
-
removeAfterShared: boolean;
|
|
16
|
-
removeAfterFailedToShare: boolean;
|
|
17
11
|
enableRotation: boolean;
|
|
18
12
|
rotationAngle: number;
|
|
19
13
|
}
|
|
@@ -22,6 +16,12 @@ export interface EditorConfig extends BaseOptions {
|
|
|
22
16
|
enableHapticFeedback: boolean;
|
|
23
17
|
maxDuration: number;
|
|
24
18
|
minDuration: number;
|
|
19
|
+
openDocumentsOnFinish: boolean;
|
|
20
|
+
openShareSheetOnFinish: boolean;
|
|
21
|
+
removeAfterSavedToDocuments: boolean;
|
|
22
|
+
removeAfterFailedToSaveDocuments: boolean;
|
|
23
|
+
removeAfterShared: boolean;
|
|
24
|
+
removeAfterFailedToShare: boolean;
|
|
25
25
|
cancelButtonText: string;
|
|
26
26
|
saveButtonText: string;
|
|
27
27
|
enableCancelDialog: boolean;
|
package/src/index.tsx
CHANGED
|
@@ -17,14 +17,8 @@ function createBaseOptions(overrides: Partial<BaseOptions> = {}): BaseOptions {
|
|
|
17
17
|
saveToPhoto: false,
|
|
18
18
|
type: 'video',
|
|
19
19
|
outputExt: 'mp4',
|
|
20
|
-
openDocumentsOnFinish: false,
|
|
21
|
-
openShareSheetOnFinish: false,
|
|
22
20
|
removeAfterSavedToPhoto: false,
|
|
23
21
|
removeAfterFailedToSavePhoto: false,
|
|
24
|
-
removeAfterSavedToDocuments: false,
|
|
25
|
-
removeAfterFailedToSaveDocuments: false,
|
|
26
|
-
removeAfterShared: false,
|
|
27
|
-
removeAfterFailedToShare: false,
|
|
28
22
|
enableRotation: false,
|
|
29
23
|
rotationAngle: 0,
|
|
30
24
|
...overrides,
|
|
@@ -38,6 +32,12 @@ function createEditorConfig(
|
|
|
38
32
|
enableHapticFeedback: true,
|
|
39
33
|
maxDuration: -1,
|
|
40
34
|
minDuration: -1,
|
|
35
|
+
openDocumentsOnFinish: false,
|
|
36
|
+
openShareSheetOnFinish: false,
|
|
37
|
+
removeAfterSavedToDocuments: false,
|
|
38
|
+
removeAfterFailedToSaveDocuments: false,
|
|
39
|
+
removeAfterShared: false,
|
|
40
|
+
removeAfterFailedToShare: false,
|
|
41
41
|
cancelButtonText: 'Cancel',
|
|
42
42
|
saveButtonText: 'Save',
|
|
43
43
|
enableCancelDialog: true,
|