@imgly/editor-react-native 1.73.0 → 1.73.1

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.
Files changed (117) hide show
  1. package/android/build.gradle +1 -1
  2. package/android/src/main/assets/scene/apparel.scene +1 -0
  3. package/android/src/main/assets/scene/design.scene +1 -0
  4. package/android/src/main/assets/scene/postcard.scene +1 -0
  5. package/android/src/main/assets/scene/video.scene +1 -0
  6. package/android/src/main/kotlin/ly/img/editor/configuration/apparel/ApparelConfigurationBuilder.kt +113 -0
  7. package/android/src/main/kotlin/ly/img/editor/configuration/apparel/EditorActivity.kt +37 -0
  8. package/android/src/main/kotlin/ly/img/editor/configuration/apparel/callback/OnCreate.kt +190 -0
  9. package/android/src/main/kotlin/ly/img/editor/configuration/apparel/callback/OnExport.kt +82 -0
  10. package/android/src/main/kotlin/ly/img/editor/configuration/apparel/callback/OnLoaded.kt +88 -0
  11. package/android/src/main/kotlin/ly/img/editor/configuration/apparel/component/CanvasMenu.kt +45 -0
  12. package/android/src/main/kotlin/ly/img/editor/configuration/apparel/component/Dock.kt +37 -0
  13. package/android/src/main/kotlin/ly/img/editor/configuration/apparel/component/InspectorBar.kt +52 -0
  14. package/android/src/main/kotlin/ly/img/editor/configuration/apparel/component/NavigationBar.kt +45 -0
  15. package/android/src/main/kotlin/ly/img/editor/configuration/apparel/component/Overlay.kt +15 -0
  16. package/android/src/main/kotlin/ly/img/editor/configuration/design/DesignConfigurationBuilder.kt +106 -0
  17. package/android/src/main/kotlin/ly/img/editor/configuration/design/EditorActivity.kt +37 -0
  18. package/android/src/main/kotlin/ly/img/editor/configuration/design/callback/OnCreate.kt +162 -0
  19. package/android/src/main/kotlin/ly/img/editor/configuration/design/callback/OnExport.kt +82 -0
  20. package/android/src/main/kotlin/ly/img/editor/configuration/design/callback/OnLoaded.kt +10 -0
  21. package/android/src/main/kotlin/ly/img/editor/configuration/design/component/CanvasMenu.kt +45 -0
  22. package/android/src/main/kotlin/ly/img/editor/configuration/design/component/Dock.kt +35 -0
  23. package/android/src/main/kotlin/ly/img/editor/configuration/design/component/InspectorBar.kt +52 -0
  24. package/android/src/main/kotlin/ly/img/editor/configuration/design/component/NavigationBar.kt +45 -0
  25. package/android/src/main/kotlin/ly/img/editor/configuration/design/component/Overlay.kt +15 -0
  26. package/android/src/main/kotlin/ly/img/editor/configuration/photo/EditorActivity.kt +37 -0
  27. package/android/src/main/kotlin/ly/img/editor/configuration/photo/PhotoConfigurationBuilder.kt +106 -0
  28. package/android/src/main/kotlin/ly/img/editor/configuration/photo/callback/OnCreate.kt +143 -0
  29. package/android/src/main/kotlin/ly/img/editor/configuration/photo/callback/OnExport.kt +82 -0
  30. package/android/src/main/kotlin/ly/img/editor/configuration/photo/callback/OnLoaded.kt +49 -0
  31. package/android/src/main/kotlin/ly/img/editor/configuration/photo/component/CanvasMenu.kt +45 -0
  32. package/android/src/main/kotlin/ly/img/editor/configuration/photo/component/Dock.kt +43 -0
  33. package/android/src/main/kotlin/ly/img/editor/configuration/photo/component/InspectorBar.kt +52 -0
  34. package/android/src/main/kotlin/ly/img/editor/configuration/photo/component/NavigationBar.kt +45 -0
  35. package/android/src/main/kotlin/ly/img/editor/configuration/photo/component/Overlay.kt +15 -0
  36. package/android/src/main/kotlin/ly/img/editor/configuration/postcard/EditorActivity.kt +37 -0
  37. package/android/src/main/kotlin/ly/img/editor/configuration/postcard/PostcardConfigurationBuilder.kt +106 -0
  38. package/android/src/main/kotlin/ly/img/editor/configuration/postcard/callback/OnCreate.kt +149 -0
  39. package/android/src/main/kotlin/ly/img/editor/configuration/postcard/callback/OnExport.kt +82 -0
  40. package/android/src/main/kotlin/ly/img/editor/configuration/postcard/callback/OnLoaded.kt +58 -0
  41. package/android/src/main/kotlin/ly/img/editor/configuration/postcard/component/CanvasMenu.kt +45 -0
  42. package/android/src/main/kotlin/ly/img/editor/configuration/postcard/component/Dock.kt +243 -0
  43. package/android/src/main/kotlin/ly/img/editor/configuration/postcard/component/InspectorBar.kt +52 -0
  44. package/android/src/main/kotlin/ly/img/editor/configuration/postcard/component/NavigationBar.kt +76 -0
  45. package/android/src/main/kotlin/ly/img/editor/configuration/postcard/component/Overlay.kt +15 -0
  46. package/android/src/main/kotlin/ly/img/editor/configuration/postcard/ext/Engine.kt +107 -0
  47. package/android/src/main/kotlin/ly/img/editor/configuration/video/EditorActivity.kt +37 -0
  48. package/android/src/main/kotlin/ly/img/editor/configuration/video/VideoConfigurationBuilder.kt +135 -0
  49. package/android/src/main/kotlin/ly/img/editor/configuration/video/callback/OnCreate.kt +191 -0
  50. package/android/src/main/kotlin/ly/img/editor/configuration/video/callback/OnExport.kt +98 -0
  51. package/android/src/main/kotlin/ly/img/editor/configuration/video/callback/OnLoaded.kt +20 -0
  52. package/android/src/main/kotlin/ly/img/editor/configuration/video/component/BottomPanel.kt +13 -0
  53. package/android/src/main/kotlin/ly/img/editor/configuration/video/component/CanvasMenu.kt +45 -0
  54. package/android/src/main/kotlin/ly/img/editor/configuration/video/component/Dock.kt +71 -0
  55. package/android/src/main/kotlin/ly/img/editor/configuration/video/component/InspectorBar.kt +68 -0
  56. package/android/src/main/kotlin/ly/img/editor/configuration/video/component/NavigationBar.kt +57 -0
  57. package/android/src/main/kotlin/ly/img/editor/configuration/video/component/Overlay.kt +421 -0
  58. package/android/src/main/kotlin/ly/img/editor/configuration/video/iconPack/CheckCircleOutline.kt +79 -0
  59. package/android/src/main/kotlin/ly/img/editor/configuration/video/iconPack/ErrorOutline.kt +92 -0
  60. package/android/src/main/kotlin/ly/img/editor/configuration/video/iconPack/IconPack.kt +14 -0
  61. package/android/src/main/kotlin/ly/img/editor/configuration/video/model/ExportStatus.kt +39 -0
  62. package/ios/IMGLYEditorModule.swift +9 -1
  63. package/ios/StarterKits/Apparel/ApparelEditorConfiguration.swift +46 -0
  64. package/ios/StarterKits/Apparel/ApparelEditorStarterKit.swift +30 -0
  65. package/ios/StarterKits/Apparel/callbacks/OnChanged+Apparel.swift +116 -0
  66. package/ios/StarterKits/Apparel/callbacks/OnCreate+Apparel.swift +197 -0
  67. package/ios/StarterKits/Apparel/callbacks/OnExport+Apparel.swift +29 -0
  68. package/ios/StarterKits/Apparel/components/CanvasMenu+Apparel.swift +22 -0
  69. package/ios/StarterKits/Apparel/components/Dock+Apparel.swift +19 -0
  70. package/ios/StarterKits/Apparel/components/InspectorBar+Apparel.swift +35 -0
  71. package/ios/StarterKits/Apparel/components/NavigationBar+Apparel.swift +26 -0
  72. package/ios/StarterKits/Design/DesignEditorConfiguration.swift +43 -0
  73. package/ios/StarterKits/Design/DesignEditorStarterKit.swift +30 -0
  74. package/ios/StarterKits/Design/callbacks/OnCreate+Design.swift +131 -0
  75. package/ios/StarterKits/Design/callbacks/OnExport+Design.swift +30 -0
  76. package/ios/StarterKits/Design/components/CanvasMenu+Design.swift +22 -0
  77. package/ios/StarterKits/Design/components/Dock+Design.swift +23 -0
  78. package/ios/StarterKits/Design/components/InspectorBar+Design.swift +35 -0
  79. package/ios/StarterKits/Design/components/NavigationBar+Design.swift +24 -0
  80. package/ios/StarterKits/Photo/PhotoEditorConfiguration.swift +48 -0
  81. package/ios/StarterKits/Photo/PhotoEditorStarterKit.swift +30 -0
  82. package/ios/StarterKits/Photo/callbacks/OnChanged+Photo.swift +25 -0
  83. package/ios/StarterKits/Photo/callbacks/OnCreate+Photo.swift +138 -0
  84. package/ios/StarterKits/Photo/callbacks/OnExport+Photo.swift +29 -0
  85. package/ios/StarterKits/Photo/callbacks/OnLoaded+Photo.swift +18 -0
  86. package/ios/StarterKits/Photo/components/CanvasMenu+Photo.swift +22 -0
  87. package/ios/StarterKits/Photo/components/Dock+Photo.swift +23 -0
  88. package/ios/StarterKits/Photo/components/InspectorBar+Photo.swift +39 -0
  89. package/ios/StarterKits/Photo/components/NavigationBar+Photo.swift +24 -0
  90. package/ios/StarterKits/Photo/examples/ExampleForceCrop.swift +30 -0
  91. package/ios/StarterKits/Postcard/PostcardEditorConfiguration.swift +45 -0
  92. package/ios/StarterKits/Postcard/PostcardEditorStarterKit.swift +30 -0
  93. package/ios/StarterKits/Postcard/callbacks/OnChanged+Postcard.swift +92 -0
  94. package/ios/StarterKits/Postcard/callbacks/OnCreate+Postcard.swift +127 -0
  95. package/ios/StarterKits/Postcard/callbacks/OnExport+Postcard.swift +29 -0
  96. package/ios/StarterKits/Postcard/components/CanvasMenu+Postcard.swift +22 -0
  97. package/ios/StarterKits/Postcard/components/Dock+Postcard.swift +34 -0
  98. package/ios/StarterKits/Postcard/components/DockButtons+Postcard.swift +129 -0
  99. package/ios/StarterKits/Postcard/components/InspectorBar+Postcard.swift +35 -0
  100. package/ios/StarterKits/Postcard/components/NavigationBar+Postcard.swift +38 -0
  101. package/ios/StarterKits/Video/VideoEditorConfiguration.swift +54 -0
  102. package/ios/StarterKits/Video/VideoEditorStarterKit.swift +30 -0
  103. package/ios/StarterKits/Video/callbacks/OnCreate+Video.swift +137 -0
  104. package/ios/StarterKits/Video/callbacks/OnExport+Video.swift +46 -0
  105. package/ios/StarterKits/Video/components/BottomPanel+Video.swift +16 -0
  106. package/ios/StarterKits/Video/components/CanvasMenu+Video.swift +22 -0
  107. package/ios/StarterKits/Video/components/Dock+Video.swift +30 -0
  108. package/ios/StarterKits/Video/components/InspectorBar+Video.swift +43 -0
  109. package/ios/StarterKits/Video/components/NavigationBar+Video.swift +32 -0
  110. package/ios/StarterKits/Video/examples/ExampleVideoDurationConstraints.swift +23 -0
  111. package/ios/StarterKits/apparel-ui-b-empty.scene +1 -0
  112. package/ios/StarterKits/design-ui-empty.scene +1 -0
  113. package/ios/StarterKits/photo-ui-empty.png +0 -0
  114. package/ios/StarterKits/postcard-empty.scene +1 -0
  115. package/ios/StarterKits/video-empty.scene +1 -0
  116. package/package.json +1 -1
  117. package/src/NativeIMGLYEditor.ts +1 -1
@@ -0,0 +1,13 @@
1
+ package ly.img.editor.configuration.video.component
2
+
3
+ import androidx.compose.runtime.Composable
4
+ import ly.img.editor.core.component.Timeline
5
+ import ly.img.editor.core.component.remember
6
+
7
+ /**
8
+ * The configuration of the component that is displayed over the editor. It is useful if you want to display a popup dialog or anything in the
9
+ * overlay.
10
+ * Implementation shows [Timeline] in the bottom panel.
11
+ */
12
+ @Composable
13
+ fun rememberBottomPanel() = Timeline.remember()
@@ -0,0 +1,45 @@
1
+ @file:Suppress("UnusedReceiverParameter")
2
+
3
+ package ly.img.editor.configuration.video.component
4
+
5
+ import androidx.compose.runtime.Composable
6
+ import ly.img.editor.configuration.video.VideoConfigurationBuilder
7
+ import ly.img.editor.core.component.CanvasMenu
8
+ import ly.img.editor.core.component.EditorComponentId
9
+ import ly.img.editor.core.component.remember
10
+ import ly.img.editor.core.component.rememberBringForward
11
+ import ly.img.editor.core.component.rememberDelete
12
+ import ly.img.editor.core.component.rememberDuplicate
13
+ import ly.img.editor.core.component.rememberSelectGroup
14
+ import ly.img.editor.core.component.rememberSendBackward
15
+
16
+ /**
17
+ * The configuration of the component that is displayed as horizontal list of items next to
18
+ * the selected design block.
19
+ */
20
+ @Composable
21
+ fun VideoConfigurationBuilder.rememberCanvasMenu() = CanvasMenu.remember {
22
+ listBuilder = {
23
+ CanvasMenu.ListBuilder.remember {
24
+ add { CanvasMenu.Button.rememberSelectGroup() }
25
+ if (editorContext.isSelectionInGroup) {
26
+ add {
27
+ CanvasMenu.Divider.remember {
28
+ id = { EditorComponentId("ly.img.component.canvasMenu.divider1") }
29
+ }
30
+ }
31
+ }
32
+ add { CanvasMenu.Button.rememberBringForward() }
33
+ add { CanvasMenu.Button.rememberSendBackward() }
34
+ if (editorContext.canSelectionMove) {
35
+ add {
36
+ CanvasMenu.Divider.remember {
37
+ id = { EditorComponentId("ly.img.component.canvasMenu.divider2") }
38
+ }
39
+ }
40
+ }
41
+ add { CanvasMenu.Button.rememberDuplicate() }
42
+ add { CanvasMenu.Button.rememberDelete() }
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,71 @@
1
+ @file:Suppress("UnusedReceiverParameter", "UnusedFlow")
2
+ @file:OptIn(ExperimentalCoroutinesApi::class)
3
+
4
+ package ly.img.editor.configuration.video.component
5
+
6
+ import androidx.compose.foundation.layout.Arrangement
7
+ import androidx.compose.runtime.Composable
8
+ import androidx.compose.runtime.remember
9
+ import kotlinx.coroutines.ExperimentalCoroutinesApi
10
+ import ly.img.editor.configuration.video.VideoConfigurationBuilder
11
+ import ly.img.editor.core.component.Dock
12
+ import ly.img.editor.core.component.EditorTrigger
13
+ import ly.img.editor.core.component.remember
14
+ import ly.img.editor.core.component.rememberAudiosLibrary
15
+ import ly.img.editor.core.component.rememberOverlaysLibrary
16
+ import ly.img.editor.core.component.rememberReorder
17
+ import ly.img.editor.core.component.rememberResizeAll
18
+ import ly.img.editor.core.component.rememberStickersAndShapesLibrary
19
+ import ly.img.editor.core.component.rememberSystemCamera
20
+ import ly.img.editor.core.component.rememberSystemGallery
21
+ import ly.img.editor.core.component.rememberTextLibrary
22
+ import ly.img.editor.core.component.rememberVoiceoverRecord
23
+ import ly.img.editor.core.iconpack.AddCameraBackground
24
+ import ly.img.editor.core.iconpack.AddGalleryBackground
25
+ import ly.img.editor.core.iconpack.IconPack
26
+
27
+ /**
28
+ * The configuration of the component that is displayed as horizontal list of items at the bottom of the editor.
29
+ */
30
+ @Composable
31
+ fun VideoConfigurationBuilder.rememberDock() = Dock.remember {
32
+ scope = {
33
+ val activeSceneTrigger by EditorTrigger.remember {
34
+ editorContext.engine.scene.onActiveChanged()
35
+ }
36
+ // Update Dock whenever the active scene changes.
37
+ remember(this, activeSceneTrigger) {
38
+ Dock.Scope(parentScope = this)
39
+ }
40
+ }
41
+ horizontalArrangement = {
42
+ // All buttons should have equal size
43
+ Arrangement.SpaceEvenly
44
+ }
45
+ listBuilder = {
46
+ // highlight-starter-kit-imgly-camera
47
+ Dock.ListBuilder.remember {
48
+ add {
49
+ Dock.Button.rememberSystemGallery {
50
+ vectorIcon = { IconPack.AddGalleryBackground }
51
+ }
52
+ }
53
+ add {
54
+ // As an alternative to the system camera we also provide our own camera tech accessible via [Dock.Button.rememberImglyCamera].
55
+ // In order to make it work the following dependency is required:
56
+ // implementation "ly.img:camera:<same version as editor>".
57
+ Dock.Button.rememberSystemCamera(captureVideo = { true }) {
58
+ vectorIcon = { IconPack.AddCameraBackground }
59
+ }
60
+ }
61
+ add { Dock.Button.rememberOverlaysLibrary() }
62
+ add { Dock.Button.rememberTextLibrary() }
63
+ add { Dock.Button.rememberStickersAndShapesLibrary() }
64
+ add { Dock.Button.rememberAudiosLibrary() }
65
+ add { Dock.Button.rememberVoiceoverRecord() }
66
+ add { Dock.Button.rememberResizeAll() }
67
+ add { Dock.Button.rememberReorder() }
68
+ }
69
+ // highlight-starter-kit-imgly-camera
70
+ }
71
+ }
@@ -0,0 +1,68 @@
1
+ @file:Suppress("UnusedReceiverParameter")
2
+
3
+ package ly.img.editor.configuration.video.component
4
+
5
+ import androidx.compose.runtime.Composable
6
+ import ly.img.editor.configuration.video.VideoConfigurationBuilder
7
+ import ly.img.editor.core.component.InspectorBar
8
+ import ly.img.editor.core.component.remember
9
+ import ly.img.editor.core.component.rememberAdjustments
10
+ import ly.img.editor.core.component.rememberAnimations
11
+ import ly.img.editor.core.component.rememberBlur
12
+ import ly.img.editor.core.component.rememberClipSpeed
13
+ import ly.img.editor.core.component.rememberCrop
14
+ import ly.img.editor.core.component.rememberDelete
15
+ import ly.img.editor.core.component.rememberDuplicate
16
+ import ly.img.editor.core.component.rememberEditText
17
+ import ly.img.editor.core.component.rememberEffect
18
+ import ly.img.editor.core.component.rememberEnterGroup
19
+ import ly.img.editor.core.component.rememberFillStroke
20
+ import ly.img.editor.core.component.rememberFilter
21
+ import ly.img.editor.core.component.rememberFormatText
22
+ import ly.img.editor.core.component.rememberLayer
23
+ import ly.img.editor.core.component.rememberMoveAsClip
24
+ import ly.img.editor.core.component.rememberMoveAsOverlay
25
+ import ly.img.editor.core.component.rememberReorder
26
+ import ly.img.editor.core.component.rememberReplace
27
+ import ly.img.editor.core.component.rememberSelectGroup
28
+ import ly.img.editor.core.component.rememberShape
29
+ import ly.img.editor.core.component.rememberSplit
30
+ import ly.img.editor.core.component.rememberTextBackground
31
+ import ly.img.editor.core.component.rememberVoiceover
32
+ import ly.img.editor.core.component.rememberVolume
33
+
34
+ /**
35
+ * The configuration of the component that is displayed as horizontal list of items at the
36
+ * bottom of the editor when a design block is selected.
37
+ */
38
+ @Composable
39
+ fun VideoConfigurationBuilder.rememberInspectorBar() = InspectorBar.remember {
40
+ listBuilder = {
41
+ InspectorBar.ListBuilder.remember {
42
+ add { InspectorBar.Button.rememberVoiceover() } // Voiceover Audio
43
+ add { InspectorBar.Button.rememberReplace() } // Video, Image, Sticker, Audio
44
+ add { InspectorBar.Button.rememberEditText() } // Text
45
+ add { InspectorBar.Button.rememberFormatText() } // Text
46
+ add { InspectorBar.Button.rememberFillStroke() } // Page, Video, Image, Shape, Text
47
+ add { InspectorBar.Button.rememberTextBackground() } // Text
48
+ add { InspectorBar.Button.rememberVolume() } // Video, Audio
49
+ add { InspectorBar.Button.rememberClipSpeed() } // Video
50
+ add { InspectorBar.Button.rememberCrop() } // Video, Image
51
+ add { InspectorBar.Button.rememberAnimations() } // Video, Image, Sticker, Shape, Text
52
+ add { InspectorBar.Button.rememberAdjustments() } // Video, Image
53
+ add { InspectorBar.Button.rememberFilter() } // Video, Image
54
+ add { InspectorBar.Button.rememberEffect() } // Video, Image
55
+ add { InspectorBar.Button.rememberBlur() } // Video, Image
56
+ add { InspectorBar.Button.rememberShape() } // Video, Image, Shape
57
+ add { InspectorBar.Button.rememberSelectGroup() } // Video, Image, Sticker, Shape, Text
58
+ add { InspectorBar.Button.rememberEnterGroup() } // Group
59
+ add { InspectorBar.Button.rememberLayer() } // Video, Image, Sticker, Shape, Text
60
+ add { InspectorBar.Button.rememberSplit() } // Video, Image, Sticker, Shape, Text, Audio
61
+ add { InspectorBar.Button.rememberMoveAsClip() } // Video, Image, Sticker, Shape, Text
62
+ add { InspectorBar.Button.rememberMoveAsOverlay() } // Video, Image, Sticker, Shape, Text
63
+ add { InspectorBar.Button.rememberReorder() } // Video, Image, Sticker, Shape, Text
64
+ add { InspectorBar.Button.rememberDuplicate() } // Video, Image, Sticker, Shape, Text
65
+ add { InspectorBar.Button.rememberDelete() } // Video, Image, Sticker, Shape, Text
66
+ }
67
+ }
68
+ }
@@ -0,0 +1,57 @@
1
+ @file:Suppress("UnusedReceiverParameter")
2
+
3
+ package ly.img.editor.configuration.video.component
4
+
5
+ import androidx.compose.runtime.Composable
6
+ import androidx.compose.runtime.remember
7
+ import androidx.compose.ui.Alignment
8
+ import ly.img.editor.configuration.video.VideoConfigurationBuilder
9
+ import ly.img.editor.core.component.EditorTrigger
10
+ import ly.img.editor.core.component.NavigationBar
11
+ import ly.img.editor.core.component.remember
12
+ import ly.img.editor.core.component.rememberCloseEditor
13
+ import ly.img.editor.core.component.rememberExport
14
+ import ly.img.editor.core.component.rememberRedo
15
+ import ly.img.editor.core.component.rememberUndo
16
+
17
+ /**
18
+ * The configuration of the component that is displayed as horizontal list of items at the top of the editor.
19
+ */
20
+ @Composable
21
+ fun VideoConfigurationBuilder.rememberNavigationBar() = NavigationBar.remember {
22
+ scope = {
23
+ val activeSceneTrigger by EditorTrigger.remember {
24
+ editorContext.engine.scene.onActiveChanged()
25
+ }
26
+ val historyTrigger by EditorTrigger.remember(activeSceneTrigger) {
27
+ editorContext.engine.editor.onHistoryUpdated()
28
+ }
29
+ // Update NavigationBar whenever the active scene or editor history changes.
30
+ remember(this, historyTrigger) {
31
+ NavigationBar.Scope(parentScope = this)
32
+ }
33
+ }
34
+ listBuilder = {
35
+ NavigationBar.ListBuilder.remember {
36
+ aligned(alignment = Alignment.Start) {
37
+ add { NavigationBar.Button.rememberCloseEditor() }
38
+ }
39
+ aligned(alignment = Alignment.End) {
40
+ add { NavigationBar.Button.rememberUndo() }
41
+ add { NavigationBar.Button.rememberRedo() }
42
+ add {
43
+ NavigationBar.Button.rememberExport {
44
+ enabled = {
45
+ // Show export button if the duration of scene is greater than 0
46
+ remember(this) {
47
+ editorContext.engine.scene.get()?.let {
48
+ editorContext.engine.block.getDuration(it) > 0
49
+ } ?: false
50
+ }
51
+ }
52
+ }
53
+ }
54
+ }
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,421 @@
1
+ @file:Suppress("UnusedReceiverParameter")
2
+
3
+ package ly.img.editor.configuration.video.component
4
+
5
+ import androidx.compose.animation.core.LinearEasing
6
+ import androidx.compose.animation.core.animateFloat
7
+ import androidx.compose.animation.core.animateFloatAsState
8
+ import androidx.compose.animation.core.infiniteRepeatable
9
+ import androidx.compose.animation.core.rememberInfiniteTransition
10
+ import androidx.compose.animation.core.tween
11
+ import androidx.compose.foundation.Canvas
12
+ import androidx.compose.foundation.background
13
+ import androidx.compose.foundation.gestures.detectTapGestures
14
+ import androidx.compose.foundation.layout.Arrangement
15
+ import androidx.compose.foundation.layout.Box
16
+ import androidx.compose.foundation.layout.Column
17
+ import androidx.compose.foundation.layout.Spacer
18
+ import androidx.compose.foundation.layout.fillMaxSize
19
+ import androidx.compose.foundation.layout.fillMaxWidth
20
+ import androidx.compose.foundation.layout.height
21
+ import androidx.compose.foundation.layout.navigationBarsPadding
22
+ import androidx.compose.foundation.layout.padding
23
+ import androidx.compose.foundation.layout.size
24
+ import androidx.compose.foundation.progressSemantics
25
+ import androidx.compose.foundation.shape.RoundedCornerShape
26
+ import androidx.compose.material3.AlertDialog
27
+ import androidx.compose.material3.ButtonDefaults
28
+ import androidx.compose.material3.Icon
29
+ import androidx.compose.material3.MaterialTheme
30
+ import androidx.compose.material3.ProgressIndicatorDefaults
31
+ import androidx.compose.material3.Surface
32
+ import androidx.compose.material3.Text
33
+ import androidx.compose.material3.TextButton
34
+ import androidx.compose.runtime.Composable
35
+ import androidx.compose.runtime.getValue
36
+ import androidx.compose.runtime.remember
37
+ import androidx.compose.ui.Alignment
38
+ import androidx.compose.ui.Modifier
39
+ import androidx.compose.ui.geometry.Offset
40
+ import androidx.compose.ui.geometry.Size
41
+ import androidx.compose.ui.graphics.Brush
42
+ import androidx.compose.ui.graphics.Color
43
+ import androidx.compose.ui.graphics.StrokeCap
44
+ import androidx.compose.ui.graphics.drawscope.DrawScope
45
+ import androidx.compose.ui.graphics.drawscope.Stroke
46
+ import androidx.compose.ui.graphics.drawscope.rotate
47
+ import androidx.compose.ui.input.pointer.pointerInput
48
+ import androidx.compose.ui.platform.LocalDensity
49
+ import androidx.compose.ui.res.stringResource
50
+ import androidx.compose.ui.text.style.TextAlign
51
+ import androidx.compose.ui.unit.Dp
52
+ import androidx.compose.ui.unit.dp
53
+ import androidx.compose.ui.window.DialogProperties
54
+ import ly.img.editor.configuration.video.VideoConfigurationBuilder
55
+ import ly.img.editor.configuration.video.iconPack.CheckCircleOutline
56
+ import ly.img.editor.configuration.video.iconPack.ErrorOutline
57
+ import ly.img.editor.configuration.video.iconPack.IconPack
58
+ import ly.img.editor.configuration.video.model.ExportStatus
59
+ import ly.img.editor.core.R
60
+ import ly.img.editor.core.component.EditorComponent
61
+ import ly.img.editor.core.component.remember
62
+ import ly.img.editor.core.event.EditorEvent
63
+ import ly.img.editor.core.theme.LocalExtendedColorScheme
64
+
65
+ /**
66
+ * The configuration of the component that is displayed over the editor. It is useful if you want to display a popup dialog or anything in the
67
+ * overlay.
68
+ */
69
+ @Composable
70
+ fun VideoConfigurationBuilder.rememberOverlay() = EditorComponent.remember {
71
+ decoration = {
72
+ // Other than the main overlay also display export related overlays
73
+ Overlay()
74
+ if (exportStatus != null) {
75
+ ExportOverlay()
76
+ }
77
+ }
78
+ }
79
+
80
+ /**
81
+ * The export related overlay composable.
82
+ *
83
+ * @param loading the export loading composable.
84
+ * @param success the export success composable.
85
+ * @param error the export error composable.
86
+ */
87
+ @Composable
88
+ fun VideoConfigurationBuilder.ExportOverlay(
89
+ loading: @Composable VideoConfigurationBuilder.(ExportStatus.Loading) -> Unit = {
90
+ ExportLoading(it)
91
+ },
92
+ success: @Composable VideoConfigurationBuilder.(ExportStatus.Success) -> Unit = {
93
+ ExportSuccess(it)
94
+ },
95
+ error: @Composable VideoConfigurationBuilder.(ExportStatus.Error) -> Unit = {
96
+ ExportError()
97
+ },
98
+ ) {
99
+ Box(
100
+ modifier = Modifier
101
+ .fillMaxSize()
102
+ .background(MaterialTheme.colorScheme.onSurface.copy(alpha = 0.32f))
103
+ .pointerInput(Unit) {
104
+ detectTapGestures {
105
+ // do nothing
106
+ }
107
+ },
108
+ ) {
109
+ Surface(
110
+ modifier = Modifier
111
+ .fillMaxWidth()
112
+ .align(Alignment.BottomStart),
113
+ shape = RoundedCornerShape(
114
+ topStart = 28.0.dp,
115
+ topEnd = 28.0.dp,
116
+ bottomEnd = 0.0.dp,
117
+ bottomStart = 0.0.dp,
118
+ ),
119
+ shadowElevation = 16.dp,
120
+ ) {
121
+ Box(
122
+ Modifier
123
+ .fillMaxWidth()
124
+ .navigationBarsPadding()
125
+ .padding(all = 16.dp),
126
+ ) {
127
+ val localExportStatus = exportStatus
128
+ when (localExportStatus) {
129
+ is ExportStatus.Loading -> loading(localExportStatus)
130
+ is ExportStatus.Success -> success(localExportStatus)
131
+ is ExportStatus.Error -> error(localExportStatus)
132
+ null -> {} // not possible
133
+ }
134
+ }
135
+ }
136
+ }
137
+ }
138
+
139
+ /**
140
+ * The export loading composable.
141
+ *
142
+ * @param status the current status of the export.
143
+ */
144
+ @Composable
145
+ fun VideoConfigurationBuilder.ExportLoading(status: ExportStatus.Loading) {
146
+ ExportOverlayContent(
147
+ title = stringResource(R.string.ly_img_editor_dialog_export_progress_title),
148
+ text = stringResource(R.string.ly_img_editor_dialog_export_progress_text),
149
+ button = stringResource(R.string.ly_img_editor_dialog_export_progress_button_dismiss),
150
+ buttonColor = MaterialTheme.colorScheme.error,
151
+ onClick = { showCancelExportConfirmationDialog = true },
152
+ centerContent = {
153
+ Box {
154
+ ExportProgressIndicator(progress = status.progress)
155
+ Text(
156
+ text = "${(status.progress * 100).toInt()}%",
157
+ style = MaterialTheme.typography.labelLarge,
158
+ color = MaterialTheme.colorScheme.onSurface,
159
+ modifier = Modifier.align(Alignment.Center),
160
+ )
161
+ }
162
+ },
163
+ )
164
+ if (showCancelExportConfirmationDialog) {
165
+ CancelExportConfirmationDialog()
166
+ }
167
+ }
168
+
169
+ /**
170
+ * The export success composable.
171
+ *
172
+ * @param status the current status of the export.
173
+ */
174
+ @Composable
175
+ fun VideoConfigurationBuilder.ExportSuccess(status: ExportStatus.Success) {
176
+ ExportOverlayContent(
177
+ title = stringResource(R.string.ly_img_editor_dialog_export_success_title),
178
+ text = stringResource(R.string.ly_img_editor_dialog_export_success_text),
179
+ button = stringResource(R.string.ly_img_editor_dialog_export_success_button_dismiss),
180
+ onClick = {
181
+ exportStatus = null
182
+ shareFile(file = status.file, mimeType = status.mimeType)
183
+ },
184
+ centerContent = {
185
+ Icon(
186
+ imageVector = IconPack.CheckCircleOutline,
187
+ contentDescription = null,
188
+ modifier = Modifier.size(144.dp),
189
+ tint = LocalExtendedColorScheme.current.green.color,
190
+ )
191
+ },
192
+ )
193
+ }
194
+
195
+ /**
196
+ * The error loading composable.
197
+ */
198
+ @Composable
199
+ fun VideoConfigurationBuilder.ExportError() {
200
+ ExportOverlayContent(
201
+ title = stringResource(R.string.ly_img_editor_dialog_export_error_title),
202
+ text = stringResource(R.string.ly_img_editor_dialog_export_error_text),
203
+ button = stringResource(R.string.ly_img_editor_dialog_export_error_button_dismiss),
204
+ onClick = { exportStatus = null },
205
+ centerContent = {
206
+ Icon(
207
+ imageVector = IconPack.ErrorOutline,
208
+ contentDescription = null,
209
+ modifier = Modifier.size(144.dp),
210
+ tint = MaterialTheme.colorScheme.error,
211
+ )
212
+ },
213
+ )
214
+ }
215
+
216
+ /**
217
+ * The confirmation dialog to cancel the export process.
218
+ */
219
+ @Composable
220
+ fun VideoConfigurationBuilder.CancelExportConfirmationDialog() {
221
+ AlertDialog(
222
+ icon = {
223
+ Icon(
224
+ imageVector = IconPack.ErrorOutline,
225
+ contentDescription = null,
226
+ tint = MaterialTheme.colorScheme.error,
227
+ )
228
+ },
229
+ title = {
230
+ Text(text = stringResource(R.string.ly_img_editor_dialog_export_cancel_title))
231
+ },
232
+ text = {
233
+ Text(text = stringResource(R.string.ly_img_editor_dialog_export_cancel_text))
234
+ },
235
+ confirmButton = {
236
+ TextButton(
237
+ onClick = {
238
+ showCancelExportConfirmationDialog = false
239
+ editorContext.eventHandler.send(EditorEvent.Export.Cancel())
240
+ },
241
+ colors = ButtonDefaults.textButtonColors(
242
+ containerColor = MaterialTheme.colorScheme.errorContainer,
243
+ contentColor = MaterialTheme.colorScheme.onErrorContainer,
244
+ ),
245
+ ) {
246
+ Text(stringResource(R.string.ly_img_editor_dialog_export_cancel_button_confirm))
247
+ }
248
+ },
249
+ dismissButton = {
250
+ TextButton(
251
+ onClick = {
252
+ showCancelExportConfirmationDialog = false
253
+ },
254
+ ) {
255
+ Text(stringResource(R.string.ly_img_editor_dialog_export_cancel_button_dismiss))
256
+ }
257
+ },
258
+ onDismissRequest = {
259
+ showCancelExportConfirmationDialog = false
260
+ },
261
+ properties = DialogProperties(dismissOnBackPress = false, dismissOnClickOutside = false),
262
+ )
263
+ }
264
+
265
+ /**
266
+ * Generic export overlay content that should be used by all the states of the [ExportStatus].
267
+ *
268
+ * @param modifier the modifier.
269
+ * @param title the title of the export state.
270
+ * @param text the title of the export state.
271
+ * @param button the action button of the export state.
272
+ */
273
+ @Composable
274
+ fun VideoConfigurationBuilder.ExportOverlayContent(
275
+ modifier: Modifier = Modifier.fillMaxWidth(),
276
+ title: String,
277
+ text: String,
278
+ button: String,
279
+ buttonColor: Color = MaterialTheme.colorScheme.primary,
280
+ onClick: () -> Unit,
281
+ centerContent: @Composable () -> Unit,
282
+ ) {
283
+ Column(
284
+ modifier = modifier,
285
+ verticalArrangement = Arrangement.Center,
286
+ horizontalAlignment = Alignment.CenterHorizontally,
287
+ ) {
288
+ Spacer(modifier = Modifier.height(24.dp))
289
+ centerContent()
290
+ Spacer(modifier = Modifier.height(24.dp))
291
+ Text(
292
+ text = title,
293
+ style = MaterialTheme.typography.headlineSmall,
294
+ color = MaterialTheme.colorScheme.onSurface,
295
+ textAlign = TextAlign.Center,
296
+ )
297
+ Spacer(modifier = Modifier.height(4.dp))
298
+ Text(
299
+ text = text,
300
+ style = MaterialTheme.typography.bodyMedium,
301
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
302
+ textAlign = TextAlign.Center,
303
+ )
304
+ Spacer(modifier = Modifier.height(24.dp))
305
+ TextButton(
306
+ onClick = onClick,
307
+ colors = ButtonDefaults.textButtonColors(
308
+ contentColor = buttonColor,
309
+ ),
310
+ ) {
311
+ Text(button)
312
+ }
313
+ }
314
+ }
315
+
316
+ /**
317
+ * Circular export progress indicator.
318
+ *
319
+ * @param progress the progress of the export, between 0F and 1F.
320
+ * @param fillColor the fill color of the circle.
321
+ * @param gradientColor the gradient color of the circle.
322
+ * @param strokeWidth the stroke width of the circle.
323
+ * @param strokeWidth the size (width/height) of the circle.
324
+ */
325
+ @Composable
326
+ fun VideoConfigurationBuilder.ExportProgressIndicator(
327
+ progress: Float,
328
+ fillColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
329
+ gradientColor: Color = MaterialTheme.colorScheme.onSurface,
330
+ strokeWidth: Dp = 8.dp,
331
+ size: Dp = 144.dp,
332
+ ) {
333
+ val animatedProgress by animateFloatAsState(
334
+ targetValue = progress,
335
+ animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec,
336
+ )
337
+
338
+ val stroke = with(LocalDensity.current) {
339
+ Stroke(width = strokeWidth.toPx(), cap = StrokeCap.Round)
340
+ }
341
+
342
+ val transition = rememberInfiniteTransition()
343
+ val rotationAngle by transition.animateFloat(
344
+ initialValue = 0f,
345
+ targetValue = 360f,
346
+ animationSpec = infiniteRepeatable(
347
+ animation = tween(1500, easing = LinearEasing),
348
+ ),
349
+ label = "Gradient Rotation",
350
+ )
351
+
352
+ val sweepGradient = remember(gradientColor) {
353
+ Brush.sweepGradient(
354
+ colorStops = listOf(
355
+ 0.02f to Color.Transparent,
356
+ 0.5f to gradientColor.copy(alpha = 0.24f),
357
+ 1f to Color.Transparent,
358
+ ).toTypedArray(),
359
+ )
360
+ }
361
+
362
+ Canvas(
363
+ modifier = Modifier
364
+ .progressSemantics(animatedProgress)
365
+ .size(size - strokeWidth * 2),
366
+ ) {
367
+ drawSweepGradientCircularIndicator(
368
+ rotationAngle = rotationAngle,
369
+ stroke = stroke,
370
+ gradientBrush = sweepGradient,
371
+ )
372
+
373
+ drawCircularIndicator(
374
+ startAngle = 270f,
375
+ sweep = animatedProgress * 360f,
376
+ color = fillColor,
377
+ stroke = stroke,
378
+ )
379
+ }
380
+ }
381
+
382
+ private fun DrawScope.drawSweepGradientCircularIndicator(
383
+ rotationAngle: Float,
384
+ stroke: Stroke,
385
+ gradientBrush: Brush,
386
+ ) {
387
+ rotate(-180f + rotationAngle) {
388
+ val diameterOffset = stroke.width / 2
389
+ val arcDimen = size.width - 2 * diameterOffset
390
+ drawArc(
391
+ brush = gradientBrush,
392
+ startAngle = 0f,
393
+ sweepAngle = 180f,
394
+ useCenter = false,
395
+ topLeft = Offset(diameterOffset, diameterOffset),
396
+ style = stroke,
397
+ size = Size(arcDimen, arcDimen),
398
+ )
399
+ }
400
+ }
401
+
402
+ private fun DrawScope.drawCircularIndicator(
403
+ startAngle: Float,
404
+ sweep: Float,
405
+ color: Color,
406
+ stroke: Stroke,
407
+ ) {
408
+ // To draw this circle we need a rect with edges that line up with the midpoint of the stroke.
409
+ // To do this we need to remove half the stroke width from the total diameter for both sides.
410
+ val diameterOffset = stroke.width / 2
411
+ val arcDimen = size.width - 2 * diameterOffset
412
+ drawArc(
413
+ color = color,
414
+ startAngle = startAngle,
415
+ sweepAngle = sweep,
416
+ useCenter = false,
417
+ topLeft = Offset(diameterOffset, diameterOffset),
418
+ size = Size(arcDimen, arcDimen),
419
+ style = stroke,
420
+ )
421
+ }