@imgly/editor-react-native 1.80.0-rc.1 → 1.81.0-rc.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/android/build.gradle +1 -1
- package/android/src/main/assets/ly.img.memories.style/content.json +50 -0
- package/android/src/main/assets/ly.img.memories.style/thumbnails/bubblegum.png +0 -0
- package/android/src/main/assets/ly.img.memories.style/thumbnails/hologram.png +0 -0
- package/android/src/main/assets/ly.img.memories.style/thumbnails/noir.png +0 -0
- package/android/src/main/assets/ly.img.memories.style/videos/bubblegum.mp4 +0 -0
- package/android/src/main/assets/ly.img.memories.style/videos/hologram.mp4 +0 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/MemoriesApp.kt +77 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/MemoriesConfiguration.kt +38 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/MemoriesEditor.kt +79 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/MemoriesViewModel.kt +469 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/callback/OnCreate.kt +69 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/callback/OnExport.kt +46 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/component/BottomPanel.kt +151 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/component/Dock.kt +145 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/component/ExportOverlay.kt +229 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/component/NavigationBar.kt +80 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/component/Overlay.kt +138 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/extension/EditorScopeExt.kt +111 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/AddPhotoAlternate.kt +79 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/ArrowForward.kt +45 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/Audiotrack.kt +43 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/Check.kt +39 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/CheckCircle.kt +44 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/Close.kt +43 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/Delete.kt +54 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/DeleteOutline.kt +53 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/Fullscreen.kt +74 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/Image.kt +51 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/Info.kt +48 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/Pause.kt +46 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/PlayArrow.kt +38 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/RepeatOff.kt +80 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/RepeatOn.kt +59 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/SkipPrevious.kt +46 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/Sort.kt +55 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/Style.kt +59 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/Title.kt +43 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/TouchTriple.kt +134 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/VolumeDown.kt +48 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/VolumeUp.kt +60 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/model/ExportStatus.kt +20 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/model/ImageItem.kt +8 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/model/TimelineImage.kt +18 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/scene/Events.kt +59 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/scene/Playback.kt +29 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/scene/SceneSetup.kt +79 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/scene/Timeline.kt +27 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/scene/Title.kt +162 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/scene/TrackEditor.kt +740 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/screen/ImageSelectionScreen.kt +387 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/screen/LoadingScreen.kt +258 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/sheet/FilterStylesSheet.kt +167 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/sheet/FilterStylesSheetType.kt +21 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/sheet/VolumeSheet.kt +104 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/style/StyleApplier.kt +220 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/style/StyleAssetSource.kt +43 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/style/VideoStyle.kt +118 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/util/Animations.kt +58 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/util/Constants.kt +20 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/util/ImageDate.kt +81 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/util/Utils.kt +27 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/widget/CenteredAddImageButton.kt +82 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/widget/DraggableImageItem.kt +145 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/widget/FullscreenVideoControls.kt +136 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/widget/ImageGrid.kt +122 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/widget/ImageGridOverlay.kt +149 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/widget/ImageTimelineStrip.kt +323 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/widget/ImageToolbar.kt +401 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/widget/ReorderableGridState.kt +183 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/widget/VideoControlPanel.kt +180 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/widget/VideoThumbnail.kt +110 -0
- package/android/src/main/kotlin/ly/img/editor/configuration/video/callback/OnCreate.kt +0 -7
- package/android/src/main/kotlin/ly/img/editor/configuration/video/component/InspectorBar.kt +2 -0
- package/ios/StarterKits/Video/callbacks/OnCreate+Video.swift +1 -1
- package/ios/StarterKits/Video/components/Dock+Video.swift +1 -0
- package/ios/StarterKits/Video/components/InspectorBar+Video.swift +4 -0
- package/package.json +1 -1
- package/src/NativeIMGLYEditor.ts +1 -1
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
package ly.img.editor.configuration.memories.iconPack
|
|
2
|
+
|
|
3
|
+
import androidx.compose.ui.graphics.Color
|
|
4
|
+
import androidx.compose.ui.graphics.PathFillType
|
|
5
|
+
import androidx.compose.ui.graphics.SolidColor
|
|
6
|
+
import androidx.compose.ui.graphics.StrokeCap
|
|
7
|
+
import androidx.compose.ui.graphics.StrokeJoin
|
|
8
|
+
import androidx.compose.ui.graphics.vector.ImageVector
|
|
9
|
+
import androidx.compose.ui.graphics.vector.path
|
|
10
|
+
import androidx.compose.ui.unit.dp
|
|
11
|
+
import ly.img.editor.core.iconpack.IconPack
|
|
12
|
+
|
|
13
|
+
// Material Symbols Rounded "touch_triple" (filled) — used for the multi-select toggle.
|
|
14
|
+
val IconPack.TouchTriple: ImageVector
|
|
15
|
+
get() {
|
|
16
|
+
if (touchTriple != null) return touchTriple!!
|
|
17
|
+
touchTriple = ImageVector.Builder(
|
|
18
|
+
name = "touch_triple",
|
|
19
|
+
defaultWidth = 24.dp,
|
|
20
|
+
defaultHeight = 24.dp,
|
|
21
|
+
viewportWidth = 24f,
|
|
22
|
+
viewportHeight = 24f,
|
|
23
|
+
).apply {
|
|
24
|
+
path(
|
|
25
|
+
fill = SolidColor(Color.Black),
|
|
26
|
+
fillAlpha = 1f,
|
|
27
|
+
stroke = null,
|
|
28
|
+
strokeAlpha = 1f,
|
|
29
|
+
strokeLineWidth = 1f,
|
|
30
|
+
strokeLineCap = StrokeCap.Butt,
|
|
31
|
+
strokeLineJoin = StrokeJoin.Bevel,
|
|
32
|
+
strokeLineMiter = 1f,
|
|
33
|
+
pathFillType = PathFillType.NonZero,
|
|
34
|
+
) {
|
|
35
|
+
moveTo(19.95f, 9f)
|
|
36
|
+
quadTo(19.53f, 9f, 19.25f, 8.71f)
|
|
37
|
+
quadTo(18.98f, 8.42f, 18.98f, 8f)
|
|
38
|
+
quadToRelative(0f, -0.1f, 0.13f, -0.5f)
|
|
39
|
+
quadTo(19.3f, 7.15f, 19.4f, 6.77f)
|
|
40
|
+
reflectiveQuadTo(19.5f, 6f)
|
|
41
|
+
quadToRelative(0f, -0.5f, -0.14f, -0.99f)
|
|
42
|
+
reflectiveQuadTo(18.88f, 4.15f)
|
|
43
|
+
quadTo(18.73f, 3.97f, 18.63f, 3.76f)
|
|
44
|
+
quadTo(18.53f, 3.55f, 18.53f, 3.32f)
|
|
45
|
+
quadToRelative(0f, -0.38f, 0.26f, -0.65f)
|
|
46
|
+
reflectiveQuadTo(19.43f, 2.4f)
|
|
47
|
+
quadToRelative(0.32f, 0f, 0.59f, 0.16f)
|
|
48
|
+
quadToRelative(0.26f, 0.16f, 0.46f, 0.41f)
|
|
49
|
+
quadToRelative(0.55f, 0.63f, 0.79f, 1.41f)
|
|
50
|
+
reflectiveQuadTo(21.5f, 6f)
|
|
51
|
+
quadToRelative(0f, 0.65f, -0.16f, 1.29f)
|
|
52
|
+
quadTo(21.18f, 7.93f, 20.83f, 8.5f)
|
|
53
|
+
quadTo(20.7f, 8.73f, 20.46f, 8.86f)
|
|
54
|
+
reflectiveQuadTo(19.95f, 9f)
|
|
55
|
+
close()
|
|
56
|
+
moveToRelative(-4f, 0f)
|
|
57
|
+
quadTo(15.53f, 9f, 15.25f, 8.71f)
|
|
58
|
+
reflectiveQuadTo(14.98f, 8f)
|
|
59
|
+
quadToRelative(0f, -0.1f, 0.13f, -0.5f)
|
|
60
|
+
quadTo(15.3f, 7.15f, 15.4f, 6.77f)
|
|
61
|
+
reflectiveQuadTo(15.5f, 6f)
|
|
62
|
+
quadToRelative(0f, -0.5f, -0.14f, -0.99f)
|
|
63
|
+
reflectiveQuadTo(14.88f, 4.15f)
|
|
64
|
+
quadTo(14.73f, 3.97f, 14.63f, 3.76f)
|
|
65
|
+
quadTo(14.53f, 3.55f, 14.53f, 3.32f)
|
|
66
|
+
quadToRelative(0f, -0.38f, 0.26f, -0.65f)
|
|
67
|
+
reflectiveQuadTo(15.43f, 2.4f)
|
|
68
|
+
quadToRelative(0.32f, 0f, 0.59f, 0.16f)
|
|
69
|
+
quadToRelative(0.26f, 0.16f, 0.46f, 0.41f)
|
|
70
|
+
quadToRelative(0.55f, 0.63f, 0.79f, 1.41f)
|
|
71
|
+
reflectiveQuadTo(17.5f, 6f)
|
|
72
|
+
quadToRelative(0f, 0.65f, -0.16f, 1.29f)
|
|
73
|
+
quadTo(17.18f, 7.93f, 16.83f, 8.5f)
|
|
74
|
+
quadTo(16.7f, 8.73f, 16.46f, 8.86f)
|
|
75
|
+
reflectiveQuadTo(15.95f, 9f)
|
|
76
|
+
close()
|
|
77
|
+
moveTo(10.48f, 22f)
|
|
78
|
+
quadTo(9.78f, 22f, 9.16f, 21.7f)
|
|
79
|
+
reflectiveQuadTo(8.13f, 20.85f)
|
|
80
|
+
lineTo(3.1f, 14.48f)
|
|
81
|
+
quadTo(2.9f, 14.25f, 2.93f, 13.94f)
|
|
82
|
+
reflectiveQuadTo(3.15f, 13.43f)
|
|
83
|
+
quadTo(3.65f, 12.9f, 4.35f, 12.8f)
|
|
84
|
+
reflectiveQuadToRelative(1.3f, 0.28f)
|
|
85
|
+
lineTo(7.5f, 14.2f)
|
|
86
|
+
verticalLineTo(6f)
|
|
87
|
+
quadTo(7.5f, 5.57f, 7.79f, 5.29f)
|
|
88
|
+
reflectiveQuadTo(8.5f, 5f)
|
|
89
|
+
quadTo(8.93f, 5f, 9.23f, 5.29f)
|
|
90
|
+
quadTo(9.53f, 5.57f, 9.53f, 6f)
|
|
91
|
+
verticalLineToRelative(5f)
|
|
92
|
+
horizontalLineTo(17f)
|
|
93
|
+
quadToRelative(1.25f, 0f, 2.13f, 0.88f)
|
|
94
|
+
reflectiveQuadTo(20f, 14f)
|
|
95
|
+
verticalLineToRelative(4f)
|
|
96
|
+
quadToRelative(0f, 1.65f, -1.18f, 2.82f)
|
|
97
|
+
reflectiveQuadTo(16f, 22f)
|
|
98
|
+
horizontalLineTo(10.48f)
|
|
99
|
+
close()
|
|
100
|
+
moveTo(11.98f, 9f)
|
|
101
|
+
quadTo(11.55f, 9f, 11.26f, 8.71f)
|
|
102
|
+
reflectiveQuadTo(10.98f, 8f)
|
|
103
|
+
quadToRelative(0f, -0.05f, 0.13f, -0.5f)
|
|
104
|
+
quadTo(11.3f, 7.15f, 11.4f, 6.79f)
|
|
105
|
+
quadTo(11.5f, 6.43f, 11.5f, 6f)
|
|
106
|
+
quadToRelative(0f, -1.25f, -0.88f, -2.13f)
|
|
107
|
+
reflectiveQuadTo(8.5f, 3f)
|
|
108
|
+
reflectiveQuadTo(6.38f, 3.88f)
|
|
109
|
+
reflectiveQuadTo(5.5f, 6f)
|
|
110
|
+
quadToRelative(0f, 0.43f, 0.1f, 0.79f)
|
|
111
|
+
reflectiveQuadTo(5.9f, 7.5f)
|
|
112
|
+
quadTo(5.98f, 7.63f, 6f, 7.75f)
|
|
113
|
+
reflectiveQuadTo(6.03f, 8f)
|
|
114
|
+
quadToRelative(0f, 0.42f, -0.28f, 0.71f)
|
|
115
|
+
reflectiveQuadTo(5.05f, 9f)
|
|
116
|
+
quadTo(4.78f, 9f, 4.54f, 8.85f)
|
|
117
|
+
reflectiveQuadTo(4.18f, 8.48f)
|
|
118
|
+
quadTo(3.85f, 7.93f, 3.68f, 7.3f)
|
|
119
|
+
reflectiveQuadTo(3.5f, 6f)
|
|
120
|
+
quadTo(3.5f, 3.92f, 4.96f, 2.46f)
|
|
121
|
+
reflectiveQuadTo(8.5f, 1f)
|
|
122
|
+
reflectiveQuadToRelative(3.54f, 1.46f)
|
|
123
|
+
reflectiveQuadTo(13.5f, 6f)
|
|
124
|
+
quadToRelative(0f, 0.68f, -0.17f, 1.3f)
|
|
125
|
+
reflectiveQuadToRelative(-0.5f, 1.17f)
|
|
126
|
+
quadTo(12.7f, 8.7f, 12.48f, 8.85f)
|
|
127
|
+
reflectiveQuadTo(11.98f, 9f)
|
|
128
|
+
close()
|
|
129
|
+
}
|
|
130
|
+
}.build()
|
|
131
|
+
return touchTriple!!
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private var touchTriple: ImageVector? = null
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
package ly.img.editor.configuration.memories.iconPack
|
|
2
|
+
|
|
3
|
+
import androidx.compose.ui.graphics.Color
|
|
4
|
+
import androidx.compose.ui.graphics.SolidColor
|
|
5
|
+
import androidx.compose.ui.graphics.vector.ImageVector
|
|
6
|
+
import androidx.compose.ui.graphics.vector.path
|
|
7
|
+
import androidx.compose.ui.unit.dp
|
|
8
|
+
import ly.img.editor.core.iconpack.IconPack
|
|
9
|
+
|
|
10
|
+
val IconPack.VolumeDown: ImageVector
|
|
11
|
+
get() {
|
|
12
|
+
if (_VolumeDown != null) {
|
|
13
|
+
return _VolumeDown!!
|
|
14
|
+
}
|
|
15
|
+
_VolumeDown = ImageVector.Builder(
|
|
16
|
+
name = "VolumeDown",
|
|
17
|
+
defaultWidth = 24.dp,
|
|
18
|
+
defaultHeight = 24.dp,
|
|
19
|
+
viewportWidth = 24f,
|
|
20
|
+
viewportHeight = 24f,
|
|
21
|
+
autoMirror = true,
|
|
22
|
+
).apply {
|
|
23
|
+
path(fill = SolidColor(Color.Black)) {
|
|
24
|
+
moveTo(18.5f, 12f)
|
|
25
|
+
arcTo(4.5f, 4.5f, 0f, isMoreThanHalf = false, isPositiveArc = false, 16f, 7.97f)
|
|
26
|
+
verticalLineToRelative(8.05f)
|
|
27
|
+
curveToRelative(1.48f, -0.73f, 2.5f, -2.25f, 2.5f, -4.02f)
|
|
28
|
+
close()
|
|
29
|
+
moveTo(5f, 10f)
|
|
30
|
+
verticalLineToRelative(4f)
|
|
31
|
+
curveToRelative(0f, 0.55f, 0.45f, 1f, 1f, 1f)
|
|
32
|
+
horizontalLineToRelative(3f)
|
|
33
|
+
lineToRelative(3.29f, 3.29f)
|
|
34
|
+
curveToRelative(0.63f, 0.63f, 1.71f, 0.18f, 1.71f, -0.71f)
|
|
35
|
+
verticalLineTo(6.41f)
|
|
36
|
+
curveToRelative(0f, -0.89f, -1.08f, -1.34f, -1.71f, -0.71f)
|
|
37
|
+
lineTo(9f, 9f)
|
|
38
|
+
horizontalLineTo(6f)
|
|
39
|
+
curveToRelative(-0.55f, 0f, -1f, 0.45f, -1f, 1f)
|
|
40
|
+
close()
|
|
41
|
+
}
|
|
42
|
+
}.build()
|
|
43
|
+
|
|
44
|
+
return _VolumeDown!!
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@Suppress("ObjectPropertyName")
|
|
48
|
+
private var _VolumeDown: ImageVector? = null
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
package ly.img.editor.configuration.memories.iconPack
|
|
2
|
+
|
|
3
|
+
import androidx.compose.ui.graphics.Color
|
|
4
|
+
import androidx.compose.ui.graphics.SolidColor
|
|
5
|
+
import androidx.compose.ui.graphics.vector.ImageVector
|
|
6
|
+
import androidx.compose.ui.graphics.vector.path
|
|
7
|
+
import androidx.compose.ui.unit.dp
|
|
8
|
+
import ly.img.editor.core.iconpack.IconPack
|
|
9
|
+
|
|
10
|
+
val IconPack.VolumeUp: ImageVector
|
|
11
|
+
get() {
|
|
12
|
+
if (_VolumeUp != null) {
|
|
13
|
+
return _VolumeUp!!
|
|
14
|
+
}
|
|
15
|
+
_VolumeUp = ImageVector.Builder(
|
|
16
|
+
name = "VolumeUp",
|
|
17
|
+
defaultWidth = 24.dp,
|
|
18
|
+
defaultHeight = 24.dp,
|
|
19
|
+
viewportWidth = 24f,
|
|
20
|
+
viewportHeight = 24f,
|
|
21
|
+
autoMirror = true,
|
|
22
|
+
).apply {
|
|
23
|
+
path(fill = SolidColor(Color.Black)) {
|
|
24
|
+
moveTo(3f, 10f)
|
|
25
|
+
verticalLineToRelative(4f)
|
|
26
|
+
curveToRelative(0f, 0.55f, 0.45f, 1f, 1f, 1f)
|
|
27
|
+
horizontalLineToRelative(3f)
|
|
28
|
+
lineToRelative(3.29f, 3.29f)
|
|
29
|
+
curveToRelative(0.63f, 0.63f, 1.71f, 0.18f, 1.71f, -0.71f)
|
|
30
|
+
lineTo(12f, 6.41f)
|
|
31
|
+
curveToRelative(0f, -0.89f, -1.08f, -1.34f, -1.71f, -0.71f)
|
|
32
|
+
lineTo(7f, 9f)
|
|
33
|
+
lineTo(4f, 9f)
|
|
34
|
+
curveToRelative(-0.55f, 0f, -1f, 0.45f, -1f, 1f)
|
|
35
|
+
close()
|
|
36
|
+
moveTo(16.5f, 12f)
|
|
37
|
+
arcTo(4.5f, 4.5f, 0f, isMoreThanHalf = false, isPositiveArc = false, 14f, 7.97f)
|
|
38
|
+
verticalLineToRelative(8.05f)
|
|
39
|
+
curveToRelative(1.48f, -0.73f, 2.5f, -2.25f, 2.5f, -4.02f)
|
|
40
|
+
close()
|
|
41
|
+
moveTo(14f, 4.45f)
|
|
42
|
+
verticalLineToRelative(0.2f)
|
|
43
|
+
curveToRelative(0f, 0.38f, 0.25f, 0.71f, 0.6f, 0.85f)
|
|
44
|
+
curveTo(17.18f, 6.53f, 19f, 9.06f, 19f, 12f)
|
|
45
|
+
reflectiveCurveToRelative(-1.82f, 5.47f, -4.4f, 6.5f)
|
|
46
|
+
curveToRelative(-0.36f, 0.14f, -0.6f, 0.47f, -0.6f, 0.85f)
|
|
47
|
+
verticalLineToRelative(0.2f)
|
|
48
|
+
curveToRelative(0f, 0.63f, 0.63f, 1.07f, 1.21f, 0.85f)
|
|
49
|
+
curveTo(18.6f, 19.11f, 21f, 15.84f, 21f, 12f)
|
|
50
|
+
reflectiveCurveToRelative(-2.4f, -7.11f, -5.79f, -8.4f)
|
|
51
|
+
curveToRelative(-0.58f, -0.23f, -1.21f, 0.22f, -1.21f, 0.85f)
|
|
52
|
+
close()
|
|
53
|
+
}
|
|
54
|
+
}.build()
|
|
55
|
+
|
|
56
|
+
return _VolumeUp!!
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@Suppress("ObjectPropertyName")
|
|
60
|
+
private var _VolumeUp: ImageVector? = null
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
package ly.img.editor.configuration.memories.model
|
|
2
|
+
|
|
3
|
+
import ly.img.engine.MimeType
|
|
4
|
+
import java.io.File
|
|
5
|
+
|
|
6
|
+
/** The state of the MP4 export, surfaced as a progress overlay. */
|
|
7
|
+
sealed interface ExportStatus {
|
|
8
|
+
data class Loading(
|
|
9
|
+
val progress: Float,
|
|
10
|
+
) : ExportStatus
|
|
11
|
+
|
|
12
|
+
data class Success(
|
|
13
|
+
val file: File,
|
|
14
|
+
val mimeType: MimeType = MimeType.MP4,
|
|
15
|
+
) : ExportStatus
|
|
16
|
+
|
|
17
|
+
data class Error(
|
|
18
|
+
val exception: Exception,
|
|
19
|
+
) : ExportStatus
|
|
20
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
package ly.img.editor.configuration.memories.model
|
|
2
|
+
|
|
3
|
+
data class ImageItem(
|
|
4
|
+
val id: Int,
|
|
5
|
+
val url: String,
|
|
6
|
+
val creationDate: Long? = null, // Timestamp in milliseconds since epoch, null for stock images
|
|
7
|
+
val isVideo: Boolean = false, // true when [url] points to a video; placed as a trimmed video clip
|
|
8
|
+
)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
package ly.img.editor.configuration.memories.model
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* An image as it sits on the video timeline, used by the bottom-panel thumbnail strip.
|
|
5
|
+
*
|
|
6
|
+
* @param url the image fill URI
|
|
7
|
+
* @param startTime when the image block starts on the timeline (seconds)
|
|
8
|
+
* @param endTime when the image block ends (seconds)
|
|
9
|
+
* @param fullyVisibleTime the moment the image is fully visible — right after its enter
|
|
10
|
+
* (Ken Burns) animation completes — which is where tapping the thumbnail seeks to.
|
|
11
|
+
*/
|
|
12
|
+
data class TimelineImage(
|
|
13
|
+
val url: String,
|
|
14
|
+
val startTime: Double,
|
|
15
|
+
val endTime: Double,
|
|
16
|
+
val fullyVisibleTime: Double,
|
|
17
|
+
val isVideo: Boolean = false,
|
|
18
|
+
)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
package ly.img.editor.configuration.memories.scene
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import kotlinx.coroutines.delay
|
|
5
|
+
import kotlinx.coroutines.flow.first
|
|
6
|
+
import kotlinx.coroutines.launch
|
|
7
|
+
import ly.img.editor.configuration.memories.MemoriesViewModel
|
|
8
|
+
import ly.img.editor.core.EditorContext
|
|
9
|
+
import ly.img.engine.DesignBlockEvent
|
|
10
|
+
import ly.img.engine.Engine
|
|
11
|
+
|
|
12
|
+
private const val TAG = "MemoriesEvents"
|
|
13
|
+
|
|
14
|
+
private const val AUDIO_TYPE = "//ly.img.ubq/audio"
|
|
15
|
+
private const val MIN_LOAD_MILLIS = 2000L
|
|
16
|
+
|
|
17
|
+
internal fun setupEventSubscriptions(
|
|
18
|
+
engine: Engine,
|
|
19
|
+
editorContext: EditorContext,
|
|
20
|
+
viewModel: MemoriesViewModel,
|
|
21
|
+
) {
|
|
22
|
+
subscribeMinimumLoad(editorContext, viewModel)
|
|
23
|
+
subscribeSingleAudioTrack(engine, editorContext)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Hold the loading screen for a minimum time so it doesn't flash. */
|
|
27
|
+
private fun subscribeMinimumLoad(
|
|
28
|
+
editorContext: EditorContext,
|
|
29
|
+
viewModel: MemoriesViewModel,
|
|
30
|
+
) {
|
|
31
|
+
editorContext.coroutineScope.launch {
|
|
32
|
+
val elapsed = System.currentTimeMillis() - viewModel.loadingStartTime.first()
|
|
33
|
+
if (elapsed < MIN_LOAD_MILLIS) delay(MIN_LOAD_MILLIS - elapsed)
|
|
34
|
+
viewModel.setEditorLoading(false)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Keep a single audio track: destroy older audio blocks when a new one is added. */
|
|
39
|
+
private fun subscribeSingleAudioTrack(
|
|
40
|
+
engine: Engine,
|
|
41
|
+
editorContext: EditorContext,
|
|
42
|
+
) {
|
|
43
|
+
editorContext.coroutineScope.launch {
|
|
44
|
+
engine.event.subscribe(listOf()).collect { events ->
|
|
45
|
+
events.filter { it.type == DesignBlockEvent.Type.CREATED }
|
|
46
|
+
.filter { runCatching { engine.block.getType(it.block) == AUDIO_TYPE }.getOrDefault(false) }
|
|
47
|
+
.forEach { event ->
|
|
48
|
+
try {
|
|
49
|
+
val parent = engine.block.getParent(event.block) ?: return@forEach
|
|
50
|
+
engine.block.getChildren(parent)
|
|
51
|
+
.filter { it != event.block && engine.block.getType(it) == AUDIO_TYPE }
|
|
52
|
+
.forEach { engine.block.destroy(it) }
|
|
53
|
+
} catch (e: Exception) {
|
|
54
|
+
Log.w(TAG, "Could not enforce single audio track", e)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
package ly.img.editor.configuration.memories.scene
|
|
2
|
+
|
|
3
|
+
import ly.img.engine.Engine
|
|
4
|
+
|
|
5
|
+
/** Playback engine ops on the slideshow page. The ViewModel holds the matching UI state. */
|
|
6
|
+
|
|
7
|
+
/** Flip the page's loop setting and return the new value (null if there's no page yet). */
|
|
8
|
+
internal fun togglePageLoop(engine: Engine): Boolean? {
|
|
9
|
+
val page = engine.scene.getCurrentPage() ?: return null
|
|
10
|
+
val next = !engine.block.isLooping(page)
|
|
11
|
+
engine.block.setLooping(page, next)
|
|
12
|
+
return next
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
internal fun setPageLooping(
|
|
16
|
+
engine: Engine,
|
|
17
|
+
looping: Boolean,
|
|
18
|
+
) {
|
|
19
|
+
engine.scene.getCurrentPage()?.let { engine.block.setLooping(it, looping) }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
internal fun setPageVolume(
|
|
23
|
+
engine: Engine,
|
|
24
|
+
volume: Float,
|
|
25
|
+
) {
|
|
26
|
+
engine.scene.getCurrentPage()?.let { engine.block.setVolume(it, volume) }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
internal fun getPageVolume(engine: Engine): Float? = engine.scene.getCurrentPage()?.let { engine.block.getVolume(it) }
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
package ly.img.editor.configuration.memories.scene
|
|
2
|
+
|
|
3
|
+
import ly.img.editor.configuration.memories.util.PAGE_HEIGHT
|
|
4
|
+
import ly.img.editor.configuration.memories.util.PAGE_WIDTH
|
|
5
|
+
import ly.img.engine.Color
|
|
6
|
+
import ly.img.engine.DesignBlock
|
|
7
|
+
import ly.img.engine.DesignBlockType
|
|
8
|
+
import ly.img.engine.Engine
|
|
9
|
+
import ly.img.engine.FillType
|
|
10
|
+
import ly.img.engine.ShapeType
|
|
11
|
+
|
|
12
|
+
/** Build the slideshow scene in code (no serialized blob): a video scene with one sized page. */
|
|
13
|
+
internal fun createSlideshowScene(engine: Engine): DesignBlock {
|
|
14
|
+
val scene = engine.scene.createForVideo()
|
|
15
|
+
val page = engine.block.create(DesignBlockType.Page)
|
|
16
|
+
engine.block.appendChild(parent = scene, child = page)
|
|
17
|
+
engine.block.setWidth(page, PAGE_WIDTH)
|
|
18
|
+
engine.block.setHeight(page, PAGE_HEIGHT)
|
|
19
|
+
|
|
20
|
+
val fill = engine.block.createFill(FillType.Color)
|
|
21
|
+
engine.block.setColor(fill, "fill/color/value", Color.fromRGBA(0f, 0f, 0f, 1f))
|
|
22
|
+
engine.block.setFill(page, fill)
|
|
23
|
+
return page
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The persistent, single-purpose tracks of the slideshow, bottom-to-top in render order. The per-
|
|
28
|
+
* slide media tracks are created later (in [createMainImageSequence]) and stack on top of these.
|
|
29
|
+
*/
|
|
30
|
+
internal data class TrackReferences(
|
|
31
|
+
val textTrack: DesignBlock,
|
|
32
|
+
val backgroundTrack: DesignBlock,
|
|
33
|
+
val backgroundBlock: DesignBlock,
|
|
34
|
+
val matteTrack: DesignBlock,
|
|
35
|
+
val matteBlock: DesignBlock,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
internal fun setupTracks(
|
|
39
|
+
engine: Engine,
|
|
40
|
+
page: DesignBlock,
|
|
41
|
+
): TrackReferences {
|
|
42
|
+
// background = per-style backdrop (hidden by default); matte = black rectangle so a crossfade
|
|
43
|
+
// fades to black instead of revealing the backdrop. Appended first → they render behind the
|
|
44
|
+
// media. The text (title) track sits just above them; the media tracks are appended on top later.
|
|
45
|
+
val backgroundTrack = engine.block.create(DesignBlockType.Track)
|
|
46
|
+
val matteTrack = engine.block.create(DesignBlockType.Track)
|
|
47
|
+
val textTrack = engine.block.create(DesignBlockType.Track)
|
|
48
|
+
|
|
49
|
+
// Tags let the rest of the kit re-locate tracks by role instead of retaining stale block ids.
|
|
50
|
+
tagSlideshowTracks(engine, textTrack = textTrack, backgroundTrack = backgroundTrack, matteTrack = matteTrack)
|
|
51
|
+
|
|
52
|
+
engine.block.appendChild(parent = page, child = backgroundTrack)
|
|
53
|
+
engine.block.appendChild(parent = page, child = matteTrack)
|
|
54
|
+
engine.block.appendChild(parent = page, child = textTrack)
|
|
55
|
+
|
|
56
|
+
val backgroundBlock = fullPageBlock(engine, page)
|
|
57
|
+
engine.block.setVisible(backgroundBlock, false)
|
|
58
|
+
engine.block.appendChild(parent = backgroundTrack, child = backgroundBlock)
|
|
59
|
+
|
|
60
|
+
val matteBlock = fullPageBlock(engine, page)
|
|
61
|
+
val matteFill = engine.block.createFill(FillType.Color)
|
|
62
|
+
engine.block.setColor(matteFill, "fill/color/value", Color.fromRGBA(0f, 0f, 0f, 1f))
|
|
63
|
+
engine.block.setFill(matteBlock, matteFill)
|
|
64
|
+
engine.block.appendChild(parent = matteTrack, child = matteBlock)
|
|
65
|
+
|
|
66
|
+
return TrackReferences(textTrack, backgroundTrack, backgroundBlock, matteTrack, matteBlock)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private fun fullPageBlock(
|
|
70
|
+
engine: Engine,
|
|
71
|
+
page: DesignBlock,
|
|
72
|
+
): DesignBlock {
|
|
73
|
+
val block = engine.block.create(DesignBlockType.Graphic)
|
|
74
|
+
engine.block.setScopeEnabled(block, "editor/select", false)
|
|
75
|
+
engine.block.setShape(block, engine.block.createShape(ShapeType.Rect))
|
|
76
|
+
engine.block.setWidth(block, engine.block.getWidth(page))
|
|
77
|
+
engine.block.setHeight(block, engine.block.getHeight(page))
|
|
78
|
+
return block
|
|
79
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
package ly.img.editor.configuration.memories.scene
|
|
2
|
+
|
|
3
|
+
import ly.img.editor.configuration.memories.model.ImageItem
|
|
4
|
+
import ly.img.editor.configuration.memories.util.AnimationPair
|
|
5
|
+
import ly.img.engine.Engine
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Lay the photos/clips out, one per track. Each track is appended on top of the previous one so a
|
|
9
|
+
* later slide always renders above the earlier one; the clips (videos included) are created first so
|
|
10
|
+
* their final durations are known, then every track is positioned in time by [repositionTracks] —
|
|
11
|
+
* consecutive slides overlap for the crossfade, a short video takes up only its own length, and
|
|
12
|
+
* photos next to a video hard-cut on that side (handled per-clip in [createMediaBlock]). Returns the
|
|
13
|
+
* per-slot durations so the caller can size the page and backdrops to match.
|
|
14
|
+
*/
|
|
15
|
+
internal suspend fun createMainImageSequence(
|
|
16
|
+
engine: Engine,
|
|
17
|
+
page: Int,
|
|
18
|
+
images: List<ImageItem>,
|
|
19
|
+
hasTitle: Boolean,
|
|
20
|
+
animationPairs: List<AnimationPair>,
|
|
21
|
+
): List<Double> {
|
|
22
|
+
images.forEachIndexed { index, _ ->
|
|
23
|
+
val track = createMediaTrack(engine, page, startTime = 0.0)
|
|
24
|
+
engine.block.appendChild(parent = track, child = createMediaBlock(engine, page, index, images, animationPairs))
|
|
25
|
+
}
|
|
26
|
+
return repositionTracks(engine, page, images, hasTitle)
|
|
27
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
package ly.img.editor.configuration.memories.scene
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.util.Log
|
|
5
|
+
import ly.img.editor.configuration.memories.model.ImageItem
|
|
6
|
+
import ly.img.editor.configuration.memories.style.VideoStyles
|
|
7
|
+
import ly.img.editor.configuration.memories.util.BURST_IMAGE_COUNT
|
|
8
|
+
import ly.img.editor.configuration.memories.util.BURST_TITLE_FRACTION
|
|
9
|
+
import ly.img.editor.configuration.memories.util.TITLE_DURATION
|
|
10
|
+
import ly.img.editor.configuration.memories.util.TITLE_FONT_SIZE
|
|
11
|
+
import ly.img.editor.configuration.memories.widget.videoThumbnailFileUri
|
|
12
|
+
import ly.img.engine.AnimationType
|
|
13
|
+
import ly.img.engine.Color
|
|
14
|
+
import ly.img.engine.DesignBlockType
|
|
15
|
+
import ly.img.engine.Engine
|
|
16
|
+
import ly.img.engine.FillType
|
|
17
|
+
import ly.img.engine.FindAssetsQuery
|
|
18
|
+
import ly.img.engine.ShapeType
|
|
19
|
+
import kotlin.random.Random
|
|
20
|
+
|
|
21
|
+
private const val TAG = "MemoriesTitle"
|
|
22
|
+
|
|
23
|
+
/** Create the centered, blur-animated title card on [textTrack]. Returns whether a title was added. */
|
|
24
|
+
internal suspend fun createTitleBlock(
|
|
25
|
+
engine: Engine,
|
|
26
|
+
page: Int,
|
|
27
|
+
title: String,
|
|
28
|
+
textTrack: Int,
|
|
29
|
+
): Boolean {
|
|
30
|
+
if (title.isEmpty()) return false
|
|
31
|
+
|
|
32
|
+
val textBlock = engine.block.create(DesignBlockType.Text)
|
|
33
|
+
engine.block.replaceText(textBlock, title)
|
|
34
|
+
engine.block.setDuration(textBlock, TITLE_DURATION)
|
|
35
|
+
engine.block.setTextColor(textBlock, Color.fromRGBA(1f, 1f, 1f, 1f))
|
|
36
|
+
engine.block.setTextFontSize(textBlock, fontSize = TITLE_FONT_SIZE, from = 0, to = title.length)
|
|
37
|
+
engine.block.setMetadata(textBlock, "blockType", "title")
|
|
38
|
+
|
|
39
|
+
applyTitleTypeface(engine, textBlock, VideoStyles.DEFAULT.typeface)
|
|
40
|
+
|
|
41
|
+
engine.block.setInAnimation(textBlock, blurAnimation(engine, 0.8))
|
|
42
|
+
engine.block.setOutAnimation(textBlock, blurAnimation(engine, 0.6))
|
|
43
|
+
|
|
44
|
+
engine.block.appendChild(parent = textTrack, child = textBlock)
|
|
45
|
+
engine.block.setEnum(textBlock, "text/horizontalAlignment", "Center")
|
|
46
|
+
engine.block.setEnum(textBlock, "text/verticalAlignment", "Center")
|
|
47
|
+
return true
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private fun blurAnimation(
|
|
51
|
+
engine: Engine,
|
|
52
|
+
duration: Double,
|
|
53
|
+
): Int {
|
|
54
|
+
val animation = engine.block.createAnimation(AnimationType.Blur)
|
|
55
|
+
engine.block.setDuration(animation, duration)
|
|
56
|
+
engine.block.setEnum(animation, "textAnimationWritingStyle", "Character")
|
|
57
|
+
return animation
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private suspend fun applyTitleTypeface(
|
|
61
|
+
engine: Engine,
|
|
62
|
+
textBlock: Int,
|
|
63
|
+
typefaceName: String,
|
|
64
|
+
) {
|
|
65
|
+
try {
|
|
66
|
+
val assets = engine.asset.findAssets(
|
|
67
|
+
sourceId = "ly.img.typeface",
|
|
68
|
+
query = FindAssetsQuery(query = typefaceName, page = 0, perPage = 100),
|
|
69
|
+
)
|
|
70
|
+
val typeface = assets.assets.firstOrNull()?.payload?.typeface ?: return
|
|
71
|
+
// Match the weight preference the default style applies (see StyleApplier.resolveFont), so
|
|
72
|
+
// the first style application doesn't visibly re-weight the title from a different initial pick.
|
|
73
|
+
val font = VideoStyles.DEFAULT.fontWeights
|
|
74
|
+
.firstNotNullOfOrNull { weight -> typeface.fonts.firstOrNull { it.subFamily == weight } }
|
|
75
|
+
?: typeface.fonts.firstOrNull()
|
|
76
|
+
?: return
|
|
77
|
+
engine.block.setFont(textBlock, font.uri, typeface)
|
|
78
|
+
} catch (e: Exception) {
|
|
79
|
+
Log.w(TAG, "Could not load title typeface '$typefaceName'", e)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Flash a handful of random photos behind the title for a lively opening. Videos can't be shown
|
|
85
|
+
* through an image fill, so each picked video is flashed as its extracted thumbnail instead (skipped
|
|
86
|
+
* if a frame can't be decoded, or if [context] is unavailable).
|
|
87
|
+
*/
|
|
88
|
+
internal suspend fun createBurstImages(
|
|
89
|
+
engine: Engine,
|
|
90
|
+
page: Int,
|
|
91
|
+
context: Context?,
|
|
92
|
+
images: List<ImageItem>,
|
|
93
|
+
hasTitle: Boolean,
|
|
94
|
+
) {
|
|
95
|
+
if (images.isEmpty() || !hasTitle) return
|
|
96
|
+
|
|
97
|
+
// Resolve each picked item to a renderable image URI: photos use their url, videos a thumbnail.
|
|
98
|
+
val urls = pickBurstImages(images, BURST_IMAGE_COUNT).mapNotNull { item ->
|
|
99
|
+
when {
|
|
100
|
+
!item.isVideo -> item.url
|
|
101
|
+
context != null -> videoThumbnailFileUri(context, item.url)
|
|
102
|
+
else -> null
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (urls.isEmpty()) return
|
|
106
|
+
|
|
107
|
+
val burstTracks = List(urls.size) {
|
|
108
|
+
engine.block.create(DesignBlockType.Track).also { engine.block.appendChild(page, it) }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
val burstDuration = TITLE_DURATION * BURST_TITLE_FRACTION
|
|
112
|
+
val flashDuration = burstDuration / 6.0
|
|
113
|
+
val pageWidth = engine.block.getWidth(page)
|
|
114
|
+
val pageHeight = engine.block.getHeight(page)
|
|
115
|
+
val imageSize = minOf(pageWidth, pageHeight) * 0.6f
|
|
116
|
+
|
|
117
|
+
urls.forEachIndexed { index, url ->
|
|
118
|
+
val block = engine.block.create(DesignBlockType.Graphic)
|
|
119
|
+
engine.block.setScopeEnabled(block, "editor/select", false)
|
|
120
|
+
engine.block.setWidth(block, imageSize)
|
|
121
|
+
engine.block.setHeight(block, imageSize)
|
|
122
|
+
engine.block.setPositionX(block, randomIn(-imageSize / 2f, pageWidth - imageSize / 2f))
|
|
123
|
+
engine.block.setPositionY(block, randomIn(-imageSize / 2f, pageHeight - imageSize / 2f))
|
|
124
|
+
|
|
125
|
+
val fill = engine.block.createFill(FillType.Image)
|
|
126
|
+
engine.block.setFill(block, fill)
|
|
127
|
+
engine.block.setString(fill, "fill/image/imageFileURI", url)
|
|
128
|
+
engine.block.setShape(block, engine.block.createShape(ShapeType.Rect))
|
|
129
|
+
engine.block.setDuration(block, flashDuration)
|
|
130
|
+
engine.block.setInAnimation(block, fadeAnimation(engine, 0.1))
|
|
131
|
+
engine.block.setOutAnimation(block, fadeAnimation(engine, 0.15))
|
|
132
|
+
|
|
133
|
+
val startDelay = index * (flashDuration * 0.25)
|
|
134
|
+
if (startDelay > 0) {
|
|
135
|
+
engine.block.appendChild(burstTracks[index], createSpacerBlock(engine, page, startDelay))
|
|
136
|
+
}
|
|
137
|
+
engine.block.appendChild(burstTracks[index], block)
|
|
138
|
+
val remaining = TITLE_DURATION - (startDelay + flashDuration)
|
|
139
|
+
if (remaining > 0) {
|
|
140
|
+
engine.block.appendChild(burstTracks[index], createSpacerBlock(engine, page, remaining))
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private fun pickBurstImages(
|
|
146
|
+
images: List<ImageItem>,
|
|
147
|
+
count: Int,
|
|
148
|
+
): List<ImageItem> {
|
|
149
|
+
val shuffled = images.shuffled(Random(System.currentTimeMillis()))
|
|
150
|
+
if (shuffled.size >= count) return shuffled.take(count)
|
|
151
|
+
return generateSequence { shuffled }.flatten().take(count).toList()
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private fun fadeAnimation(
|
|
155
|
+
engine: Engine,
|
|
156
|
+
duration: Double,
|
|
157
|
+
): Int = engine.block.createAnimation(AnimationType.Fade).also { engine.block.setDuration(it, duration) }
|
|
158
|
+
|
|
159
|
+
private fun randomIn(
|
|
160
|
+
min: Float,
|
|
161
|
+
max: Float,
|
|
162
|
+
): Float = min + Random.nextFloat() * (max - min)
|