react-native-video-trim 6.2.2 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -9
- package/android/src/main/java/com/videotrim/BaseVideoTrimModule.kt +146 -119
- package/android/src/main/java/com/videotrim/enums/ErrorCode.kt +2 -1
- package/android/src/main/java/com/videotrim/utils/MediaMetadataUtil.kt +6 -2
- package/android/src/main/java/com/videotrim/utils/StorageUtil.kt +5 -1
- package/android/src/main/java/com/videotrim/utils/VideoTrimmerUtil.kt +99 -26
- package/android/src/main/java/com/videotrim/widgets/CropOverlayView.kt +293 -0
- package/android/src/main/java/com/videotrim/widgets/VideoTrimmerView.kt +556 -28
- package/android/src/main/res/drawable/arrow_trianglehead_left_and_right_righttriangle_left_righttriangle_right.xml +19 -0
- package/android/src/main/res/drawable/arrow_uturn_backward.xml +15 -0
- package/android/src/main/res/drawable/arrow_uturn_forward.xml +15 -0
- package/android/src/main/res/drawable/crop.xml +15 -0
- package/android/src/main/res/drawable/rotate_left.xml +19 -0
- package/android/src/main/res/layout/video_trimmer_view.xml +115 -37
- package/android/src/main/res/xml/file_paths.xml +1 -1
- package/ios/CropOverlayView.swift +285 -0
- package/ios/VideoTrim.mm +2 -4
- package/ios/VideoTrim.swift +198 -61
- package/ios/VideoTrimmer.swift +2 -4
- package/ios/VideoTrimmerViewController.swift +478 -56
- package/lib/module/NativeVideoTrim.js.map +1 -1
- package/lib/module/index.js +1 -2
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/NativeVideoTrim.d.ts +10 -4
- 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 +10 -4
- package/src/index.tsx +1 -2
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
2
|
+
android:width="23.232dp"
|
|
3
|
+
android:height="24.688dp"
|
|
4
|
+
android:viewportWidth="23.232"
|
|
5
|
+
android:viewportHeight="24.688">
|
|
6
|
+
<path
|
|
7
|
+
android:fillColor="#FF000000"
|
|
8
|
+
android:pathData="M0,0h23.232v24.688h-23.232z"
|
|
9
|
+
android:strokeAlpha="0"
|
|
10
|
+
android:fillAlpha="0"/>
|
|
11
|
+
<path
|
|
12
|
+
android:pathData="M1.475,21.236L9.189,21.236C10.342,21.236 10.869,20.718 10.869,19.556L10.869,7.886C10.869,7.027 10.234,6.568 9.541,6.568C9.072,6.568 8.613,6.782 8.301,7.232L0.303,18.911C0.088,19.224 0,19.585 0,19.917C0,20.63 0.469,21.236 1.475,21.236ZM2.09,19.644C1.963,19.644 1.943,19.536 2.002,19.448L9.102,8.706C9.16,8.618 9.268,8.648 9.268,8.755L9.268,19.361C9.268,19.585 9.209,19.644 8.975,19.644ZM21.396,21.236C22.412,21.236 22.871,20.63 22.871,19.917C22.871,19.585 22.783,19.224 22.568,18.911L14.58,7.232C14.258,6.782 13.809,6.568 13.34,6.568C12.646,6.568 12.012,7.027 12.012,7.886L12.012,19.556C12.012,20.718 12.529,21.236 13.691,21.236ZM20.781,19.644L13.896,19.644C13.672,19.644 13.613,19.585 13.613,19.361L13.613,8.755C13.613,8.648 13.721,8.618 13.769,8.706L20.869,19.448C20.938,19.536 20.918,19.644 20.781,19.644Z"
|
|
13
|
+
android:fillColor="#ffffff"
|
|
14
|
+
android:fillAlpha="0.85"/>
|
|
15
|
+
<path
|
|
16
|
+
android:pathData="M17.373,0.708L17.373,4.614C17.373,5.298 18.027,5.542 18.565,5.132L21.182,3.179C21.533,2.905 21.523,2.398 21.182,2.144L18.565,0.2C18.027,-0.21 17.373,0.034 17.373,0.708ZM5.508,4.614L5.508,0.708C5.508,0.034 4.854,-0.21 4.316,0.2L1.699,2.144C1.338,2.407 1.338,2.905 1.699,3.179L4.316,5.132C4.834,5.523 5.508,5.318 5.508,4.614ZM18.691,3.237C18.994,3.237 19.258,2.984 19.258,2.661C19.258,2.359 18.994,2.105 18.691,2.105L4.18,2.105C3.867,2.105 3.613,2.359 3.613,2.661C3.613,2.984 3.867,3.237 4.18,3.237Z"
|
|
17
|
+
android:fillColor="#ffffff"
|
|
18
|
+
android:fillAlpha="0.85"/>
|
|
19
|
+
</vector>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
2
|
+
android:width="18.818dp"
|
|
3
|
+
android:height="18.232dp"
|
|
4
|
+
android:viewportWidth="18.818"
|
|
5
|
+
android:viewportHeight="18.232">
|
|
6
|
+
<path
|
|
7
|
+
android:fillColor="#FF000000"
|
|
8
|
+
android:pathData="M0,0h18.818v18.232h-18.818z"
|
|
9
|
+
android:strokeAlpha="0"
|
|
10
|
+
android:fillAlpha="0"/>
|
|
11
|
+
<path
|
|
12
|
+
android:pathData="M0,6.572C0,6.807 0.098,7.031 0.293,7.217L6.035,12.852C6.201,13.027 6.455,13.125 6.66,13.125C7.188,13.125 7.529,12.773 7.529,12.266C7.529,12.012 7.441,11.826 7.295,11.67L4.473,8.926L1.719,6.572L4.473,4.209L7.295,1.465C7.441,1.309 7.529,1.123 7.529,0.869C7.529,0.361 7.188,0.01 6.66,0.01C6.455,0.01 6.201,0.107 6.035,0.283L0.293,5.918C0.098,6.104 0,6.328 0,6.572ZM8.486,17.363C8.486,17.842 8.838,18.232 9.375,18.232L11.631,18.232C15.996,18.232 18.457,15.742 18.457,12.002C18.457,8.271 15.928,5.693 11.475,5.693L4.912,5.693L1.68,5.84C1.279,5.859 0.957,6.162 0.957,6.572C0.957,6.973 1.279,7.275 1.68,7.295L4.912,7.441L11.621,7.441C14.951,7.441 16.709,9.277 16.709,11.904C16.709,14.541 14.951,16.494 11.621,16.494L9.375,16.494C8.838,16.494 8.486,16.885 8.486,17.363Z"
|
|
13
|
+
android:fillColor="#ffffff"
|
|
14
|
+
android:fillAlpha="0.85"/>
|
|
15
|
+
</vector>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
2
|
+
android:width="18.818dp"
|
|
3
|
+
android:height="18.232dp"
|
|
4
|
+
android:viewportWidth="18.818"
|
|
5
|
+
android:viewportHeight="18.232">
|
|
6
|
+
<path
|
|
7
|
+
android:fillColor="#FF000000"
|
|
8
|
+
android:pathData="M0,0h18.818v18.232h-18.818z"
|
|
9
|
+
android:strokeAlpha="0"
|
|
10
|
+
android:fillAlpha="0"/>
|
|
11
|
+
<path
|
|
12
|
+
android:pathData="M18.457,6.572C18.457,6.328 18.359,6.104 18.164,5.918L12.422,0.283C12.256,0.107 12.002,0.01 11.787,0.01C11.269,0.01 10.928,0.361 10.928,0.869C10.928,1.123 11.006,1.309 11.162,1.465L13.975,4.209L16.738,6.572L13.975,8.926L11.162,11.67C11.006,11.826 10.928,12.012 10.928,12.266C10.928,12.773 11.269,13.125 11.787,13.125C12.002,13.125 12.256,13.027 12.422,12.852L18.164,7.217C18.359,7.031 18.457,6.807 18.457,6.572ZM9.971,17.363C9.971,16.885 9.619,16.494 9.082,16.494L6.836,16.494C3.506,16.494 1.738,14.541 1.738,11.904C1.738,9.277 3.506,7.441 6.836,7.441L13.535,7.441L16.768,7.295C17.178,7.275 17.5,6.973 17.5,6.572C17.5,6.162 17.178,5.859 16.768,5.84L13.535,5.693L6.982,5.693C2.52,5.693 0,8.271 0,12.002C0,15.742 2.451,18.232 6.826,18.232L9.082,18.232C9.619,18.232 9.971,17.842 9.971,17.363Z"
|
|
13
|
+
android:fillColor="#ffffff"
|
|
14
|
+
android:fillAlpha="0.85"/>
|
|
15
|
+
</vector>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
2
|
+
android:width="21.777dp"
|
|
3
|
+
android:height="21.982dp"
|
|
4
|
+
android:viewportWidth="21.777"
|
|
5
|
+
android:viewportHeight="21.982">
|
|
6
|
+
<path
|
|
7
|
+
android:fillColor="#FF000000"
|
|
8
|
+
android:pathData="M0,0h21.777v21.982h-21.777z"
|
|
9
|
+
android:strokeAlpha="0"
|
|
10
|
+
android:fillAlpha="0"/>
|
|
11
|
+
<path
|
|
12
|
+
android:pathData="M21.416,16.553C21.416,16.094 21.113,15.811 20.635,15.811L6.553,15.811C6.338,15.811 6.27,15.742 6.27,15.527L6.27,1.445C6.27,0.957 5.957,0.625 5.479,0.625C5.01,0.625 4.697,0.957 4.697,1.445L4.697,16.514C4.697,16.982 5,17.285 5.469,17.285L20.635,17.285C21.113,17.285 21.416,17.002 21.416,16.553ZM0,6.055C0,6.523 0.303,6.797 0.771,6.797L14.863,6.797C15.078,6.797 15.146,6.875 15.146,7.08L15.146,21.162C15.146,21.65 15.459,21.982 15.928,21.982C16.406,21.982 16.719,21.65 16.719,21.162L16.719,6.104C16.719,5.635 16.416,5.322 15.947,5.322L0.771,5.322C0.303,5.322 0,5.605 0,6.055Z"
|
|
13
|
+
android:fillColor="#ffffff"
|
|
14
|
+
android:fillAlpha="0.85"/>
|
|
15
|
+
</vector>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
2
|
+
android:width="24.082dp"
|
|
3
|
+
android:height="24.727dp"
|
|
4
|
+
android:viewportWidth="24.082"
|
|
5
|
+
android:viewportHeight="24.727">
|
|
6
|
+
<path
|
|
7
|
+
android:fillColor="#FF000000"
|
|
8
|
+
android:pathData="M0,0h24.082v24.727h-24.082z"
|
|
9
|
+
android:strokeAlpha="0"
|
|
10
|
+
android:fillAlpha="0"/>
|
|
11
|
+
<path
|
|
12
|
+
android:pathData="M7.021,21.353L16.699,21.353C18.369,21.353 19.209,20.562 19.209,18.853L19.209,9.194C19.209,7.485 18.369,6.685 16.699,6.685L7.021,6.685C5.352,6.685 4.512,7.485 4.512,9.194L4.512,18.853C4.512,20.562 5.352,21.353 7.021,21.353ZM7.051,19.78C6.387,19.78 6.084,19.507 6.084,18.823L6.084,9.214C6.084,8.53 6.387,8.257 7.051,8.257L16.67,8.257C17.344,8.257 17.637,8.53 17.637,9.214L17.637,18.823C17.637,19.507 17.344,19.78 16.67,19.78Z"
|
|
13
|
+
android:fillColor="#ffffff"
|
|
14
|
+
android:fillAlpha="0.85"/>
|
|
15
|
+
<path
|
|
16
|
+
android:pathData="M19.209,4.312L19.209,0.698C19.209,-0.024 18.662,-0.19 18.115,0.22L15.654,2.027C15.244,2.329 15.234,2.681 15.654,2.984L18.115,4.78C18.662,5.181 19.209,5.015 19.209,4.312ZM18.184,1.929C17.871,1.929 17.617,2.183 17.617,2.486C17.617,2.798 17.871,3.062 18.184,3.062L19.404,3.062C21.523,3.062 22.861,4.527 22.861,6.607L22.861,8.071C22.861,8.403 23.135,8.677 23.467,8.677C23.799,8.677 24.082,8.403 24.082,8.071L24.082,6.607C24.082,3.814 22.217,1.929 19.414,1.929Z"
|
|
17
|
+
android:fillColor="#ffffff"
|
|
18
|
+
android:fillAlpha="0.85"/>
|
|
19
|
+
</vector>
|
|
@@ -3,39 +3,130 @@
|
|
|
3
3
|
android:layout_width="match_parent"
|
|
4
4
|
android:layout_height="match_parent"
|
|
5
5
|
android:orientation="vertical">
|
|
6
|
+
|
|
6
7
|
<FrameLayout
|
|
7
8
|
android:id="@+id/headerView"
|
|
8
9
|
android:layout_width="match_parent"
|
|
9
10
|
android:layout_height="wrap_content"
|
|
10
|
-
android:minHeight="50dp"
|
|
11
11
|
android:background="@android:color/black"
|
|
12
|
-
android:visibility="gone"
|
|
13
|
-
|
|
14
|
-
<
|
|
15
|
-
android:
|
|
16
|
-
android:
|
|
17
|
-
android:layout_height="match_parent"
|
|
12
|
+
android:visibility="gone">
|
|
13
|
+
|
|
14
|
+
<HorizontalScrollView
|
|
15
|
+
android:layout_width="match_parent"
|
|
16
|
+
android:layout_height="wrap_content"
|
|
18
17
|
android:layout_gravity="center"
|
|
19
|
-
android:
|
|
20
|
-
android:
|
|
21
|
-
android:
|
|
22
|
-
android:
|
|
23
|
-
|
|
18
|
+
android:scrollbars="none"
|
|
19
|
+
android:fillViewport="true"
|
|
20
|
+
android:paddingTop="6dp"
|
|
21
|
+
android:paddingBottom="2dp">
|
|
22
|
+
|
|
23
|
+
<TextView
|
|
24
|
+
android:id="@+id/headerText"
|
|
25
|
+
android:layout_width="wrap_content"
|
|
26
|
+
android:layout_height="wrap_content"
|
|
27
|
+
android:gravity="center"
|
|
28
|
+
android:singleLine="true"
|
|
29
|
+
android:text=""
|
|
30
|
+
android:textColor="@android:color/white"
|
|
31
|
+
android:textSize="16dp"
|
|
32
|
+
android:paddingHorizontal="16dp" />
|
|
33
|
+
</HorizontalScrollView>
|
|
24
34
|
</FrameLayout>
|
|
25
35
|
|
|
26
|
-
<
|
|
36
|
+
<LinearLayout
|
|
37
|
+
android:id="@+id/transformRow"
|
|
38
|
+
android:layout_width="match_parent"
|
|
39
|
+
android:layout_height="wrap_content"
|
|
40
|
+
android:layout_below="@+id/headerView"
|
|
41
|
+
android:orientation="horizontal"
|
|
42
|
+
android:paddingHorizontal="16dp"
|
|
43
|
+
android:paddingVertical="4dp"
|
|
44
|
+
android:background="@android:color/black"
|
|
45
|
+
android:gravity="center_vertical"
|
|
46
|
+
android:visibility="gone">
|
|
47
|
+
|
|
48
|
+
<LinearLayout
|
|
49
|
+
android:layout_width="wrap_content"
|
|
50
|
+
android:layout_height="wrap_content"
|
|
51
|
+
android:orientation="horizontal"
|
|
52
|
+
android:gravity="center_vertical">
|
|
53
|
+
|
|
54
|
+
<ImageView
|
|
55
|
+
android:id="@+id/flipBtn"
|
|
56
|
+
android:layout_width="22dp"
|
|
57
|
+
android:layout_height="22dp"
|
|
58
|
+
android:src="@drawable/arrow_trianglehead_left_and_right_righttriangle_left_righttriangle_right"
|
|
59
|
+
android:tint="@android:color/white"
|
|
60
|
+
android:scaleType="fitCenter"
|
|
61
|
+
android:layout_marginEnd="12dp"
|
|
62
|
+
android:contentDescription="Flip" />
|
|
63
|
+
|
|
64
|
+
<ImageView
|
|
65
|
+
android:id="@+id/rotateBtn"
|
|
66
|
+
android:layout_width="22dp"
|
|
67
|
+
android:layout_height="22dp"
|
|
68
|
+
android:src="@drawable/rotate_left"
|
|
69
|
+
android:tint="@android:color/white"
|
|
70
|
+
android:scaleType="fitCenter"
|
|
71
|
+
android:layout_marginEnd="12dp"
|
|
72
|
+
android:contentDescription="Rotate" />
|
|
73
|
+
|
|
74
|
+
<ImageView
|
|
75
|
+
android:id="@+id/cropBtn"
|
|
76
|
+
android:layout_width="22dp"
|
|
77
|
+
android:layout_height="22dp"
|
|
78
|
+
android:src="@drawable/crop"
|
|
79
|
+
android:tint="#80FFFFFF"
|
|
80
|
+
android:scaleType="fitCenter"
|
|
81
|
+
android:contentDescription="Crop" />
|
|
82
|
+
</LinearLayout>
|
|
83
|
+
|
|
84
|
+
<View
|
|
85
|
+
android:layout_width="0dp"
|
|
86
|
+
android:layout_height="0dp"
|
|
87
|
+
android:layout_weight="1" />
|
|
88
|
+
|
|
89
|
+
<LinearLayout
|
|
90
|
+
android:layout_width="wrap_content"
|
|
91
|
+
android:layout_height="wrap_content"
|
|
92
|
+
android:orientation="horizontal"
|
|
93
|
+
android:gravity="center_vertical">
|
|
94
|
+
|
|
95
|
+
<ImageView
|
|
96
|
+
android:id="@+id/undoBtn"
|
|
97
|
+
android:layout_width="22dp"
|
|
98
|
+
android:layout_height="22dp"
|
|
99
|
+
android:src="@drawable/arrow_uturn_backward"
|
|
100
|
+
android:tint="#80FFFFFF"
|
|
101
|
+
android:scaleType="fitCenter"
|
|
102
|
+
android:layout_marginEnd="12dp"
|
|
103
|
+
android:contentDescription="Undo" />
|
|
104
|
+
|
|
105
|
+
<ImageView
|
|
106
|
+
android:id="@+id/redoBtn"
|
|
107
|
+
android:layout_width="22dp"
|
|
108
|
+
android:layout_height="22dp"
|
|
109
|
+
android:src="@drawable/arrow_uturn_forward"
|
|
110
|
+
android:tint="#80FFFFFF"
|
|
111
|
+
android:scaleType="fitCenter"
|
|
112
|
+
android:contentDescription="Redo" />
|
|
113
|
+
</LinearLayout>
|
|
114
|
+
</LinearLayout>
|
|
115
|
+
|
|
116
|
+
<FrameLayout
|
|
117
|
+
android:id="@+id/videoContainer"
|
|
27
118
|
android:layout_width="match_parent"
|
|
28
119
|
android:layout_height="match_parent"
|
|
29
120
|
android:layout_above="@+id/layout"
|
|
30
|
-
android:layout_below="@+id/
|
|
121
|
+
android:layout_below="@+id/transformRow"
|
|
31
122
|
android:background="@android:color/black"
|
|
32
|
-
android:
|
|
123
|
+
android:clipChildren="true">
|
|
33
124
|
|
|
34
|
-
<
|
|
125
|
+
<TextureView
|
|
35
126
|
android:id="@+id/video_loader"
|
|
36
127
|
android:layout_width="match_parent"
|
|
37
128
|
android:layout_height="match_parent"
|
|
38
|
-
/>
|
|
129
|
+
android:layout_gravity="center" />
|
|
39
130
|
|
|
40
131
|
<FrameLayout
|
|
41
132
|
android:id="@+id/audioBannerView"
|
|
@@ -53,11 +144,9 @@
|
|
|
53
144
|
android:layout_height="match_parent"
|
|
54
145
|
android:layout_gravity="center"
|
|
55
146
|
android:contentDescription="Airpods Max"
|
|
56
|
-
android:src="@drawable/airpodsmax"
|
|
57
|
-
/>
|
|
147
|
+
android:src="@drawable/airpodsmax" />
|
|
58
148
|
</FrameLayout>
|
|
59
|
-
|
|
60
|
-
</RelativeLayout>
|
|
149
|
+
</FrameLayout>
|
|
61
150
|
|
|
62
151
|
<RelativeLayout
|
|
63
152
|
android:id="@+id/layout"
|
|
@@ -66,7 +155,6 @@
|
|
|
66
155
|
android:layout_alignParentBottom="true"
|
|
67
156
|
android:background="@android:color/black">
|
|
68
157
|
|
|
69
|
-
|
|
70
158
|
<RelativeLayout
|
|
71
159
|
android:id="@+id/trimmerView"
|
|
72
160
|
android:layout_width="match_parent"
|
|
@@ -79,8 +167,7 @@
|
|
|
79
167
|
android:layout_height="match_parent"
|
|
80
168
|
android:layout_marginHorizontal="20dp"
|
|
81
169
|
android:orientation="horizontal"
|
|
82
|
-
android:padding="4dp"
|
|
83
|
-
/>
|
|
170
|
+
android:padding="4dp" />
|
|
84
171
|
|
|
85
172
|
<RelativeLayout
|
|
86
173
|
android:id="@+id/trimmerContainerWrapper"
|
|
@@ -161,9 +248,7 @@
|
|
|
161
248
|
android:contentDescription="TrailingHandle"
|
|
162
249
|
android:src="@drawable/chevron_compact_right" />
|
|
163
250
|
</FrameLayout>
|
|
164
|
-
|
|
165
251
|
</RelativeLayout>
|
|
166
|
-
|
|
167
252
|
</RelativeLayout>
|
|
168
253
|
|
|
169
254
|
<FrameLayout
|
|
@@ -213,7 +298,6 @@
|
|
|
213
298
|
android:text="00:00.00"
|
|
214
299
|
android:textColor="@android:color/white"
|
|
215
300
|
android:textSize="16sp" />
|
|
216
|
-
|
|
217
301
|
</FrameLayout>
|
|
218
302
|
|
|
219
303
|
<FrameLayout
|
|
@@ -244,8 +328,7 @@
|
|
|
244
328
|
android:padding="12dp"
|
|
245
329
|
android:src="@drawable/play_fill"
|
|
246
330
|
android:tint="@color/white"
|
|
247
|
-
android:visibility="gone"
|
|
248
|
-
/>
|
|
331
|
+
android:visibility="gone" />
|
|
249
332
|
|
|
250
333
|
<ImageView
|
|
251
334
|
android:id="@+id/failToLoadBtn"
|
|
@@ -255,8 +338,7 @@
|
|
|
255
338
|
android:padding="12dp"
|
|
256
339
|
android:src="@drawable/exclamationmark_triangle_fill"
|
|
257
340
|
android:tint="@color/trim_color"
|
|
258
|
-
android:visibility="gone"
|
|
259
|
-
/>
|
|
341
|
+
android:visibility="gone" />
|
|
260
342
|
|
|
261
343
|
<ProgressBar
|
|
262
344
|
android:id="@+id/loadingIndicator"
|
|
@@ -265,8 +347,7 @@
|
|
|
265
347
|
android:layout_height="wrap_content"
|
|
266
348
|
android:layout_gravity="center"
|
|
267
349
|
android:padding="10dp"
|
|
268
|
-
android:indeterminateTint="@color/white"
|
|
269
|
-
/>
|
|
350
|
+
android:indeterminateTint="@color/white" />
|
|
270
351
|
|
|
271
352
|
<TextView
|
|
272
353
|
android:id="@+id/saveBtn"
|
|
@@ -278,10 +359,7 @@
|
|
|
278
359
|
android:text="@string/save"
|
|
279
360
|
android:textColor="#2196F3"
|
|
280
361
|
android:textSize="16dp"
|
|
281
|
-
android:visibility="gone"
|
|
282
|
-
/>
|
|
283
|
-
|
|
362
|
+
android:visibility="gone" />
|
|
284
363
|
</FrameLayout>
|
|
285
|
-
|
|
286
364
|
</RelativeLayout>
|
|
287
365
|
</RelativeLayout>
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
|
|
3
|
+
@available(iOS 13.0, *)
|
|
4
|
+
class CropOverlayView: UIView {
|
|
5
|
+
|
|
6
|
+
var cropRect: CGRect = .zero {
|
|
7
|
+
didSet {
|
|
8
|
+
setNeedsDisplay()
|
|
9
|
+
updateDimmingMask()
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
var allowedRect: CGRect = .zero {
|
|
14
|
+
didSet {
|
|
15
|
+
if cropRect.isEmpty {
|
|
16
|
+
cropRect = allowedRect
|
|
17
|
+
} else {
|
|
18
|
+
let clamped = cropRect.intersection(allowedRect)
|
|
19
|
+
if clamped.isEmpty || clamped.width < minCropSize || clamped.height < minCropSize {
|
|
20
|
+
cropRect = allowedRect
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
var onCropChanged: (() -> Void)?
|
|
27
|
+
var onCropBegan: (() -> Void)?
|
|
28
|
+
var onCropEnded: (() -> Void)?
|
|
29
|
+
|
|
30
|
+
private let minCropSize: CGFloat = 60
|
|
31
|
+
private let borderWidth: CGFloat = 1.0
|
|
32
|
+
private let cornerLength: CGFloat = 20
|
|
33
|
+
private let cornerWidth: CGFloat = 4.0
|
|
34
|
+
private let gridLineWidth: CGFloat = CGFloat(1.0 / UIScreen.main.scale)
|
|
35
|
+
private let edgeHitZone: CGFloat = 30
|
|
36
|
+
|
|
37
|
+
private var activeEdge: DragEdge?
|
|
38
|
+
private var dragStart: CGPoint = .zero
|
|
39
|
+
private var dragStartRect: CGRect = .zero
|
|
40
|
+
|
|
41
|
+
private let dimmingLayer = CAShapeLayer()
|
|
42
|
+
|
|
43
|
+
private enum DragEdge {
|
|
44
|
+
case top, bottom, left, right
|
|
45
|
+
case topLeft, topRight, bottomLeft, bottomRight
|
|
46
|
+
case move
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
override init(frame: CGRect) {
|
|
50
|
+
super.init(frame: frame)
|
|
51
|
+
commonInit()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
required init?(coder: NSCoder) {
|
|
55
|
+
super.init(coder: coder)
|
|
56
|
+
commonInit()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private func commonInit() {
|
|
60
|
+
backgroundColor = .clear
|
|
61
|
+
isUserInteractionEnabled = true
|
|
62
|
+
clipsToBounds = true
|
|
63
|
+
isOpaque = false
|
|
64
|
+
|
|
65
|
+
dimmingLayer.fillRule = .evenOdd
|
|
66
|
+
dimmingLayer.fillColor = UIColor.black.withAlphaComponent(0.55).cgColor
|
|
67
|
+
layer.addSublayer(dimmingLayer)
|
|
68
|
+
|
|
69
|
+
let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
|
|
70
|
+
addGestureRecognizer(pan)
|
|
71
|
+
|
|
72
|
+
let pinch = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(_:)))
|
|
73
|
+
addGestureRecognizer(pinch)
|
|
74
|
+
}
|
|
75
|
+
|
|
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
|
+
// MARK: - Drawing
|
|
91
|
+
|
|
92
|
+
override func draw(_ rect: CGRect) {
|
|
93
|
+
guard !cropRect.isEmpty, let ctx = UIGraphicsGetCurrentContext() else { return }
|
|
94
|
+
let cr = cropRect
|
|
95
|
+
|
|
96
|
+
ctx.setStrokeColor(UIColor.white.withAlphaComponent(0.6).cgColor)
|
|
97
|
+
ctx.setLineWidth(borderWidth)
|
|
98
|
+
ctx.stroke(cr)
|
|
99
|
+
|
|
100
|
+
ctx.setLineWidth(gridLineWidth)
|
|
101
|
+
ctx.setStrokeColor(UIColor.white.cgColor)
|
|
102
|
+
for i in 1...2 {
|
|
103
|
+
let x = cr.minX + cr.width * CGFloat(i) / 3
|
|
104
|
+
ctx.move(to: CGPoint(x: x, y: cr.minY))
|
|
105
|
+
ctx.addLine(to: CGPoint(x: x, y: cr.maxY))
|
|
106
|
+
}
|
|
107
|
+
for i in 1...2 {
|
|
108
|
+
let y = cr.minY + cr.height * CGFloat(i) / 3
|
|
109
|
+
ctx.move(to: CGPoint(x: cr.minX, y: y))
|
|
110
|
+
ctx.addLine(to: CGPoint(x: cr.maxX, y: y))
|
|
111
|
+
}
|
|
112
|
+
ctx.strokePath()
|
|
113
|
+
|
|
114
|
+
ctx.setStrokeColor(UIColor.white.cgColor)
|
|
115
|
+
ctx.setLineWidth(cornerWidth)
|
|
116
|
+
ctx.setLineCap(.round)
|
|
117
|
+
|
|
118
|
+
let cl = cornerLength
|
|
119
|
+
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)),
|
|
132
|
+
]
|
|
133
|
+
for (start, corner, end) in corners {
|
|
134
|
+
ctx.move(to: start)
|
|
135
|
+
ctx.addLine(to: corner)
|
|
136
|
+
ctx.addLine(to: end)
|
|
137
|
+
}
|
|
138
|
+
ctx.strokePath()
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// MARK: - Hit Testing
|
|
142
|
+
|
|
143
|
+
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
|
144
|
+
if detectEdge(at: point) != nil { return self }
|
|
145
|
+
return nil
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private func detectEdge(at pt: CGPoint) -> DragEdge? {
|
|
149
|
+
let r = cropRect
|
|
150
|
+
let z = edgeHitZone
|
|
151
|
+
|
|
152
|
+
let nearT = abs(pt.y - r.minY) < z
|
|
153
|
+
let nearB = abs(pt.y - r.maxY) < z
|
|
154
|
+
let nearL = abs(pt.x - r.minX) < z
|
|
155
|
+
let nearR = abs(pt.x - r.maxX) < z
|
|
156
|
+
let inH = pt.x > r.minX - z && pt.x < r.maxX + z
|
|
157
|
+
let inV = pt.y > r.minY - z && pt.y < r.maxY + z
|
|
158
|
+
|
|
159
|
+
if nearT && nearL { return .topLeft }
|
|
160
|
+
if nearT && nearR { return .topRight }
|
|
161
|
+
if nearB && nearL { return .bottomLeft }
|
|
162
|
+
if nearB && nearR { return .bottomRight }
|
|
163
|
+
if nearT && inH { return .top }
|
|
164
|
+
if nearB && inH { return .bottom }
|
|
165
|
+
if nearL && inV { return .left }
|
|
166
|
+
if nearR && inV { return .right }
|
|
167
|
+
if r.contains(pt) { return .move }
|
|
168
|
+
return nil
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// MARK: - Gestures
|
|
172
|
+
|
|
173
|
+
@objc private func handlePan(_ g: UIPanGestureRecognizer) {
|
|
174
|
+
let pt = g.location(in: self)
|
|
175
|
+
switch g.state {
|
|
176
|
+
case .began:
|
|
177
|
+
activeEdge = detectEdge(at: pt)
|
|
178
|
+
dragStart = pt
|
|
179
|
+
dragStartRect = cropRect
|
|
180
|
+
onCropBegan?()
|
|
181
|
+
case .changed:
|
|
182
|
+
guard let edge = activeEdge else { return }
|
|
183
|
+
let dx = pt.x - dragStart.x
|
|
184
|
+
let dy = pt.y - dragStart.y
|
|
185
|
+
cropRect = computeNewRect(edge: edge, dx: dx, dy: dy)
|
|
186
|
+
onCropChanged?()
|
|
187
|
+
case .ended, .cancelled:
|
|
188
|
+
activeEdge = nil
|
|
189
|
+
onCropEnded?()
|
|
190
|
+
default:
|
|
191
|
+
break
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
@objc private func handlePinch(_ g: UIPinchGestureRecognizer) {
|
|
196
|
+
switch g.state {
|
|
197
|
+
case .began:
|
|
198
|
+
dragStartRect = cropRect
|
|
199
|
+
onCropBegan?()
|
|
200
|
+
case .changed:
|
|
201
|
+
let scale = g.scale
|
|
202
|
+
let cx = dragStartRect.midX
|
|
203
|
+
let cy = dragStartRect.midY
|
|
204
|
+
var newW = max(dragStartRect.width * scale, minCropSize)
|
|
205
|
+
var newH = max(dragStartRect.height * scale, minCropSize)
|
|
206
|
+
newW = min(newW, allowedRect.width)
|
|
207
|
+
newH = min(newH, allowedRect.height)
|
|
208
|
+
var r = CGRect(x: cx - newW / 2, y: cy - newH / 2, width: newW, height: newH)
|
|
209
|
+
r = clamp(r, isMove: true)
|
|
210
|
+
cropRect = r
|
|
211
|
+
onCropChanged?()
|
|
212
|
+
case .ended, .cancelled:
|
|
213
|
+
onCropEnded?()
|
|
214
|
+
default:
|
|
215
|
+
break
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// MARK: - Rect Computation
|
|
220
|
+
|
|
221
|
+
private func computeNewRect(edge: DragEdge, dx: CGFloat, dy: CGFloat) -> CGRect {
|
|
222
|
+
var r = dragStartRect
|
|
223
|
+
|
|
224
|
+
switch edge {
|
|
225
|
+
case .topLeft:
|
|
226
|
+
r.origin.x += dx; r.size.width -= dx
|
|
227
|
+
r.origin.y += dy; r.size.height -= dy
|
|
228
|
+
case .topRight:
|
|
229
|
+
r.size.width += dx
|
|
230
|
+
r.origin.y += dy; r.size.height -= dy
|
|
231
|
+
case .bottomLeft:
|
|
232
|
+
r.origin.x += dx; r.size.width -= dx
|
|
233
|
+
r.size.height += dy
|
|
234
|
+
case .bottomRight:
|
|
235
|
+
r.size.width += dx; r.size.height += dy
|
|
236
|
+
case .top:
|
|
237
|
+
r.origin.y += dy; r.size.height -= dy
|
|
238
|
+
case .bottom:
|
|
239
|
+
r.size.height += dy
|
|
240
|
+
case .left:
|
|
241
|
+
r.origin.x += dx; r.size.width -= dx
|
|
242
|
+
case .right:
|
|
243
|
+
r.size.width += dx
|
|
244
|
+
case .move:
|
|
245
|
+
r.origin.x += dx; r.origin.y += dy
|
|
246
|
+
return clamp(r, isMove: true)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if r.width < minCropSize {
|
|
250
|
+
let anchorsRight = (edge == .left || edge == .topLeft || edge == .bottomLeft)
|
|
251
|
+
if anchorsRight { r.origin.x = dragStartRect.maxX - minCropSize }
|
|
252
|
+
r.size.width = minCropSize
|
|
253
|
+
}
|
|
254
|
+
if r.height < minCropSize {
|
|
255
|
+
let anchorsBottom = (edge == .top || edge == .topLeft || edge == .topRight)
|
|
256
|
+
if anchorsBottom { r.origin.y = dragStartRect.maxY - minCropSize }
|
|
257
|
+
r.size.height = minCropSize
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return clamp(r, isMove: false)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
private func clamp(_ rect: CGRect, isMove: Bool) -> CGRect {
|
|
264
|
+
var r = rect
|
|
265
|
+
let a = allowedRect
|
|
266
|
+
guard !a.isEmpty else { return r }
|
|
267
|
+
|
|
268
|
+
if isMove {
|
|
269
|
+
r.size.width = min(r.width, a.width)
|
|
270
|
+
r.size.height = min(r.height, a.height)
|
|
271
|
+
r.origin.x = max(a.minX, min(r.origin.x, a.maxX - r.width))
|
|
272
|
+
r.origin.y = max(a.minY, min(r.origin.y, a.maxY - r.height))
|
|
273
|
+
} else {
|
|
274
|
+
r.origin.x = max(r.origin.x, a.minX)
|
|
275
|
+
r.origin.y = max(r.origin.y, a.minY)
|
|
276
|
+
if r.maxX > a.maxX { r.size.width = a.maxX - r.origin.x }
|
|
277
|
+
if r.maxY > a.maxY { r.size.height = a.maxY - r.origin.y }
|
|
278
|
+
}
|
|
279
|
+
return r
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
func resetCrop() {
|
|
283
|
+
cropRect = allowedRect
|
|
284
|
+
}
|
|
285
|
+
}
|
package/ios/VideoTrim.mm
CHANGED
|
@@ -70,10 +70,9 @@ RCT_EXPORT_MODULE()
|
|
|
70
70
|
dict[@"outputExt"] = options.outputExt();
|
|
71
71
|
dict[@"removeAfterSavedToPhoto"] = @(options.removeAfterSavedToPhoto());
|
|
72
72
|
dict[@"removeAfterFailedToSavePhoto"] = @(options.removeAfterFailedToSavePhoto());
|
|
73
|
-
dict[@"enableRotation"] = @(options.enableRotation());
|
|
74
|
-
dict[@"rotationAngle"] = @(options.rotationAngle());
|
|
75
73
|
dict[@"startTime"] = @(options.startTime());
|
|
76
74
|
dict[@"endTime"] = @(options.endTime());
|
|
75
|
+
dict[@"enablePreciseTrimming"] = @(options.enablePreciseTrimming());
|
|
77
76
|
|
|
78
77
|
[self->videoTrim trim:url url:dict config:^(NSDictionary<NSString *,id> * _Nonnull result) {
|
|
79
78
|
BOOL success = [result[@"success"] boolValue];
|
|
@@ -109,8 +108,6 @@ RCT_EXPORT_MODULE()
|
|
|
109
108
|
dict[@"removeAfterFailedToSaveDocuments"] = @(config.removeAfterFailedToSaveDocuments());
|
|
110
109
|
dict[@"removeAfterShared"] = @(config.removeAfterShared());
|
|
111
110
|
dict[@"removeAfterFailedToShare"] = @(config.removeAfterFailedToShare());
|
|
112
|
-
dict[@"enableRotation"] = @(config.enableRotation());
|
|
113
|
-
dict[@"rotationAngle"] = @(config.rotationAngle());
|
|
114
111
|
dict[@"enableHapticFeedback"] = @(config.enableHapticFeedback());
|
|
115
112
|
dict[@"maxDuration"] = @(config.maxDuration());
|
|
116
113
|
dict[@"minDuration"] = @(config.minDuration());
|
|
@@ -145,6 +142,7 @@ RCT_EXPORT_MODULE()
|
|
|
145
142
|
dict[@"alertOnFailTitle"] = config.alertOnFailTitle();
|
|
146
143
|
dict[@"alertOnFailMessage"] = config.alertOnFailMessage();
|
|
147
144
|
dict[@"alertOnFailCloseText"] = config.alertOnFailCloseText();
|
|
145
|
+
dict[@"enablePreciseTrimming"] = @(config.enablePreciseTrimming());
|
|
148
146
|
|
|
149
147
|
// Handle optional color values
|
|
150
148
|
auto trimmerColorOpt = config.trimmerColor();
|