react-native-video-trim 7.0.0 → 7.1.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 +36 -4
- package/android/src/main/java/com/videotrim/widgets/CropOverlayView.kt +27 -29
- package/android/src/main/java/com/videotrim/widgets/VideoTrimmerView.kt +140 -42
- package/android/src/main/res/layout/video_trimmer_view.xml +7 -5
- package/ios/CropOverlayView.swift +35 -37
- package/ios/VideoTrim.mm +5 -0
- package/ios/VideoTrim.swift +7 -4
- package/ios/VideoTrimmer.swift +22 -12
- package/ios/VideoTrimmerViewController.swift +83 -44
- package/lib/module/NativeVideoTrim.js.map +1 -1
- package/lib/module/index.js +3 -2
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/NativeVideoTrim.d.ts +5 -0
- package/lib/typescript/src/NativeVideoTrim.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/NativeVideoTrim.ts +5 -0
- package/src/index.tsx +7 -2
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* [Behavior Options](#behavior-options)
|
|
13
13
|
- [Platform Setup](#platform-setup)
|
|
14
14
|
- [Advanced Features](#advanced-features)
|
|
15
|
+
* [Theming](#theming)
|
|
15
16
|
* [Audio Trimming](#audio-trimming)
|
|
16
17
|
* [Remote Files (HTTPS)](#remote-files-https)
|
|
17
18
|
* [Video Transforms (Flip, Rotate, Crop)](#video-transforms-flip-rotate-crop)
|
|
@@ -24,8 +25,7 @@
|
|
|
24
25
|
<div align="center">
|
|
25
26
|
<h2>📱 Professional video trimmer for React Native apps</h2>
|
|
26
27
|
|
|
27
|
-
<img src="images/
|
|
28
|
-
<img src="images/ios.gif" width="300" />
|
|
28
|
+
<img src="images/ios.png" width="300" />
|
|
29
29
|
|
|
30
30
|
<p>
|
|
31
31
|
<strong>✅ iOS & Android</strong> •
|
|
@@ -48,6 +48,7 @@ A powerful, easy-to-use video and audio trimming library for React Native applic
|
|
|
48
48
|
- **✅ File Validation** - Built-in validation for media files
|
|
49
49
|
- **🗂️ File Management** - List, clean up, and delete specific files
|
|
50
50
|
- **🔄 Universal Architecture** - Works with both New and Old React Native architectures
|
|
51
|
+
- **🎨 Dark & Light Theme** - Built-in dark and light theme support
|
|
51
52
|
|
|
52
53
|
### 🎛️ Core Capabilities
|
|
53
54
|
|
|
@@ -60,6 +61,7 @@ A powerful, easy-to-use video and audio trimming library for React Native applic
|
|
|
60
61
|
| **Save Options** | Photos, Documents, Share sheet integration |
|
|
61
62
|
| **File Management** | Complete file lifecycle management |
|
|
62
63
|
| **Customization** | Extensive UI and behavior customization |
|
|
64
|
+
| **Theming** | Dark and light theme with automatic color adaptation |
|
|
63
65
|
|
|
64
66
|
<div align="center">
|
|
65
67
|
<img src="images/document_picker.png" width="250" />
|
|
@@ -282,14 +284,15 @@ All configuration options are optional. Here are the most commonly used ones:
|
|
|
282
284
|
|
|
283
285
|
| Option | Type | Default | Description |
|
|
284
286
|
|--------|------|---------|-------------|
|
|
287
|
+
| `theme` | `'dark' \| 'light'` | `'dark'` | Editor color theme (see [Theming](#theming)) |
|
|
285
288
|
| `cancelButtonText` | `string` | `"Cancel"` | Cancel button text |
|
|
286
289
|
| `saveButtonText` | `string` | `"Save"` | Save button text |
|
|
287
290
|
| `trimmingText` | `string` | `"Trimming video..."` | Progress dialog text |
|
|
288
291
|
| `headerText` | `string` | - | Header text |
|
|
289
292
|
| `headerTextSize` | `number` | `16` | Header text size |
|
|
290
|
-
| `headerTextColor` | `string` | - | Header text color |
|
|
293
|
+
| `headerTextColor` | `string` | - | Header text color (defaults to black in light theme, white in dark theme) |
|
|
291
294
|
| `trimmerColor` | `string` | - | Trimmer bar color |
|
|
292
|
-
| `handleIconColor` | `string` | - | Trimmer left/right handles color |
|
|
295
|
+
| `handleIconColor` | `string` | - | Trimmer left/right handles color (defaults to black in light theme, white in dark theme) |
|
|
293
296
|
| `fullScreenModalIOS` | `boolean` | `false` | Use fullscreen modal on iOS |
|
|
294
297
|
|
|
295
298
|
### Dialog Options
|
|
@@ -367,6 +370,7 @@ showEditor(videoPath, {
|
|
|
367
370
|
removeAfterSavedToPhoto: true,
|
|
368
371
|
|
|
369
372
|
// UI customization
|
|
373
|
+
theme: 'light',
|
|
370
374
|
headerText: "Trim Your Video",
|
|
371
375
|
cancelButtonText: "Back",
|
|
372
376
|
saveButtonText: "Done",
|
|
@@ -398,6 +402,34 @@ buildscript {
|
|
|
398
402
|
|
|
399
403
|
## Advanced Features
|
|
400
404
|
|
|
405
|
+
### Theming
|
|
406
|
+
|
|
407
|
+
The editor supports dark and light themes. Set the `theme` option to switch between them:
|
|
408
|
+
|
|
409
|
+
```javascript
|
|
410
|
+
// Light theme
|
|
411
|
+
showEditor(videoUrl, {
|
|
412
|
+
theme: 'light',
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
// Dark theme (default)
|
|
416
|
+
showEditor(videoUrl, {
|
|
417
|
+
theme: 'dark',
|
|
418
|
+
});
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
| | Dark (default) | Light |
|
|
422
|
+
|---|---|---|
|
|
423
|
+
| **Background** | Black | White |
|
|
424
|
+
| **Icons & text** | White | Black |
|
|
425
|
+
| **Cancel/Save text** | White | Black |
|
|
426
|
+
| **Crop brackets & grid** | White | Black |
|
|
427
|
+
| **Progress indicator** | White | White |
|
|
428
|
+
| **Trimmer handle chevrons** | White | White |
|
|
429
|
+
| **Dialogs** | Dark style | Light style |
|
|
430
|
+
|
|
431
|
+
The `headerTextColor` and `handleIconColor` options automatically adapt to the active theme but can still be overridden explicitly.
|
|
432
|
+
|
|
401
433
|
### Audio Trimming
|
|
402
434
|
|
|
403
435
|
<div align="center">
|
|
@@ -6,8 +6,6 @@ import android.graphics.Color
|
|
|
6
6
|
import android.graphics.Paint
|
|
7
7
|
import android.graphics.Path
|
|
8
8
|
import android.graphics.RectF
|
|
9
|
-
import android.graphics.Region
|
|
10
|
-
import android.os.Build
|
|
11
9
|
import android.util.AttributeSet
|
|
12
10
|
import android.util.TypedValue
|
|
13
11
|
import android.view.MotionEvent
|
|
@@ -46,16 +44,27 @@ class CropOverlayView @JvmOverloads constructor(
|
|
|
46
44
|
var onCropBegan: (() -> Unit)? = null
|
|
47
45
|
var onCropEnded: (() -> Unit)? = null
|
|
48
46
|
|
|
47
|
+
var isLightTheme = false
|
|
48
|
+
set(value) {
|
|
49
|
+
field = value
|
|
50
|
+
val c = if (value) Color.BLACK else Color.WHITE
|
|
51
|
+
borderPaint.color = c
|
|
52
|
+
gridPaint.color = c
|
|
53
|
+
cornerPaint.color = c
|
|
54
|
+
invalidate()
|
|
55
|
+
}
|
|
56
|
+
|
|
49
57
|
private val minCropSize = dpToPx(60f)
|
|
50
58
|
private val borderWidth = dpToPx(1f)
|
|
51
59
|
private val cornerLength = dpToPx(20f)
|
|
52
60
|
private val cornerWidth = dpToPx(4f)
|
|
61
|
+
private val edgeHandleLength = dpToPx(20f)
|
|
53
62
|
private val gridLineWidth = 1f / resources.displayMetrics.density
|
|
54
63
|
private val edgeHitZone = dpToPx(30f)
|
|
55
64
|
|
|
56
65
|
private val borderPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
|
57
66
|
style = Paint.Style.STROKE
|
|
58
|
-
color = Color.
|
|
67
|
+
color = Color.WHITE
|
|
59
68
|
strokeWidth = borderWidth
|
|
60
69
|
}
|
|
61
70
|
private val gridPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
|
@@ -68,10 +77,7 @@ class CropOverlayView @JvmOverloads constructor(
|
|
|
68
77
|
color = Color.WHITE
|
|
69
78
|
strokeWidth = cornerWidth
|
|
70
79
|
strokeCap = Paint.Cap.ROUND
|
|
71
|
-
|
|
72
|
-
private val dimmingPaint = Paint().apply {
|
|
73
|
-
color = Color.argb(140, 0, 0, 0)
|
|
74
|
-
style = Paint.Style.FILL
|
|
80
|
+
strokeJoin = Paint.Join.ROUND
|
|
75
81
|
}
|
|
76
82
|
|
|
77
83
|
private enum class DragEdge {
|
|
@@ -126,8 +132,6 @@ class CropOverlayView @JvmOverloads constructor(
|
|
|
126
132
|
if (cropRect.isEmpty) return
|
|
127
133
|
val cr = cropRect
|
|
128
134
|
|
|
129
|
-
drawDimming(canvas, cr)
|
|
130
|
-
|
|
131
135
|
canvas.drawRect(cr, borderPaint)
|
|
132
136
|
|
|
133
137
|
for (i in 1..2) {
|
|
@@ -140,33 +144,27 @@ class CropOverlayView @JvmOverloads constructor(
|
|
|
140
144
|
}
|
|
141
145
|
|
|
142
146
|
val cl = cornerLength
|
|
147
|
+
val hw = cornerWidth / 2f
|
|
143
148
|
val path = Path()
|
|
144
149
|
fun addCorner(sx: Float, sy: Float, cx: Float, cy: Float, ex: Float, ey: Float) {
|
|
145
150
|
path.moveTo(sx, sy); path.lineTo(cx, cy); path.lineTo(ex, ey)
|
|
146
151
|
}
|
|
147
|
-
addCorner(cr.left, cr.top + cl, cr.left, cr.top, cr.left + cl, cr.top)
|
|
148
|
-
addCorner(cr.right - cl, cr.top, cr.right, cr.top, cr.right, cr.top + cl)
|
|
149
|
-
addCorner(cr.left, cr.bottom - cl, cr.left, cr.bottom, cr.left + cl, cr.bottom)
|
|
150
|
-
addCorner(cr.right - cl, cr.bottom, cr.right, cr.bottom, cr.right, cr.bottom - cl)
|
|
152
|
+
addCorner(cr.left - hw, cr.top + cl, cr.left - hw, cr.top - hw, cr.left + cl, cr.top - hw)
|
|
153
|
+
addCorner(cr.right - cl, cr.top - hw, cr.right + hw, cr.top - hw, cr.right + hw, cr.top + cl)
|
|
154
|
+
addCorner(cr.left - hw, cr.bottom - cl, cr.left - hw, cr.bottom + hw, cr.left + cl, cr.bottom + hw)
|
|
155
|
+
addCorner(cr.right - cl, cr.bottom + hw, cr.right + hw, cr.bottom + hw, cr.right + hw, cr.bottom - cl)
|
|
156
|
+
|
|
157
|
+
val ehl = edgeHandleLength / 2f
|
|
158
|
+
val cx = cr.centerX()
|
|
159
|
+
val cy = cr.centerY()
|
|
160
|
+
path.moveTo(cx - ehl, cr.top - hw); path.lineTo(cx + ehl, cr.top - hw)
|
|
161
|
+
path.moveTo(cx - ehl, cr.bottom + hw); path.lineTo(cx + ehl, cr.bottom + hw)
|
|
162
|
+
path.moveTo(cr.left - hw, cy - ehl); path.lineTo(cr.left - hw, cy + ehl)
|
|
163
|
+
path.moveTo(cr.right + hw, cy - ehl); path.lineTo(cr.right + hw, cy + ehl)
|
|
164
|
+
|
|
151
165
|
canvas.drawPath(path, cornerPaint)
|
|
152
166
|
}
|
|
153
167
|
|
|
154
|
-
private fun drawDimming(canvas: Canvas, cr: RectF) {
|
|
155
|
-
canvas.save()
|
|
156
|
-
val fullPath = Path()
|
|
157
|
-
fullPath.addRect(0f, 0f, width.toFloat(), height.toFloat(), Path.Direction.CW)
|
|
158
|
-
val cropPath = Path()
|
|
159
|
-
cropPath.addRect(cr, Path.Direction.CW)
|
|
160
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
161
|
-
fullPath.op(cropPath, Path.Op.DIFFERENCE)
|
|
162
|
-
canvas.drawPath(fullPath, dimmingPaint)
|
|
163
|
-
} else {
|
|
164
|
-
@Suppress("DEPRECATION")
|
|
165
|
-
canvas.clipPath(cropPath, Region.Op.DIFFERENCE)
|
|
166
|
-
canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), dimmingPaint)
|
|
167
|
-
}
|
|
168
|
-
canvas.restore()
|
|
169
|
-
}
|
|
170
168
|
|
|
171
169
|
override fun onTouchEvent(event: MotionEvent): Boolean {
|
|
172
170
|
if (event.pointerCount > 1) {
|
|
@@ -181,6 +181,9 @@ class VideoTrimmerView(
|
|
|
181
181
|
|
|
182
182
|
private var trimmerColor = context.getString(R.string.trim_color).toColorInt()
|
|
183
183
|
private var handleIconColor = Color.BLACK
|
|
184
|
+
private var isLightTheme = false
|
|
185
|
+
private val iconColor: Int get() = if (isLightTheme) Color.BLACK else Color.WHITE
|
|
186
|
+
private val dimmedIconColor: Int get() = if (isLightTheme) Color.argb(128, 0, 0, 0) else Color.argb(128, 255, 255, 255)
|
|
184
187
|
private lateinit var leadingChevron: ImageView
|
|
185
188
|
private lateinit var trailingChevron: ImageView
|
|
186
189
|
|
|
@@ -266,10 +269,19 @@ class VideoTrimmerView(
|
|
|
266
269
|
}
|
|
267
270
|
mVideoView.surfaceTextureListener = object : TextureView.SurfaceTextureListener {
|
|
268
271
|
override fun onSurfaceTextureAvailable(st: SurfaceTexture, w: Int, h: Int) {
|
|
269
|
-
|
|
272
|
+
val mp = mediaPlayer
|
|
273
|
+
if (mp != null) {
|
|
274
|
+
videoSurface?.release()
|
|
275
|
+
videoSurface = Surface(st)
|
|
276
|
+
mp.setSurface(videoSurface)
|
|
277
|
+
} else {
|
|
278
|
+
setupVideoPlayer(st, videoURI)
|
|
279
|
+
}
|
|
270
280
|
}
|
|
271
281
|
override fun onSurfaceTextureSizeChanged(st: SurfaceTexture, w: Int, h: Int) {}
|
|
272
|
-
override fun onSurfaceTextureDestroyed(st: SurfaceTexture): Boolean
|
|
282
|
+
override fun onSurfaceTextureDestroyed(st: SurfaceTexture): Boolean {
|
|
283
|
+
return false
|
|
284
|
+
}
|
|
273
285
|
override fun onSurfaceTextureUpdated(st: SurfaceTexture) {}
|
|
274
286
|
}
|
|
275
287
|
} else {
|
|
@@ -320,20 +332,25 @@ class VideoTrimmerView(
|
|
|
320
332
|
if (vw <= 0 || vh <= 0) return
|
|
321
333
|
|
|
322
334
|
videoContainer.post {
|
|
323
|
-
val
|
|
324
|
-
val
|
|
325
|
-
if (
|
|
335
|
+
val cw = containerContentWidth().toInt()
|
|
336
|
+
val ch = containerContentHeight().toInt()
|
|
337
|
+
if (cw <= 0 || ch <= 0) return@post
|
|
338
|
+
|
|
339
|
+
val margin = bracketOverflow()
|
|
340
|
+
val availW = cw - 2 * margin
|
|
341
|
+
val availH = ch - 2 * margin
|
|
342
|
+
if (availW <= 0 || availH <= 0) return@post
|
|
326
343
|
|
|
327
344
|
val videoAR = vw.toFloat() / vh
|
|
328
|
-
val containerAR =
|
|
345
|
+
val containerAR = availW.toFloat() / availH
|
|
329
346
|
val newW: Int
|
|
330
347
|
val newH: Int
|
|
331
348
|
if (videoAR > containerAR) {
|
|
332
|
-
newW =
|
|
333
|
-
newH = (
|
|
349
|
+
newW = availW
|
|
350
|
+
newH = (availW / videoAR).toInt()
|
|
334
351
|
} else {
|
|
335
|
-
newH =
|
|
336
|
-
newW = (
|
|
352
|
+
newH = availH
|
|
353
|
+
newW = (availH * videoAR).toInt()
|
|
337
354
|
}
|
|
338
355
|
mVideoView.layoutParams = FrameLayout.LayoutParams(newW, newH, Gravity.CENTER)
|
|
339
356
|
}
|
|
@@ -519,6 +536,10 @@ class VideoTrimmerView(
|
|
|
519
536
|
cropBtn.setOnClickListener { onCropTapped() }
|
|
520
537
|
undoBtn.setOnClickListener { onUndoTapped() }
|
|
521
538
|
redoBtn.setOnClickListener { onRedoTapped() }
|
|
539
|
+
|
|
540
|
+
cropBtn.setColorFilter(dimmedIconColor, android.graphics.PorterDuff.Mode.SRC_IN)
|
|
541
|
+
undoBtn.setColorFilter(dimmedIconColor, android.graphics.PorterDuff.Mode.SRC_IN)
|
|
542
|
+
redoBtn.setColorFilter(dimmedIconColor, android.graphics.PorterDuff.Mode.SRC_IN)
|
|
522
543
|
}
|
|
523
544
|
|
|
524
545
|
fun onSaveClicked() {
|
|
@@ -633,6 +654,8 @@ class VideoTrimmerView(
|
|
|
633
654
|
mMinDuration = maxOf(1000L, config.getDouble("minDuration").toLong())
|
|
634
655
|
}
|
|
635
656
|
|
|
657
|
+
isLightTheme = config.hasKey("theme") && config.getString("theme") == "light"
|
|
658
|
+
|
|
636
659
|
cancelBtn.text = config.getString("cancelButtonText")
|
|
637
660
|
saveBtn.text = config.getString("saveButtonText")
|
|
638
661
|
isVideoType = config.hasKey("type") && config.getString("type") == "video"
|
|
@@ -673,9 +696,10 @@ class VideoTrimmerView(
|
|
|
673
696
|
trimmerColor = if (config.hasKey("trimmerColor")) config.getInt("trimmerColor") else context.getString(
|
|
674
697
|
R.string.trim_color
|
|
675
698
|
).toColorInt()
|
|
676
|
-
handleIconColor = if (config.hasKey("handleIconColor")) config.getInt("handleIconColor") else Color.BLACK
|
|
699
|
+
handleIconColor = if (config.hasKey("handleIconColor")) config.getInt("handleIconColor") else (if (isLightTheme) Color.WHITE else Color.BLACK)
|
|
677
700
|
|
|
678
701
|
applyTrimmerColor()
|
|
702
|
+
applyThemeColors()
|
|
679
703
|
}
|
|
680
704
|
|
|
681
705
|
private fun applyTrimmerColor() {
|
|
@@ -704,6 +728,39 @@ class VideoTrimmerView(
|
|
|
704
728
|
trailingChevron.setColorFilter(handleIconColor, android.graphics.PorterDuff.Mode.SRC_IN)
|
|
705
729
|
}
|
|
706
730
|
|
|
731
|
+
private fun applyThemeColors() {
|
|
732
|
+
val bgColor = if (isLightTheme) Color.WHITE else Color.BLACK
|
|
733
|
+
val textColor = if (isLightTheme) Color.BLACK else Color.WHITE
|
|
734
|
+
val overlayColor = if (isLightTheme) Color.argb(153, 255, 255, 255) else Color.argb(191, 0, 0, 0)
|
|
735
|
+
|
|
736
|
+
// Backgrounds
|
|
737
|
+
(findViewById<View>(R.id.layout) as? RelativeLayout)?.setBackgroundColor(bgColor)
|
|
738
|
+
headerView.setBackgroundColor(bgColor)
|
|
739
|
+
transformRow.setBackgroundColor(bgColor)
|
|
740
|
+
videoContainer.setBackgroundColor(bgColor)
|
|
741
|
+
|
|
742
|
+
// Text colors
|
|
743
|
+
cancelBtn.setTextColor(iconColor)
|
|
744
|
+
startTimeText.setTextColor(textColor)
|
|
745
|
+
currentTimeText.setTextColor(textColor)
|
|
746
|
+
endTimeText.setTextColor(textColor)
|
|
747
|
+
|
|
748
|
+
// Play icon
|
|
749
|
+
mPlayView.setColorFilter(iconColor, android.graphics.PorterDuff.Mode.SRC_IN)
|
|
750
|
+
|
|
751
|
+
// Transform toolbar icons
|
|
752
|
+
flipBtn.setColorFilter(iconColor, android.graphics.PorterDuff.Mode.SRC_IN)
|
|
753
|
+
rotateBtn.setColorFilter(iconColor, android.graphics.PorterDuff.Mode.SRC_IN)
|
|
754
|
+
cropBtn.setColorFilter(dimmedIconColor, android.graphics.PorterDuff.Mode.SRC_IN)
|
|
755
|
+
undoBtn.setColorFilter(dimmedIconColor, android.graphics.PorterDuff.Mode.SRC_IN)
|
|
756
|
+
redoBtn.setColorFilter(dimmedIconColor, android.graphics.PorterDuff.Mode.SRC_IN)
|
|
757
|
+
|
|
758
|
+
// Overlays
|
|
759
|
+
leadingOverlay.setBackgroundColor(overlayColor)
|
|
760
|
+
trailingOverlay.setBackgroundColor(overlayColor)
|
|
761
|
+
|
|
762
|
+
}
|
|
763
|
+
|
|
707
764
|
private fun dpToPx(dp: Int): Int {
|
|
708
765
|
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp.toFloat(), resources.displayMetrics).toInt()
|
|
709
766
|
}
|
|
@@ -916,9 +973,14 @@ class VideoTrimmerView(
|
|
|
916
973
|
var newX = event.rawX - view.width.toFloat() / 2
|
|
917
974
|
|
|
918
975
|
if (isLeading) {
|
|
976
|
+
val unclamped = newX
|
|
919
977
|
newX = maxOf(0f, minOf(newX, trailingHandle.x - view.width))
|
|
978
|
+
if (unclamped < 0f) didClamp = true
|
|
920
979
|
} else {
|
|
921
|
-
|
|
980
|
+
val unclamped = newX
|
|
981
|
+
val maxX = trimmerContainerBg.width.toFloat() + view.width
|
|
982
|
+
newX = minOf(maxX, maxOf(newX, leadingHandle.x + view.width))
|
|
983
|
+
if (unclamped > maxX) didClamp = true
|
|
922
984
|
}
|
|
923
985
|
|
|
924
986
|
view.x = newX
|
|
@@ -1062,7 +1124,8 @@ class VideoTrimmerView(
|
|
|
1062
1124
|
|
|
1063
1125
|
private fun playHapticFeedback(isLight: Boolean) {
|
|
1064
1126
|
if (vibrator != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && enableHapticFeedback) {
|
|
1065
|
-
|
|
1127
|
+
val amplitude = if (isLight) 30 else 80
|
|
1128
|
+
vibrator!!.vibrate(VibrationEffect.createOneShot(if (isLight) 8L else 15L, amplitude))
|
|
1066
1129
|
}
|
|
1067
1130
|
}
|
|
1068
1131
|
|
|
@@ -1304,14 +1367,28 @@ class VideoTrimmerView(
|
|
|
1304
1367
|
|
|
1305
1368
|
// region Transform
|
|
1306
1369
|
|
|
1370
|
+
private fun containerContentWidth(): Float =
|
|
1371
|
+
(videoContainer.width - videoContainer.paddingLeft - videoContainer.paddingRight).toFloat()
|
|
1372
|
+
|
|
1373
|
+
private fun containerContentHeight(): Float =
|
|
1374
|
+
(videoContainer.height - videoContainer.paddingTop - videoContainer.paddingBottom).toFloat()
|
|
1375
|
+
|
|
1376
|
+
private fun bracketOverflow(): Int =
|
|
1377
|
+
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8f, resources.displayMetrics).toInt()
|
|
1378
|
+
|
|
1307
1379
|
private fun onFlipTapped() {
|
|
1308
1380
|
pushUndo()
|
|
1309
1381
|
isFlipped = !isFlipped
|
|
1310
1382
|
val newCumDeg = -cumulativeRotationDeg
|
|
1311
1383
|
val fitScale = if (rotationCount % 2 != 0) {
|
|
1312
|
-
val cw =
|
|
1313
|
-
val ch =
|
|
1314
|
-
|
|
1384
|
+
val cw = containerContentWidth()
|
|
1385
|
+
val ch = containerContentHeight()
|
|
1386
|
+
val vw = mVideoView.width.toFloat()
|
|
1387
|
+
val vh = mVideoView.height.toFloat()
|
|
1388
|
+
val margin = bracketOverflow().toFloat()
|
|
1389
|
+
val availW = cw - 2 * margin
|
|
1390
|
+
val availH = ch - 2 * margin
|
|
1391
|
+
if (availW > 0 && availH > 0 && vw > 0 && vh > 0) minOf(availW / vh, availH / vw) else 1f
|
|
1315
1392
|
} else {
|
|
1316
1393
|
1f
|
|
1317
1394
|
}
|
|
@@ -1379,17 +1456,29 @@ class VideoTrimmerView(
|
|
|
1379
1456
|
}
|
|
1380
1457
|
|
|
1381
1458
|
private fun updateVideoTransform(resetCrop: Boolean = false) {
|
|
1382
|
-
val
|
|
1383
|
-
val
|
|
1384
|
-
if (
|
|
1459
|
+
val cw = containerContentWidth()
|
|
1460
|
+
val ch = containerContentHeight()
|
|
1461
|
+
if (cw <= 0 || ch <= 0) return
|
|
1385
1462
|
|
|
1386
1463
|
val fitScale = if (rotationCount % 2 != 0) {
|
|
1387
|
-
|
|
1464
|
+
val vw = mVideoView.width.toFloat()
|
|
1465
|
+
val vh = mVideoView.height.toFloat()
|
|
1466
|
+
val margin = bracketOverflow().toFloat()
|
|
1467
|
+
val availW = cw - 2 * margin
|
|
1468
|
+
val availH = ch - 2 * margin
|
|
1469
|
+
if (vw > 0 && vh > 0 && availW > 0 && availH > 0) minOf(availW / vh, availH / vw) else 1f
|
|
1388
1470
|
} else {
|
|
1389
1471
|
1f
|
|
1390
1472
|
}
|
|
1391
1473
|
val flipMul = if (isFlipped) -1f else 1f
|
|
1392
1474
|
|
|
1475
|
+
if (isCropActive) {
|
|
1476
|
+
cropOverlay?.animate()
|
|
1477
|
+
?.alpha(0f)
|
|
1478
|
+
?.setDuration(125)
|
|
1479
|
+
?.start()
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1393
1482
|
mVideoView.animate()
|
|
1394
1483
|
.scaleX(flipMul * fitScale)
|
|
1395
1484
|
.scaleY(fitScale)
|
|
@@ -1400,6 +1489,10 @@ class VideoTrimmerView(
|
|
|
1400
1489
|
if (resetCrop && isCropActive) {
|
|
1401
1490
|
updateCropAllowedRect()
|
|
1402
1491
|
cropOverlay?.resetCrop()
|
|
1492
|
+
cropOverlay?.animate()
|
|
1493
|
+
?.alpha(1f)
|
|
1494
|
+
?.setDuration(125)
|
|
1495
|
+
?.start()
|
|
1403
1496
|
}
|
|
1404
1497
|
}
|
|
1405
1498
|
.start()
|
|
@@ -1412,7 +1505,7 @@ class VideoTrimmerView(
|
|
|
1412
1505
|
private fun onCropTapped() {
|
|
1413
1506
|
isCropActive = !isCropActive
|
|
1414
1507
|
cropBtn.setColorFilter(
|
|
1415
|
-
if (isCropActive)
|
|
1508
|
+
if (isCropActive) iconColor else dimmedIconColor,
|
|
1416
1509
|
android.graphics.PorterDuff.Mode.SRC_IN
|
|
1417
1510
|
)
|
|
1418
1511
|
playHapticFeedback(true)
|
|
@@ -1422,6 +1515,7 @@ class VideoTrimmerView(
|
|
|
1422
1515
|
private fun showCropOverlay() {
|
|
1423
1516
|
hideCropOverlayImmediate()
|
|
1424
1517
|
val overlay = CropOverlayView(mContext)
|
|
1518
|
+
overlay.isLightTheme = isLightTheme
|
|
1425
1519
|
overlay.layoutParams = FrameLayout.LayoutParams(
|
|
1426
1520
|
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
1427
1521
|
FrameLayout.LayoutParams.MATCH_PARENT
|
|
@@ -1457,6 +1551,7 @@ class VideoTrimmerView(
|
|
|
1457
1551
|
private fun showCropOverlayImmediate() {
|
|
1458
1552
|
hideCropOverlayImmediate()
|
|
1459
1553
|
val overlay = CropOverlayView(mContext)
|
|
1554
|
+
overlay.isLightTheme = isLightTheme
|
|
1460
1555
|
overlay.layoutParams = FrameLayout.LayoutParams(
|
|
1461
1556
|
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
1462
1557
|
FrameLayout.LayoutParams.MATCH_PARENT
|
|
@@ -1487,22 +1582,25 @@ class VideoTrimmerView(
|
|
|
1487
1582
|
}
|
|
1488
1583
|
|
|
1489
1584
|
private fun getVideoDisplayRectInContainer(): RectF {
|
|
1490
|
-
val
|
|
1491
|
-
val
|
|
1492
|
-
if (
|
|
1585
|
+
val cw = containerContentWidth()
|
|
1586
|
+
val ch = containerContentHeight()
|
|
1587
|
+
if (cw <= 0 || ch <= 0) return RectF()
|
|
1493
1588
|
|
|
1494
1589
|
val tvW = mVideoView.width.toFloat()
|
|
1495
1590
|
val tvH = mVideoView.height.toFloat()
|
|
1496
1591
|
if (tvW <= 0 || tvH <= 0) return RectF()
|
|
1497
1592
|
|
|
1498
|
-
val tvX = (
|
|
1499
|
-
val tvY = (
|
|
1593
|
+
val tvX = (cw - tvW) / 2f
|
|
1594
|
+
val tvY = (ch - tvH) / 2f
|
|
1500
1595
|
val videoRect = RectF(tvX, tvY, tvX + tvW, tvY + tvH)
|
|
1501
1596
|
|
|
1502
|
-
val pivotX =
|
|
1503
|
-
val pivotY =
|
|
1597
|
+
val pivotX = cw / 2f
|
|
1598
|
+
val pivotY = ch / 2f
|
|
1599
|
+
val margin = bracketOverflow().toFloat()
|
|
1600
|
+
val availW = cw - 2 * margin
|
|
1601
|
+
val availH = ch - 2 * margin
|
|
1504
1602
|
val fitScale = if (rotationCount % 2 != 0) {
|
|
1505
|
-
minOf(
|
|
1603
|
+
if (tvW > 0 && tvH > 0 && availW > 0 && availH > 0) minOf(availW / tvH, availH / tvW) else 1f
|
|
1506
1604
|
} else {
|
|
1507
1605
|
1f
|
|
1508
1606
|
}
|
|
@@ -1603,10 +1701,15 @@ class VideoTrimmerView(
|
|
|
1603
1701
|
isFlipped = snap.isFlipped
|
|
1604
1702
|
cumulativeRotationDeg = snap.cumulativeRotationDeg
|
|
1605
1703
|
|
|
1606
|
-
val
|
|
1607
|
-
val
|
|
1608
|
-
val
|
|
1609
|
-
|
|
1704
|
+
val cw = containerContentWidth()
|
|
1705
|
+
val ch = containerContentHeight()
|
|
1706
|
+
val vw = mVideoView.width.toFloat()
|
|
1707
|
+
val vh = mVideoView.height.toFloat()
|
|
1708
|
+
val margin = bracketOverflow().toFloat()
|
|
1709
|
+
val availW = cw - 2 * margin
|
|
1710
|
+
val availH = ch - 2 * margin
|
|
1711
|
+
val fitScale = if (rotationCount % 2 != 0 && availW > 0 && availH > 0 && vw > 0 && vh > 0) {
|
|
1712
|
+
minOf(availW / vh, availH / vw)
|
|
1610
1713
|
} else {
|
|
1611
1714
|
1f
|
|
1612
1715
|
}
|
|
@@ -1616,17 +1719,14 @@ class VideoTrimmerView(
|
|
|
1616
1719
|
val onComplete = Runnable {
|
|
1617
1720
|
if (snap.isCropActive) {
|
|
1618
1721
|
isCropActive = true
|
|
1619
|
-
cropBtn.setColorFilter(
|
|
1722
|
+
cropBtn.setColorFilter(iconColor, android.graphics.PorterDuff.Mode.SRC_IN)
|
|
1620
1723
|
showCropOverlayImmediate()
|
|
1621
1724
|
updateCropAllowedRect()
|
|
1622
1725
|
val norm = snap.cropNormalized
|
|
1623
1726
|
if (norm != null) setCropFromNormalized(norm) else cropOverlay?.resetCrop()
|
|
1624
1727
|
} else {
|
|
1625
1728
|
isCropActive = false
|
|
1626
|
-
cropBtn.setColorFilter(
|
|
1627
|
-
Color.argb(128, 255, 255, 255),
|
|
1628
|
-
android.graphics.PorterDuff.Mode.SRC_IN
|
|
1629
|
-
)
|
|
1729
|
+
cropBtn.setColorFilter(dimmedIconColor, android.graphics.PorterDuff.Mode.SRC_IN)
|
|
1630
1730
|
hideCropOverlayImmediate()
|
|
1631
1731
|
}
|
|
1632
1732
|
}
|
|
@@ -1680,16 +1780,14 @@ class VideoTrimmerView(
|
|
|
1680
1780
|
}
|
|
1681
1781
|
|
|
1682
1782
|
private fun updateUndoRedoButtons() {
|
|
1683
|
-
val dimmed = Color.argb(128, 255, 255, 255)
|
|
1684
|
-
val active = Color.WHITE
|
|
1685
1783
|
undoBtn.isEnabled = undoStack.isNotEmpty()
|
|
1686
1784
|
undoBtn.setColorFilter(
|
|
1687
|
-
if (undoStack.isNotEmpty())
|
|
1785
|
+
if (undoStack.isNotEmpty()) iconColor else dimmedIconColor,
|
|
1688
1786
|
android.graphics.PorterDuff.Mode.SRC_IN
|
|
1689
1787
|
)
|
|
1690
1788
|
redoBtn.isEnabled = redoStack.isNotEmpty()
|
|
1691
1789
|
redoBtn.setColorFilter(
|
|
1692
|
-
if (redoStack.isNotEmpty())
|
|
1790
|
+
if (redoStack.isNotEmpty()) iconColor else dimmedIconColor,
|
|
1693
1791
|
android.graphics.PorterDuff.Mode.SRC_IN
|
|
1694
1792
|
)
|
|
1695
1793
|
}
|
|
@@ -94,8 +94,8 @@
|
|
|
94
94
|
|
|
95
95
|
<ImageView
|
|
96
96
|
android:id="@+id/undoBtn"
|
|
97
|
-
android:layout_width="
|
|
98
|
-
android:layout_height="
|
|
97
|
+
android:layout_width="18dp"
|
|
98
|
+
android:layout_height="18dp"
|
|
99
99
|
android:src="@drawable/arrow_uturn_backward"
|
|
100
100
|
android:tint="#80FFFFFF"
|
|
101
101
|
android:scaleType="fitCenter"
|
|
@@ -104,8 +104,8 @@
|
|
|
104
104
|
|
|
105
105
|
<ImageView
|
|
106
106
|
android:id="@+id/redoBtn"
|
|
107
|
-
android:layout_width="
|
|
108
|
-
android:layout_height="
|
|
107
|
+
android:layout_width="18dp"
|
|
108
|
+
android:layout_height="18dp"
|
|
109
109
|
android:src="@drawable/arrow_uturn_forward"
|
|
110
110
|
android:tint="#80FFFFFF"
|
|
111
111
|
android:scaleType="fitCenter"
|
|
@@ -120,7 +120,9 @@
|
|
|
120
120
|
android:layout_above="@+id/layout"
|
|
121
121
|
android:layout_below="@+id/transformRow"
|
|
122
122
|
android:background="@android:color/black"
|
|
123
|
-
android:clipChildren="true"
|
|
123
|
+
android:clipChildren="true"
|
|
124
|
+
android:clipToPadding="false"
|
|
125
|
+
android:paddingVertical="4dp">
|
|
124
126
|
|
|
125
127
|
<TextureView
|
|
126
128
|
android:id="@+id/video_loader"
|
|
@@ -6,7 +6,6 @@ class CropOverlayView: UIView {
|
|
|
6
6
|
var cropRect: CGRect = .zero {
|
|
7
7
|
didSet {
|
|
8
8
|
setNeedsDisplay()
|
|
9
|
-
updateDimmingMask()
|
|
10
9
|
}
|
|
11
10
|
}
|
|
12
11
|
|
|
@@ -27,10 +26,15 @@ class CropOverlayView: UIView {
|
|
|
27
26
|
var onCropBegan: (() -> Void)?
|
|
28
27
|
var onCropEnded: (() -> Void)?
|
|
29
28
|
|
|
29
|
+
var isLightTheme = false {
|
|
30
|
+
didSet { setNeedsDisplay() }
|
|
31
|
+
}
|
|
32
|
+
|
|
30
33
|
private let minCropSize: CGFloat = 60
|
|
31
34
|
private let borderWidth: CGFloat = 1.0
|
|
32
35
|
private let cornerLength: CGFloat = 20
|
|
33
36
|
private let cornerWidth: CGFloat = 4.0
|
|
37
|
+
private let edgeHandleLength: CGFloat = 20
|
|
34
38
|
private let gridLineWidth: CGFloat = CGFloat(1.0 / UIScreen.main.scale)
|
|
35
39
|
private let edgeHitZone: CGFloat = 30
|
|
36
40
|
|
|
@@ -38,8 +42,6 @@ class CropOverlayView: UIView {
|
|
|
38
42
|
private var dragStart: CGPoint = .zero
|
|
39
43
|
private var dragStartRect: CGRect = .zero
|
|
40
44
|
|
|
41
|
-
private let dimmingLayer = CAShapeLayer()
|
|
42
|
-
|
|
43
45
|
private enum DragEdge {
|
|
44
46
|
case top, bottom, left, right
|
|
45
47
|
case topLeft, topRight, bottomLeft, bottomRight
|
|
@@ -59,13 +61,9 @@ class CropOverlayView: UIView {
|
|
|
59
61
|
private func commonInit() {
|
|
60
62
|
backgroundColor = .clear
|
|
61
63
|
isUserInteractionEnabled = true
|
|
62
|
-
clipsToBounds =
|
|
64
|
+
clipsToBounds = false
|
|
63
65
|
isOpaque = false
|
|
64
66
|
|
|
65
|
-
dimmingLayer.fillRule = .evenOdd
|
|
66
|
-
dimmingLayer.fillColor = UIColor.black.withAlphaComponent(0.55).cgColor
|
|
67
|
-
layer.addSublayer(dimmingLayer)
|
|
68
|
-
|
|
69
67
|
let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
|
|
70
68
|
addGestureRecognizer(pan)
|
|
71
69
|
|
|
@@ -73,32 +71,18 @@ class CropOverlayView: UIView {
|
|
|
73
71
|
addGestureRecognizer(pinch)
|
|
74
72
|
}
|
|
75
73
|
|
|
76
|
-
override func layoutSubviews() {
|
|
77
|
-
super.layoutSubviews()
|
|
78
|
-
updateDimmingMask()
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
private func updateDimmingMask() {
|
|
82
|
-
let full = UIBezierPath(rect: bounds)
|
|
83
|
-
if !cropRect.isEmpty {
|
|
84
|
-
full.append(UIBezierPath(rect: cropRect))
|
|
85
|
-
}
|
|
86
|
-
dimmingLayer.path = full.cgPath
|
|
87
|
-
dimmingLayer.frame = bounds
|
|
88
|
-
}
|
|
89
|
-
|
|
90
74
|
// MARK: - Drawing
|
|
91
75
|
|
|
92
76
|
override func draw(_ rect: CGRect) {
|
|
93
77
|
guard !cropRect.isEmpty, let ctx = UIGraphicsGetCurrentContext() else { return }
|
|
94
78
|
let cr = cropRect
|
|
95
79
|
|
|
96
|
-
|
|
80
|
+
let strokeColor = (isLightTheme ? UIColor.black : UIColor.white).cgColor
|
|
81
|
+
ctx.setStrokeColor(strokeColor)
|
|
97
82
|
ctx.setLineWidth(borderWidth)
|
|
98
83
|
ctx.stroke(cr)
|
|
99
84
|
|
|
100
85
|
ctx.setLineWidth(gridLineWidth)
|
|
101
|
-
ctx.setStrokeColor(UIColor.white.cgColor)
|
|
102
86
|
for i in 1...2 {
|
|
103
87
|
let x = cr.minX + cr.width * CGFloat(i) / 3
|
|
104
88
|
ctx.move(to: CGPoint(x: x, y: cr.minY))
|
|
@@ -111,30 +95,44 @@ class CropOverlayView: UIView {
|
|
|
111
95
|
}
|
|
112
96
|
ctx.strokePath()
|
|
113
97
|
|
|
114
|
-
ctx.setStrokeColor(
|
|
98
|
+
ctx.setStrokeColor(strokeColor)
|
|
115
99
|
ctx.setLineWidth(cornerWidth)
|
|
116
100
|
ctx.setLineCap(.round)
|
|
101
|
+
ctx.setLineJoin(.round)
|
|
117
102
|
|
|
118
103
|
let cl = cornerLength
|
|
104
|
+
let hw = cornerWidth / 2
|
|
119
105
|
let corners: [(CGPoint, CGPoint, CGPoint)] = [
|
|
120
|
-
(CGPoint(x: cr.minX, y: cr.minY + cl),
|
|
121
|
-
CGPoint(x: cr.minX, y: cr.minY),
|
|
122
|
-
CGPoint(x: cr.minX + cl, y: cr.minY)),
|
|
123
|
-
(CGPoint(x: cr.maxX - cl, y: cr.minY),
|
|
124
|
-
CGPoint(x: cr.maxX, y: cr.minY),
|
|
125
|
-
CGPoint(x: cr.maxX, y: cr.minY + cl)),
|
|
126
|
-
(CGPoint(x: cr.minX, y: cr.maxY - cl),
|
|
127
|
-
CGPoint(x: cr.minX, y: cr.maxY),
|
|
128
|
-
CGPoint(x: cr.minX + cl, y: cr.maxY)),
|
|
129
|
-
(CGPoint(x: cr.maxX - cl, y: cr.maxY),
|
|
130
|
-
CGPoint(x: cr.maxX, y: cr.maxY),
|
|
131
|
-
CGPoint(x: cr.maxX, y: cr.maxY - cl)),
|
|
106
|
+
(CGPoint(x: cr.minX - hw, y: cr.minY + cl),
|
|
107
|
+
CGPoint(x: cr.minX - hw, y: cr.minY - hw),
|
|
108
|
+
CGPoint(x: cr.minX + cl, y: cr.minY - hw)),
|
|
109
|
+
(CGPoint(x: cr.maxX - cl, y: cr.minY - hw),
|
|
110
|
+
CGPoint(x: cr.maxX + hw, y: cr.minY - hw),
|
|
111
|
+
CGPoint(x: cr.maxX + hw, y: cr.minY + cl)),
|
|
112
|
+
(CGPoint(x: cr.minX - hw, y: cr.maxY - cl),
|
|
113
|
+
CGPoint(x: cr.minX - hw, y: cr.maxY + hw),
|
|
114
|
+
CGPoint(x: cr.minX + cl, y: cr.maxY + hw)),
|
|
115
|
+
(CGPoint(x: cr.maxX - cl, y: cr.maxY + hw),
|
|
116
|
+
CGPoint(x: cr.maxX + hw, y: cr.maxY + hw),
|
|
117
|
+
CGPoint(x: cr.maxX + hw, y: cr.maxY - cl)),
|
|
132
118
|
]
|
|
133
119
|
for (start, corner, end) in corners {
|
|
134
120
|
ctx.move(to: start)
|
|
135
121
|
ctx.addLine(to: corner)
|
|
136
122
|
ctx.addLine(to: end)
|
|
137
123
|
}
|
|
124
|
+
|
|
125
|
+
let ehl = edgeHandleLength / 2
|
|
126
|
+
let cx = cr.midX, cy = cr.midY
|
|
127
|
+
ctx.move(to: CGPoint(x: cx - ehl, y: cr.minY - hw))
|
|
128
|
+
ctx.addLine(to: CGPoint(x: cx + ehl, y: cr.minY - hw))
|
|
129
|
+
ctx.move(to: CGPoint(x: cx - ehl, y: cr.maxY + hw))
|
|
130
|
+
ctx.addLine(to: CGPoint(x: cx + ehl, y: cr.maxY + hw))
|
|
131
|
+
ctx.move(to: CGPoint(x: cr.minX - hw, y: cy - ehl))
|
|
132
|
+
ctx.addLine(to: CGPoint(x: cr.minX - hw, y: cy + ehl))
|
|
133
|
+
ctx.move(to: CGPoint(x: cr.maxX + hw, y: cy - ehl))
|
|
134
|
+
ctx.addLine(to: CGPoint(x: cr.maxX + hw, y: cy + ehl))
|
|
135
|
+
|
|
138
136
|
ctx.strokePath()
|
|
139
137
|
}
|
|
140
138
|
|
package/ios/VideoTrim.mm
CHANGED
|
@@ -160,6 +160,11 @@ RCT_EXPORT_MODULE()
|
|
|
160
160
|
dict[@"zoomOnWaitingDuration"] = @(zoomOnWaitingDurationOpt.value());
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
+
NSString *theme = config.theme();
|
|
164
|
+
if (theme != nil) {
|
|
165
|
+
dict[@"theme"] = theme;
|
|
166
|
+
}
|
|
167
|
+
|
|
163
168
|
[self->videoTrim showEditor:filePath withConfig:dict];
|
|
164
169
|
}
|
|
165
170
|
|
package/ios/VideoTrim.swift
CHANGED
|
@@ -13,6 +13,9 @@ public class VideoTrim: RCTEventEmitter, AssetLoaderDelegate, UIDocumentPickerDe
|
|
|
13
13
|
private var vc: VideoTrimmerViewController?
|
|
14
14
|
private var outputFile: URL?
|
|
15
15
|
private var editorConfig: NSDictionary?
|
|
16
|
+
private var isLightTheme: Bool {
|
|
17
|
+
return (editorConfig?["theme"] as? String) == "light"
|
|
18
|
+
}
|
|
16
19
|
|
|
17
20
|
// MARK: base options
|
|
18
21
|
private var saveToPhoto: Bool {
|
|
@@ -289,7 +292,7 @@ public class VideoTrim: RCTEventEmitter, AssetLoaderDelegate, UIDocumentPickerDe
|
|
|
289
292
|
progressAlert.onDismiss = {
|
|
290
293
|
if self.enableCancelTrimmingDialog {
|
|
291
294
|
let dialogMessage = UIAlertController(title: self.cancelTrimmingDialogTitle, message: self.cancelTrimmingDialogMessage, preferredStyle: .alert)
|
|
292
|
-
dialogMessage.overrideUserInterfaceStyle = .dark
|
|
295
|
+
dialogMessage.overrideUserInterfaceStyle = self.isLightTheme ? .light : .dark
|
|
293
296
|
|
|
294
297
|
// Create OK button with action handler
|
|
295
298
|
let ok = UIAlertAction(title: self.cancelTrimmingDialogConfirmText, style: .destructive, handler: { (action) -> Void in
|
|
@@ -879,7 +882,7 @@ extension VideoTrim {
|
|
|
879
882
|
|
|
880
883
|
// Create Alert
|
|
881
884
|
let dialogMessage = UIAlertController(title: self.cancelDialogTitle, message: self.cancelDialogMessage, preferredStyle: .alert)
|
|
882
|
-
dialogMessage.overrideUserInterfaceStyle = .dark
|
|
885
|
+
dialogMessage.overrideUserInterfaceStyle = self.isLightTheme ? .light : .dark
|
|
883
886
|
|
|
884
887
|
// Create OK button with action handler
|
|
885
888
|
let ok = UIAlertAction(title: self.cancelDialogConfirmText, style: .destructive, handler: { (action) -> Void in
|
|
@@ -911,7 +914,7 @@ extension VideoTrim {
|
|
|
911
914
|
|
|
912
915
|
// Create Alert
|
|
913
916
|
let dialogMessage = UIAlertController(title: self.saveDialogTitle, message: self.saveDialogMessage, preferredStyle: .alert)
|
|
914
|
-
dialogMessage.overrideUserInterfaceStyle = .dark
|
|
917
|
+
dialogMessage.overrideUserInterfaceStyle = self.isLightTheme ? .light : .dark
|
|
915
918
|
|
|
916
919
|
// Create OK button with action handler
|
|
917
920
|
let ok = UIAlertAction(title: self.saveDialogConfirmText, style: .default, handler: { (action) -> Void in
|
|
@@ -1134,7 +1137,7 @@ extension VideoTrim {
|
|
|
1134
1137
|
|
|
1135
1138
|
if alertOnFailToLoad {
|
|
1136
1139
|
let dialogMessage = UIAlertController(title: alertOnFailTitle, message: alertOnFailMessage, preferredStyle: .alert)
|
|
1137
|
-
dialogMessage.overrideUserInterfaceStyle = .dark
|
|
1140
|
+
dialogMessage.overrideUserInterfaceStyle = isLightTheme ? .light : .dark
|
|
1138
1141
|
|
|
1139
1142
|
// Create Cancel button with action handlder
|
|
1140
1143
|
let ok = UIAlertAction(title: alertOnFailCloseText, style: .default)
|
package/ios/VideoTrimmer.swift
CHANGED
|
@@ -133,10 +133,10 @@ import AVFoundation
|
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
// the asset to use
|
|
137
136
|
var asset: AVAsset? {
|
|
138
137
|
didSet {
|
|
139
138
|
if let asset = asset {
|
|
139
|
+
applyThemeColors()
|
|
140
140
|
let duration = asset.duration
|
|
141
141
|
range = CMTimeRange(start: .zero, duration: duration)
|
|
142
142
|
selectedRange = range
|
|
@@ -159,6 +159,7 @@ import AVFoundation
|
|
|
159
159
|
var maximumDuration: CMTime = .positiveInfinity
|
|
160
160
|
var enableHapticFeedback = true
|
|
161
161
|
var zoomOnWaitingDuration: Double = 5.0 // Default: 5 seconds
|
|
162
|
+
var isLightTheme = false
|
|
162
163
|
|
|
163
164
|
// the available range of the asset.
|
|
164
165
|
// Will be set to the full duration of the asset when assigning a new asset
|
|
@@ -219,14 +220,12 @@ import AVFoundation
|
|
|
219
220
|
// yes if the user is scrubbing the progress indicator
|
|
220
221
|
private(set) var isScrubbing = false
|
|
221
222
|
|
|
222
|
-
// background color for the track
|
|
223
223
|
var trackBackgroundColor = UIColor.black {
|
|
224
224
|
didSet {
|
|
225
225
|
thumbnailWrapperView.backgroundColor = trackBackgroundColor
|
|
226
226
|
}
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
// background color for the place where the thumbs rest on when the selectedRange == range
|
|
230
229
|
var thumbRestColor = UIColor.black {
|
|
231
230
|
didSet {
|
|
232
231
|
leadingThumbRest.backgroundColor = thumbRestColor
|
|
@@ -276,6 +275,16 @@ import AVFoundation
|
|
|
276
275
|
|
|
277
276
|
|
|
278
277
|
// MARK: - Private
|
|
278
|
+
private func applyThemeColors() {
|
|
279
|
+
let bg: UIColor = isLightTheme ? .white : .black
|
|
280
|
+
let coverAlpha: CGFloat = isLightTheme ? 0.6 : 0.75
|
|
281
|
+
trackBackgroundColor = bg
|
|
282
|
+
thumbRestColor = bg
|
|
283
|
+
thumbnailLeadingCoverView.backgroundColor = UIColor(white: isLightTheme ? 1 : 0, alpha: coverAlpha)
|
|
284
|
+
thumbnailTrailingCoverView.backgroundColor = UIColor(white: isLightTheme ? 1 : 0, alpha: coverAlpha)
|
|
285
|
+
shadowView.layer.shadowColor = (isLightTheme ? UIColor.gray : UIColor.black).cgColor
|
|
286
|
+
}
|
|
287
|
+
|
|
279
288
|
private func setup() {
|
|
280
289
|
addSubview(thumbnailClipView)
|
|
281
290
|
thumbnailClipView.addSubview(thumbnailWrapperView)
|
|
@@ -285,8 +294,6 @@ import AVFoundation
|
|
|
285
294
|
thumbnailWrapperView.addSubview(thumbnailLeadingCoverView)
|
|
286
295
|
thumbnailWrapperView.addSubview(thumbnailTrailingCoverView)
|
|
287
296
|
|
|
288
|
-
progressIndicator.backgroundColor = .white
|
|
289
|
-
progressIndicator.layer.shadowColor = UIColor.black.cgColor
|
|
290
297
|
progressIndicator.layer.shadowOffset = .zero
|
|
291
298
|
progressIndicator.layer.shadowRadius = 2
|
|
292
299
|
progressIndicator.layer.shadowOpacity = 0.25
|
|
@@ -302,13 +309,7 @@ import AVFoundation
|
|
|
302
309
|
|
|
303
310
|
thumbnailClipView.clipsToBounds = true
|
|
304
311
|
thumbnailTrackView.clipsToBounds = true
|
|
305
|
-
thumbnailLeadingCoverView.backgroundColor = UIColor(white: 0, alpha: 0.75)
|
|
306
|
-
thumbnailTrailingCoverView.backgroundColor = UIColor(white: 0, alpha: 0.75)
|
|
307
312
|
|
|
308
|
-
leadingThumbRest.backgroundColor = thumbRestColor
|
|
309
|
-
trailingThumbRest.backgroundColor = thumbRestColor
|
|
310
|
-
|
|
311
|
-
thumbnailWrapperView.backgroundColor = trackBackgroundColor
|
|
312
313
|
thumbnailWrapperView.layer.cornerRadius = 6
|
|
313
314
|
thumbnailWrapperView.layer.cornerCurve = .continuous
|
|
314
315
|
|
|
@@ -320,11 +321,20 @@ import AVFoundation
|
|
|
320
321
|
trailingThumbRest.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMaxXMinYCorner]
|
|
321
322
|
trailingThumbRest.layer.cornerCurve = .continuous
|
|
322
323
|
|
|
323
|
-
shadowView.layer.shadowColor = UIColor.black.cgColor
|
|
324
324
|
shadowView.layer.shadowOffset = .zero
|
|
325
325
|
shadowView.layer.shadowRadius = 2
|
|
326
326
|
shadowView.layer.shadowOpacity = 0.25
|
|
327
327
|
|
|
328
|
+
// Apply default (dark theme) colors — overridden by applyThemeColors() when asset is set
|
|
329
|
+
progressIndicator.backgroundColor = .white
|
|
330
|
+
progressIndicator.layer.shadowColor = UIColor.black.cgColor
|
|
331
|
+
thumbnailLeadingCoverView.backgroundColor = UIColor(white: 0, alpha: 0.75)
|
|
332
|
+
thumbnailTrailingCoverView.backgroundColor = UIColor(white: 0, alpha: 0.75)
|
|
333
|
+
leadingThumbRest.backgroundColor = thumbRestColor
|
|
334
|
+
trailingThumbRest.backgroundColor = thumbRestColor
|
|
335
|
+
thumbnailWrapperView.backgroundColor = trackBackgroundColor
|
|
336
|
+
shadowView.layer.shadowColor = UIColor.black.cgColor
|
|
337
|
+
|
|
328
338
|
setupConstraints()
|
|
329
339
|
setupGestures()
|
|
330
340
|
}
|
|
@@ -49,9 +49,11 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
49
49
|
private var enableHapticFeedback = true
|
|
50
50
|
private var zoomOnWaitingDuration: Double = 5.0 // Default: 5 seconds
|
|
51
51
|
|
|
52
|
-
// New color properties
|
|
53
52
|
private var trimmerColor: UIColor = UIColor.systemYellow
|
|
54
53
|
private var handleIconColor: UIColor = UIColor.black
|
|
54
|
+
private var isLightTheme = false
|
|
55
|
+
private var iconColor: UIColor { isLightTheme ? .black : .white }
|
|
56
|
+
private var dimmedIconColor: UIColor { iconColor.withAlphaComponent(0.5) }
|
|
55
57
|
|
|
56
58
|
private let playerController = AVPlayerViewController()
|
|
57
59
|
private var trimmer: VideoTrimmer!
|
|
@@ -212,6 +214,14 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
212
214
|
setupView()
|
|
213
215
|
setupButtons()
|
|
214
216
|
setupTimeLabels()
|
|
217
|
+
|
|
218
|
+
NotificationCenter.default.addObserver(self, selector: #selector(appWillResignActive), name: UIApplication.willResignActiveNotification, object: nil)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
@objc private func appWillResignActive() {
|
|
222
|
+
guard asset != nil else { return }
|
|
223
|
+
player.pause()
|
|
224
|
+
setPlayBtnIcon()
|
|
215
225
|
}
|
|
216
226
|
|
|
217
227
|
override func viewWillDisappear(_ animated: Bool) {
|
|
@@ -228,8 +238,8 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
228
238
|
player.removeTimeObserver(token)
|
|
229
239
|
timeObserverToken = nil
|
|
230
240
|
}
|
|
231
|
-
// Remove observer
|
|
232
241
|
NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: player.currentItem)
|
|
242
|
+
NotificationCenter.default.removeObserver(self, name: UIApplication.willResignActiveNotification, object: nil)
|
|
233
243
|
|
|
234
244
|
playerController.player = nil
|
|
235
245
|
playerController.dismiss(animated: false, completion: nil)
|
|
@@ -276,8 +286,8 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
276
286
|
|
|
277
287
|
// MARK: - Setup Methods
|
|
278
288
|
private func setupView() {
|
|
279
|
-
self.overrideUserInterfaceStyle = .dark
|
|
280
|
-
view.backgroundColor =
|
|
289
|
+
self.overrideUserInterfaceStyle = isLightTheme ? .light : .dark
|
|
290
|
+
view.backgroundColor = isLightTheme ? .white : .black
|
|
281
291
|
|
|
282
292
|
if let headerText = headerText {
|
|
283
293
|
headerView = UIView()
|
|
@@ -326,8 +336,8 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
326
336
|
}
|
|
327
337
|
|
|
328
338
|
private func setupButtons() {
|
|
329
|
-
cancelBtn = UIButton.createButton(title: cancelButtonText, font: .systemFont(ofSize: 18), titleColor:
|
|
330
|
-
playBtn = UIButton.createButton(image: playIcon, tintColor:
|
|
339
|
+
cancelBtn = UIButton.createButton(title: cancelButtonText, font: .systemFont(ofSize: 18), titleColor: iconColor, target: self, action: #selector(onCancelBtnClicked))
|
|
340
|
+
playBtn = UIButton.createButton(image: playIcon, tintColor: iconColor, target: self, action: #selector(togglePlay(sender:)))
|
|
331
341
|
playBtn.alpha = 0
|
|
332
342
|
playBtn.isEnabled = false
|
|
333
343
|
|
|
@@ -354,11 +364,12 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
354
364
|
}
|
|
355
365
|
|
|
356
366
|
private func setupTimeLabels() {
|
|
357
|
-
|
|
367
|
+
let labelColor = isLightTheme ? UIColor.black : UIColor.white
|
|
368
|
+
leadingTrimLabel = UILabel.createLabel(textAlignment: .left, textColor: labelColor)
|
|
358
369
|
leadingTrimLabel.text = "00:00.000"
|
|
359
|
-
currentTimeLabel = UILabel.createLabel(textAlignment: .center, textColor:
|
|
370
|
+
currentTimeLabel = UILabel.createLabel(textAlignment: .center, textColor: labelColor)
|
|
360
371
|
currentTimeLabel.text = "00:00.000"
|
|
361
|
-
trailingTrimLabel = UILabel.createLabel(textAlignment: .right, textColor:
|
|
372
|
+
trailingTrimLabel = UILabel.createLabel(textAlignment: .right, textColor: labelColor)
|
|
362
373
|
trailingTrimLabel.text = "00:00.000"
|
|
363
374
|
|
|
364
375
|
timingStackView = UIStackView(arrangedSubviews: [leadingTrimLabel, currentTimeLabel, trailingTrimLabel])
|
|
@@ -377,6 +388,7 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
377
388
|
|
|
378
389
|
private func setupVideoTrimmer() {
|
|
379
390
|
trimmer = VideoTrimmer()
|
|
391
|
+
trimmer.isLightTheme = isLightTheme
|
|
380
392
|
trimmer.asset = asset
|
|
381
393
|
trimmer.minimumDuration = CMTime(seconds: 1, preferredTimescale: 600)
|
|
382
394
|
trimmer.enableHapticFeedback = enableHapticFeedback
|
|
@@ -469,11 +481,14 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
469
481
|
addChild(playerController)
|
|
470
482
|
playerContainerView.addSubview(playerController.view)
|
|
471
483
|
playerController.view.translatesAutoresizingMaskIntoConstraints = false
|
|
484
|
+
playerController.view.backgroundColor = .clear
|
|
485
|
+
|
|
486
|
+
let bracketInset: CGFloat = 5
|
|
472
487
|
NSLayoutConstraint.activate([
|
|
473
|
-
playerController.view.
|
|
474
|
-
playerController.view.
|
|
475
|
-
playerController.view.
|
|
476
|
-
playerController.view.
|
|
488
|
+
playerController.view.topAnchor.constraint(equalTo: playerContainerView.topAnchor, constant: bracketInset),
|
|
489
|
+
playerController.view.bottomAnchor.constraint(equalTo: playerContainerView.bottomAnchor, constant: -bracketInset),
|
|
490
|
+
playerController.view.leadingAnchor.constraint(equalTo: playerContainerView.leadingAnchor, constant: bracketInset),
|
|
491
|
+
playerController.view.trailingAnchor.constraint(equalTo: playerContainerView.trailingAnchor, constant: -bracketInset),
|
|
477
492
|
])
|
|
478
493
|
|
|
479
494
|
// Add observer for the end of playback
|
|
@@ -491,34 +506,33 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
491
506
|
guard isVideoType else { return }
|
|
492
507
|
|
|
493
508
|
let symbolConfig = UIImage.SymbolConfiguration(pointSize: 14, weight: .medium)
|
|
494
|
-
let dimmed = UIColor.white.withAlphaComponent(0.5)
|
|
495
509
|
|
|
496
510
|
let flipBtn = UIButton(type: .system)
|
|
497
511
|
flipBtn.setImage(UIImage(systemName: "arrow.trianglehead.left.and.right.righttriangle.left.righttriangle.right", withConfiguration: symbolConfig), for: .normal)
|
|
498
|
-
flipBtn.tintColor =
|
|
512
|
+
flipBtn.tintColor = iconColor
|
|
499
513
|
flipBtn.addTarget(self, action: #selector(onFlipTapped), for: .touchUpInside)
|
|
500
514
|
|
|
501
515
|
let rotateBtn = UIButton(type: .system)
|
|
502
516
|
rotateBtn.setImage(UIImage(systemName: "rotate.left", withConfiguration: symbolConfig), for: .normal)
|
|
503
|
-
rotateBtn.tintColor =
|
|
517
|
+
rotateBtn.tintColor = iconColor
|
|
504
518
|
rotateBtn.addTarget(self, action: #selector(onRotateTapped), for: .touchUpInside)
|
|
505
519
|
|
|
506
520
|
let cropButton = UIButton(type: .system)
|
|
507
521
|
cropButton.setImage(UIImage(systemName: "crop", withConfiguration: symbolConfig), for: .normal)
|
|
508
|
-
cropButton.tintColor =
|
|
522
|
+
cropButton.tintColor = dimmedIconColor
|
|
509
523
|
cropButton.addTarget(self, action: #selector(onCropTapped), for: .touchUpInside)
|
|
510
524
|
self.cropBtn = cropButton
|
|
511
525
|
|
|
512
526
|
let undoButton = UIButton(type: .system)
|
|
513
527
|
undoButton.setImage(UIImage(systemName: "arrow.uturn.backward", withConfiguration: symbolConfig), for: .normal)
|
|
514
|
-
undoButton.tintColor =
|
|
528
|
+
undoButton.tintColor = dimmedIconColor
|
|
515
529
|
undoButton.isEnabled = false
|
|
516
530
|
undoButton.addTarget(self, action: #selector(onUndoTapped), for: .touchUpInside)
|
|
517
531
|
self.undoBtn = undoButton
|
|
518
532
|
|
|
519
533
|
let redoButton = UIButton(type: .system)
|
|
520
534
|
redoButton.setImage(UIImage(systemName: "arrow.uturn.forward", withConfiguration: symbolConfig), for: .normal)
|
|
521
|
-
redoButton.tintColor =
|
|
535
|
+
redoButton.tintColor = dimmedIconColor
|
|
522
536
|
redoButton.isEnabled = false
|
|
523
537
|
redoButton.addTarget(self, action: #selector(onRedoTapped), for: .touchUpInside)
|
|
524
538
|
self.redoBtn = redoButton
|
|
@@ -582,29 +596,63 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
582
596
|
updateVideoTransform(resetCrop: true)
|
|
583
597
|
}
|
|
584
598
|
|
|
585
|
-
private func
|
|
599
|
+
private func buildVideoTransform() -> CGAffineTransform {
|
|
586
600
|
let angle = -CGFloat(rotationCount) * (.pi / 2)
|
|
587
601
|
var transform = CGAffineTransform.identity
|
|
588
|
-
|
|
589
602
|
if isFlipped {
|
|
590
603
|
transform = transform.scaledBy(x: -1, y: 1)
|
|
591
604
|
}
|
|
592
605
|
transform = transform.rotated(by: angle)
|
|
593
|
-
|
|
594
606
|
if rotationCount % 2 != 0 {
|
|
595
|
-
let
|
|
596
|
-
|
|
597
|
-
|
|
607
|
+
let pvBounds = playerController.view.bounds
|
|
608
|
+
let cBounds = playerContainerView.bounds
|
|
609
|
+
if pvBounds.width > 0 && pvBounds.height > 0 && cBounds.width > 0 && cBounds.height > 0 {
|
|
610
|
+
var videoW = pvBounds.width
|
|
611
|
+
var videoH = pvBounds.height
|
|
612
|
+
if let track = asset?.tracks(withMediaType: .video).first {
|
|
613
|
+
let raw = track.naturalSize
|
|
614
|
+
let pt = track.preferredTransform
|
|
615
|
+
let a = atan2(pt.b, pt.a)
|
|
616
|
+
let srcRotated = abs(a - .pi / 2) < 0.1 || abs(a + .pi / 2) < 0.1
|
|
617
|
+
let ds = srcRotated ? CGSize(width: raw.height, height: raw.width) : raw
|
|
618
|
+
if ds.width > 0 && ds.height > 0 {
|
|
619
|
+
let videoAR = ds.width / ds.height
|
|
620
|
+
let viewAR = pvBounds.width / pvBounds.height
|
|
621
|
+
if videoAR > viewAR {
|
|
622
|
+
videoW = pvBounds.width
|
|
623
|
+
videoH = pvBounds.width / videoAR
|
|
624
|
+
} else {
|
|
625
|
+
videoH = pvBounds.height
|
|
626
|
+
videoW = pvBounds.height * videoAR
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
let bracketMargin: CGFloat = 5
|
|
631
|
+
let availW = cBounds.width - 2 * bracketMargin
|
|
632
|
+
let availH = cBounds.height - 2 * bracketMargin
|
|
633
|
+
let fitScale = min(availW / videoH, availH / videoW)
|
|
598
634
|
transform = transform.scaledBy(x: fitScale, y: fitScale)
|
|
599
635
|
}
|
|
600
636
|
}
|
|
601
|
-
|
|
637
|
+
return transform
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
private func updateVideoTransform(resetCrop: Bool = false) {
|
|
641
|
+
let transform = buildVideoTransform()
|
|
642
|
+
if isCropActive {
|
|
643
|
+
UIView.animate(withDuration: 0.15) {
|
|
644
|
+
self.cropOverlayView?.alpha = 0
|
|
645
|
+
}
|
|
646
|
+
}
|
|
602
647
|
UIView.animate(withDuration: 0.3, delay: 0, options: [.curveEaseInOut]) {
|
|
603
648
|
self.playerController.view.transform = transform
|
|
604
649
|
} completion: { _ in
|
|
605
650
|
if resetCrop && self.isCropActive {
|
|
606
651
|
self.updateCropAllowedRect()
|
|
607
652
|
self.cropOverlayView?.resetCrop()
|
|
653
|
+
UIView.animate(withDuration: 0.15) {
|
|
654
|
+
self.cropOverlayView?.alpha = 1
|
|
655
|
+
}
|
|
608
656
|
}
|
|
609
657
|
}
|
|
610
658
|
}
|
|
@@ -644,21 +692,11 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
644
692
|
rotationCount = snap.rotationCount
|
|
645
693
|
isFlipped = snap.isFlipped
|
|
646
694
|
|
|
647
|
-
let
|
|
648
|
-
var transform = CGAffineTransform.identity
|
|
649
|
-
if isFlipped { transform = transform.scaledBy(x: -1, y: 1) }
|
|
650
|
-
transform = transform.rotated(by: angle)
|
|
651
|
-
if rotationCount % 2 != 0 {
|
|
652
|
-
let bounds = playerContainerView.bounds
|
|
653
|
-
if bounds.width > 0 && bounds.height > 0 {
|
|
654
|
-
let fitScale = min(bounds.width / bounds.height, bounds.height / bounds.width)
|
|
655
|
-
transform = transform.scaledBy(x: fitScale, y: fitScale)
|
|
656
|
-
}
|
|
657
|
-
}
|
|
695
|
+
let transform = buildVideoTransform()
|
|
658
696
|
|
|
659
697
|
let wasActive = isCropActive
|
|
660
698
|
isCropActive = snap.isCropActive
|
|
661
|
-
cropBtn?.tintColor = isCropActive ?
|
|
699
|
+
cropBtn?.tintColor = isCropActive ? iconColor : dimmedIconColor
|
|
662
700
|
|
|
663
701
|
UIView.animate(withDuration: 0.3, delay: 0, options: [.curveEaseInOut]) {
|
|
664
702
|
self.playerController.view.transform = transform
|
|
@@ -690,10 +728,9 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
690
728
|
}
|
|
691
729
|
|
|
692
730
|
private func updateUndoRedoButtons() {
|
|
693
|
-
|
|
694
|
-
undoBtn?.tintColor = undoStack.isEmpty ? dimmed : .white
|
|
731
|
+
undoBtn?.tintColor = undoStack.isEmpty ? dimmedIconColor : iconColor
|
|
695
732
|
undoBtn?.isEnabled = !undoStack.isEmpty
|
|
696
|
-
redoBtn?.tintColor = redoStack.isEmpty ?
|
|
733
|
+
redoBtn?.tintColor = redoStack.isEmpty ? dimmedIconColor : iconColor
|
|
697
734
|
redoBtn?.isEnabled = !redoStack.isEmpty
|
|
698
735
|
}
|
|
699
736
|
|
|
@@ -701,7 +738,7 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
701
738
|
|
|
702
739
|
@objc private func onCropTapped() {
|
|
703
740
|
isCropActive.toggle()
|
|
704
|
-
cropBtn?.tintColor = isCropActive ?
|
|
741
|
+
cropBtn?.tintColor = isCropActive ? iconColor : dimmedIconColor
|
|
705
742
|
|
|
706
743
|
if enableHapticFeedback {
|
|
707
744
|
UIImpactFeedbackGenerator(style: .light).impactOccurred()
|
|
@@ -742,6 +779,7 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
742
779
|
|
|
743
780
|
private func createCropOverlay() {
|
|
744
781
|
let overlay = CropOverlayView()
|
|
782
|
+
overlay.isLightTheme = isLightTheme
|
|
745
783
|
overlay.translatesAutoresizingMaskIntoConstraints = false
|
|
746
784
|
overlay.alpha = 0
|
|
747
785
|
playerContainerView.addSubview(overlay)
|
|
@@ -915,6 +953,8 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
915
953
|
minimumDuration = minDuration
|
|
916
954
|
}
|
|
917
955
|
|
|
956
|
+
isLightTheme = (config["theme"] as? String) == "light"
|
|
957
|
+
|
|
918
958
|
cancelButtonText = config["cancelButtonText"] as? String ?? "Cancel"
|
|
919
959
|
saveButtonText = config["saveButtonText"] as? String ?? "Save"
|
|
920
960
|
jumpToPositionOnLoad = config["jumpToPositionOnLoad"] as? Double ?? 0
|
|
@@ -926,12 +966,11 @@ class VideoTrimmerViewController: UIViewController {
|
|
|
926
966
|
headerTextSize = config["headerTextSize"] as? Int ?? 16
|
|
927
967
|
headerTextColor = config["headerTextColor"] as? Double
|
|
928
968
|
|
|
929
|
-
// Handle new color properties
|
|
930
969
|
if let trimmerColorValue = config["trimmerColor"] as? Double {
|
|
931
970
|
trimmerColor = RCTConvert.uiColor(trimmerColorValue) ?? UIColor.systemYellow
|
|
932
971
|
}
|
|
933
972
|
if let handleIconColorValue = config["handleIconColor"] as? Double {
|
|
934
|
-
handleIconColor = RCTConvert.uiColor(handleIconColorValue) ??
|
|
973
|
+
handleIconColor = RCTConvert.uiColor(handleIconColorValue) ?? (isLightTheme ? .white : .black)
|
|
935
974
|
}
|
|
936
975
|
}
|
|
937
976
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeVideoTrim.ts"],"mappings":";;AACA,SAASA,mBAAmB,QAAQ,cAAc;;AAGlD;AACA;AACA;;AAwBA;AACA;AACA;;
|
|
1
|
+
{"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeVideoTrim.ts"],"mappings":";;AACA,SAASA,mBAAmB,QAAQ,cAAc;;AAGlD;AACA;AACA;;AAwBA;AACA;AACA;;AAqGA;AACA;AACA;;AAQA;AACA;AACA;;AAUA;AACA;AACA;;AAcA;AACA;AACA;;AAmEA,eAAeA,mBAAmB,CAACC,YAAY,CAAO,WAAW,CAAC","ignoreList":[]}
|
package/lib/module/index.js
CHANGED
|
@@ -90,9 +90,10 @@ export function showEditor(filePath, config) {
|
|
|
90
90
|
trimmerColor,
|
|
91
91
|
handleIconColor
|
|
92
92
|
} = config;
|
|
93
|
-
const
|
|
93
|
+
const isLight = config.theme === 'light';
|
|
94
|
+
const _headerTextColor = processColor(headerTextColor || (isLight ? 'black' : 'white'));
|
|
94
95
|
const _trimmerColor = processColor(trimmerColor || '#f1d247');
|
|
95
|
-
const _handleIconColor = processColor(handleIconColor || 'black');
|
|
96
|
+
const _handleIconColor = processColor(handleIconColor || (isLight ? 'white' : 'black'));
|
|
96
97
|
VideoTrim.showEditor(filePath, createEditorConfig({
|
|
97
98
|
...config,
|
|
98
99
|
headerTextColor: _headerTextColor,
|
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","removeAfterSavedToPhoto","removeAfterFailedToSavePhoto","enablePreciseTrimming","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","zoomOnWaitingDuration","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":";;AAAA,OAAOA,gBAAgB,MAAM,sBAAmB;AAChD,OAAOC,gBAAgB,MAAM,cAAW;AAQxC,SAASC,YAAY,QAAQ,cAAc;;AAE3C;AACA,MAAMC,QAAQ,GAAG,CAAC,CAAEC,MAAM,CAASC,qBAAqB;AACxD,MAAMC,SAAS,GAAGH,QAAQ,GAAGH,gBAAgB,GAAGC,gBAAgB;AAEhE,SAASM,iBAAiBA,CAACC,SAA+B,GAAG,CAAC,CAAC,EAAe;EAC5E,OAAO;IACLC,WAAW,EAAE,KAAK;IAClBC,IAAI,EAAE,OAAO;IACbC,SAAS,EAAE,KAAK;IAChBC,uBAAuB,EAAE,KAAK;IAC9BC,4BAA4B,EAAE,KAAK;IACnCC,qBAAqB,EAAE,KAAK;IAC5B,GAAGN;EACL,CAAC;AACH;AAEA,SAASO,kBAAkBA,CACzBP,SAAgC,GAAG,CAAC,CAAC,EACvB;EACd,OAAO;IACLQ,oBAAoB,EAAE,IAAI;IAC1BC,WAAW,EAAE,CAAC,CAAC;IACfC,WAAW,EAAE,CAAC,CAAC;IACfC,qBAAqB,EAAE,KAAK;IAC5BC,sBAAsB,EAAE,KAAK;IAC7BC,2BAA2B,EAAE,KAAK;IAClCC,gCAAgC,EAAE,KAAK;IACvCC,iBAAiB,EAAE,KAAK;IACxBC,wBAAwB,EAAE,KAAK;IAC/BC,gBAAgB,EAAE,QAAQ;IAC1BC,cAAc,EAAE,MAAM;IACtBC,kBAAkB,EAAE,IAAI;IACxBC,iBAAiB,EAAE,UAAU;IAC7BC,mBAAmB,EAAE,8BAA8B;IACnDC,sBAAsB,EAAE,OAAO;IAC/BC,uBAAuB,EAAE,SAAS;IAClCC,gBAAgB,EAAE,IAAI;IACtBC,eAAe,EAAE,eAAe;IAChCC,iBAAiB,EAAE,4BAA4B;IAC/CC,oBAAoB,EAAE,OAAO;IAC7BC,qBAAqB,EAAE,SAAS;IAChCC,YAAY,EAAE,mBAAmB;IACjCC,kBAAkB,EAAE,KAAK;IACzBC,QAAQ,EAAE,KAAK;IACfC,oBAAoB,EAAE,CAAC,CAAC;IACxBC,eAAe,EAAE,IAAI;IACrBC,oBAAoB,EAAE,IAAI;IAC1BC,wBAAwB,EAAE,QAAQ;IAClCC,0BAA0B,EAAE,IAAI;IAChCC,yBAAyB,EAAE,UAAU;IACrCC,2BAA2B,EAAE,uCAAuC;IACpEC,8BAA8B,EAAE,OAAO;IACvCC,+BAA+B,EAAE,SAAS;IAC1CC,UAAU,EAAE,EAAE;IACdC,cAAc,EAAE,EAAE;IAClBC,eAAe,EAAEjD,YAAY,CAAC,OAAO,CAAW;IAChDkD,YAAY,EAAElD,YAAY,CAAC,SAAS,CAAW;IAC/CmD,eAAe,EAAEnD,YAAY,CAAC,OAAO,CAAW;IAChDoD,qBAAqB,EAAE,IAAI;IAC3BC,iBAAiB,EAAE,IAAI;IACvBC,gBAAgB,EAAE,OAAO;IACzBC,kBAAkB,EAChB,oEAAoE;IACtEC,oBAAoB,EAAE,OAAO;IAC7B,GAAGnD,iBAAiB,CAACC,SAAS,CAAC;IAC/B,GAAGA;EACL,CAAC;AACH;AAEA,SAASmD,iBAAiBA,CAACnD,SAA+B,GAAG,CAAC,CAAC,EAAe;EAC5E,OAAO;IACLoD,SAAS,EAAE,CAAC;IACZC,OAAO,EAAE,IAAI;IACb,GAAGtD,iBAAiB,CAACC,SAAS,CAAC;IAC/B,GAAGA;EACL,CAAC;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASsD,UAAUA,CACxBC,QAAgB,EAChBC,MAMC,EACK;EACN,MAAM;IAAEb,eAAe;IAAEC,YAAY;IAAEC;EAAgB,CAAC,GAAGW,MAAM;EACjE,MAAMC,gBAAgB,
|
|
1
|
+
{"version":3,"names":["VideoTrimNewArch","VideoTrimOldArch","processColor","isFabric","global","nativeFabricUIManager","VideoTrim","createBaseOptions","overrides","saveToPhoto","type","outputExt","removeAfterSavedToPhoto","removeAfterFailedToSavePhoto","enablePreciseTrimming","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","zoomOnWaitingDuration","alertOnFailToLoad","alertOnFailTitle","alertOnFailMessage","alertOnFailCloseText","createTrimOptions","startTime","endTime","showEditor","filePath","config","isLight","theme","_headerTextColor","_trimmerColor","_handleIconColor","listFiles","cleanFiles","deleteFile","trim","length","Error","closeEditor","isValidFile","url","options"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -121,6 +121,11 @@ export interface EditorConfig extends BaseOptions {
|
|
|
121
121
|
* this duration around the current trim position for more precise editing.
|
|
122
122
|
*/
|
|
123
123
|
zoomOnWaitingDuration?: number;
|
|
124
|
+
/**
|
|
125
|
+
* Editor theme. `"dark"` (default) uses a black background with white UI elements.
|
|
126
|
+
* `"light"` uses a white background with black icons/text and white trimmer-handle chevrons.
|
|
127
|
+
*/
|
|
128
|
+
theme?: string;
|
|
124
129
|
}
|
|
125
130
|
/**
|
|
126
131
|
* Options for headless (non-UI) trim operations.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativeVideoTrim.d.ts","sourceRoot":"","sources":["../../../src/NativeVideoTrim.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"NativeVideoTrim.d.ts","sourceRoot":"","sources":["../../../src/NativeVideoTrim.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEV,YAAY,EACZ,oBAAoB,EACpB,WAAW,EACX,UAAU,EACX,MAAM,mBAAmB,CAAC;AAK3B,QAAA,MAAM,SAAS,KAAiD,CAAC;AA4EjE;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,OAAO,CACb,IAAI,CAAC,YAAY,EAAE,iBAAiB,GAAG,cAAc,GAAG,iBAAiB,CAAC,CAC3E,GAAG;IACF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,GACA,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEV,YAAY,EACZ,oBAAoB,EACpB,WAAW,EACX,UAAU,EACX,MAAM,mBAAmB,CAAC;AAK3B,QAAA,MAAM,SAAS,KAAiD,CAAC;AA4EjE;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,OAAO,CACb,IAAI,CAAC,YAAY,EAAE,iBAAiB,GAAG,cAAc,GAAG,iBAAiB,CAAC,CAC3E,GAAG;IACF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,GACA,IAAI,CAoBN;AAED;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAE7C;AAED;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAE5C;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAK7D;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAElC;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAEtE;AAED;;;;;;GAMG;AACH,wBAAgB,IAAI,CAClB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,GAC5B,OAAO,CAAC,UAAU,CAAC,CAErB;AAED,cAAc,mBAAmB,CAAC;AAClC,eAAe,SAAS,CAAC"}
|
package/package.json
CHANGED
package/src/NativeVideoTrim.ts
CHANGED
|
@@ -124,6 +124,11 @@ export interface EditorConfig extends BaseOptions {
|
|
|
124
124
|
* this duration around the current trim position for more precise editing.
|
|
125
125
|
*/
|
|
126
126
|
zoomOnWaitingDuration?: number;
|
|
127
|
+
/**
|
|
128
|
+
* Editor theme. `"dark"` (default) uses a black background with white UI elements.
|
|
129
|
+
* `"light"` uses a white background with black icons/text and white trimmer-handle chevrons.
|
|
130
|
+
*/
|
|
131
|
+
theme?: string;
|
|
127
132
|
}
|
|
128
133
|
|
|
129
134
|
/**
|
package/src/index.tsx
CHANGED
|
@@ -106,9 +106,14 @@ export function showEditor(
|
|
|
106
106
|
}
|
|
107
107
|
): void {
|
|
108
108
|
const { headerTextColor, trimmerColor, handleIconColor } = config;
|
|
109
|
-
const
|
|
109
|
+
const isLight = config.theme === 'light';
|
|
110
|
+
const _headerTextColor = processColor(
|
|
111
|
+
headerTextColor || (isLight ? 'black' : 'white')
|
|
112
|
+
);
|
|
110
113
|
const _trimmerColor = processColor(trimmerColor || '#f1d247');
|
|
111
|
-
const _handleIconColor = processColor(
|
|
114
|
+
const _handleIconColor = processColor(
|
|
115
|
+
handleIconColor || (isLight ? 'white' : 'black')
|
|
116
|
+
);
|
|
112
117
|
|
|
113
118
|
VideoTrim.showEditor(
|
|
114
119
|
filePath,
|