@imgly/editor-react-native 1.81.0-rc.0 → 1.81.0-rc.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.
- package/android/build.gradle +4 -1
- package/package.json +1 -1
- package/src/NativeIMGLYEditor.ts +1 -1
- package/android/src/main/assets/ly.img.memories.style/content.json +0 -50
- 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 +0 -77
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/MemoriesConfiguration.kt +0 -38
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/MemoriesEditor.kt +0 -79
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/MemoriesViewModel.kt +0 -469
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/callback/OnCreate.kt +0 -69
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/callback/OnExport.kt +0 -46
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/component/BottomPanel.kt +0 -151
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/component/Dock.kt +0 -145
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/component/ExportOverlay.kt +0 -229
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/component/NavigationBar.kt +0 -80
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/component/Overlay.kt +0 -138
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/extension/EditorScopeExt.kt +0 -111
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/AddPhotoAlternate.kt +0 -79
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/ArrowForward.kt +0 -45
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/Audiotrack.kt +0 -43
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/Check.kt +0 -39
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/CheckCircle.kt +0 -44
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/Close.kt +0 -43
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/Delete.kt +0 -54
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/DeleteOutline.kt +0 -53
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/Fullscreen.kt +0 -74
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/Image.kt +0 -51
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/Info.kt +0 -48
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/Pause.kt +0 -46
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/PlayArrow.kt +0 -38
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/RepeatOff.kt +0 -80
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/RepeatOn.kt +0 -59
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/SkipPrevious.kt +0 -46
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/Sort.kt +0 -55
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/Style.kt +0 -59
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/Title.kt +0 -43
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/TouchTriple.kt +0 -134
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/VolumeDown.kt +0 -48
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/iconPack/VolumeUp.kt +0 -60
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/model/ExportStatus.kt +0 -20
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/model/ImageItem.kt +0 -8
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/model/TimelineImage.kt +0 -18
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/scene/Events.kt +0 -59
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/scene/Playback.kt +0 -29
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/scene/SceneSetup.kt +0 -79
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/scene/Timeline.kt +0 -27
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/scene/Title.kt +0 -162
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/scene/TrackEditor.kt +0 -740
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/screen/ImageSelectionScreen.kt +0 -387
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/screen/LoadingScreen.kt +0 -258
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/sheet/FilterStylesSheet.kt +0 -167
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/sheet/FilterStylesSheetType.kt +0 -21
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/sheet/VolumeSheet.kt +0 -104
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/style/StyleApplier.kt +0 -220
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/style/StyleAssetSource.kt +0 -43
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/style/VideoStyle.kt +0 -118
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/util/Animations.kt +0 -58
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/util/Constants.kt +0 -20
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/util/ImageDate.kt +0 -81
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/util/Utils.kt +0 -27
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/widget/CenteredAddImageButton.kt +0 -82
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/widget/DraggableImageItem.kt +0 -145
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/widget/FullscreenVideoControls.kt +0 -136
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/widget/ImageGrid.kt +0 -122
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/widget/ImageGridOverlay.kt +0 -149
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/widget/ImageTimelineStrip.kt +0 -323
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/widget/ImageToolbar.kt +0 -401
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/widget/ReorderableGridState.kt +0 -183
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/widget/VideoControlPanel.kt +0 -180
- package/android/src/main/kotlin/ly/img/editor/configuration/memories/widget/VideoThumbnail.kt +0 -110
package/android/src/main/kotlin/ly/img/editor/configuration/memories/screen/ImageSelectionScreen.kt
DELETED
|
@@ -1,387 +0,0 @@
|
|
|
1
|
-
package ly.img.editor.configuration.memories.screen
|
|
2
|
-
|
|
3
|
-
import android.net.Uri
|
|
4
|
-
import androidx.activity.compose.BackHandler
|
|
5
|
-
import androidx.activity.compose.rememberLauncherForActivityResult
|
|
6
|
-
import androidx.activity.result.PickVisualMediaRequest
|
|
7
|
-
import androidx.activity.result.contract.ActivityResultContracts
|
|
8
|
-
import androidx.compose.foundation.background
|
|
9
|
-
import androidx.compose.foundation.layout.Arrangement
|
|
10
|
-
import androidx.compose.foundation.layout.Box
|
|
11
|
-
import androidx.compose.foundation.layout.Column
|
|
12
|
-
import androidx.compose.foundation.layout.PaddingValues
|
|
13
|
-
import androidx.compose.foundation.layout.Row
|
|
14
|
-
import androidx.compose.foundation.layout.Spacer
|
|
15
|
-
import androidx.compose.foundation.layout.defaultMinSize
|
|
16
|
-
import androidx.compose.foundation.layout.fillMaxSize
|
|
17
|
-
import androidx.compose.foundation.layout.fillMaxWidth
|
|
18
|
-
import androidx.compose.foundation.layout.height
|
|
19
|
-
import androidx.compose.foundation.layout.navigationBarsPadding
|
|
20
|
-
import androidx.compose.foundation.layout.padding
|
|
21
|
-
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
|
22
|
-
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
23
|
-
import androidx.compose.foundation.text.KeyboardOptions
|
|
24
|
-
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
25
|
-
import androidx.compose.material3.Icon
|
|
26
|
-
import androidx.compose.material3.MaterialTheme
|
|
27
|
-
import androidx.compose.material3.OutlinedTextField
|
|
28
|
-
import androidx.compose.material3.Text
|
|
29
|
-
import androidx.compose.runtime.Composable
|
|
30
|
-
import androidx.compose.runtime.collectAsState
|
|
31
|
-
import androidx.compose.runtime.derivedStateOf
|
|
32
|
-
import androidx.compose.runtime.getValue
|
|
33
|
-
import androidx.compose.runtime.mutableFloatStateOf
|
|
34
|
-
import androidx.compose.runtime.remember
|
|
35
|
-
import androidx.compose.runtime.rememberCoroutineScope
|
|
36
|
-
import androidx.compose.runtime.setValue
|
|
37
|
-
import androidx.compose.ui.Alignment
|
|
38
|
-
import androidx.compose.ui.Modifier
|
|
39
|
-
import androidx.compose.ui.graphics.Brush
|
|
40
|
-
import androidx.compose.ui.graphics.graphicsLayer
|
|
41
|
-
import androidx.compose.ui.layout.onGloballyPositioned
|
|
42
|
-
import androidx.compose.ui.platform.LocalContext
|
|
43
|
-
import androidx.compose.ui.platform.LocalDensity
|
|
44
|
-
import androidx.compose.ui.text.font.FontWeight
|
|
45
|
-
import androidx.compose.ui.text.input.ImeAction
|
|
46
|
-
import androidx.compose.ui.text.style.TextAlign
|
|
47
|
-
import androidx.compose.ui.tooling.preview.Preview
|
|
48
|
-
import androidx.compose.ui.unit.dp
|
|
49
|
-
import androidx.compose.ui.unit.sp
|
|
50
|
-
import androidx.compose.ui.zIndex
|
|
51
|
-
import kotlinx.coroutines.launch
|
|
52
|
-
import ly.img.editor.configuration.memories.MemoriesViewModel
|
|
53
|
-
import ly.img.editor.configuration.memories.iconPack.Info
|
|
54
|
-
import ly.img.editor.configuration.memories.model.ImageItem
|
|
55
|
-
import ly.img.editor.configuration.memories.util.buildImageItems
|
|
56
|
-
import ly.img.editor.configuration.memories.widget.CenteredAddImageButton
|
|
57
|
-
import ly.img.editor.configuration.memories.widget.ImageGrid
|
|
58
|
-
import ly.img.editor.configuration.memories.widget.ImageToolbar
|
|
59
|
-
import ly.img.editor.core.iconpack.IconPack
|
|
60
|
-
|
|
61
|
-
@OptIn(ExperimentalMaterial3Api::class)
|
|
62
|
-
@Composable
|
|
63
|
-
fun ImageSelectionScreen(
|
|
64
|
-
viewModel: MemoriesViewModel,
|
|
65
|
-
onProceedToEditor: () -> Unit,
|
|
66
|
-
modifier: Modifier = Modifier,
|
|
67
|
-
) {
|
|
68
|
-
val selectedImages by viewModel.selectedImages.collectAsState()
|
|
69
|
-
val imagesToDelete by viewModel.imagesToDelete.collectAsState()
|
|
70
|
-
val multiSelectMode by viewModel.multiSelectMode.collectAsState()
|
|
71
|
-
val videoTitle by viewModel.videoTitle.collectAsState()
|
|
72
|
-
val isSortEnabled by viewModel.isSortEnabled.collectAsState()
|
|
73
|
-
val isSortAscending by viewModel.isSortAscending.collectAsState()
|
|
74
|
-
val context = LocalContext.current
|
|
75
|
-
val density = LocalDensity.current
|
|
76
|
-
val scope = rememberCoroutineScope()
|
|
77
|
-
|
|
78
|
-
// Back exits selection mode (rather than leaving the screen) while it's on.
|
|
79
|
-
BackHandler(enabled = multiSelectMode) {
|
|
80
|
-
viewModel.toggleMultiSelect()
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// LazyGrid state for scroll monitoring
|
|
84
|
-
val lazyGridState = rememberLazyGridState()
|
|
85
|
-
|
|
86
|
-
// Height measurement for top content
|
|
87
|
-
var columnHeight by remember { mutableFloatStateOf(0f) }
|
|
88
|
-
|
|
89
|
-
// Calculate fade effect based on scroll position
|
|
90
|
-
val fadeAlpha = remember {
|
|
91
|
-
derivedStateOf {
|
|
92
|
-
if (lazyGridState.firstVisibleItemIndex == 0) {
|
|
93
|
-
val offset = lazyGridState.firstVisibleItemScrollOffset
|
|
94
|
-
val fadeThreshold = with(density) { 100.dp.toPx() }
|
|
95
|
-
(1f - (offset / fadeThreshold)).coerceIn(0f, 1f)
|
|
96
|
-
} else {
|
|
97
|
-
0f
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Media picker launcher (images and videos)
|
|
103
|
-
val photoPickerLauncher = rememberLauncherForActivityResult(
|
|
104
|
-
contract = ActivityResultContracts.PickMultipleVisualMedia(),
|
|
105
|
-
) { uris: List<Uri> ->
|
|
106
|
-
if (uris.isNotEmpty()) {
|
|
107
|
-
// Base new ids on the current max id (not list size) so they stay unique
|
|
108
|
-
// even after some images have been deleted.
|
|
109
|
-
val baseId = selectedImages.maxOfOrNull { it.id } ?: 0
|
|
110
|
-
scope.launch {
|
|
111
|
-
val newImages = buildImageItems(context.contentResolver, uris, baseId)
|
|
112
|
-
// The ViewModel merges these in (re-sorting if sort is on) and reconciles the
|
|
113
|
-
// sort button: if the resulting list is chronological it flips sort on automatically.
|
|
114
|
-
viewModel.addSelectedImages(newImages)
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
Box(
|
|
120
|
-
modifier = modifier,
|
|
121
|
-
) {
|
|
122
|
-
Column(
|
|
123
|
-
modifier = Modifier
|
|
124
|
-
.zIndex(if (fadeAlpha.value > .9f) 100f else -1f)
|
|
125
|
-
.fillMaxWidth()
|
|
126
|
-
.padding(16.dp)
|
|
127
|
-
.onGloballyPositioned { coordinates ->
|
|
128
|
-
columnHeight = coordinates.size.height.toFloat()
|
|
129
|
-
}
|
|
130
|
-
.graphicsLayer {
|
|
131
|
-
alpha = fadeAlpha.value
|
|
132
|
-
translationY = (1f - fadeAlpha.value) * -100f
|
|
133
|
-
},
|
|
134
|
-
verticalArrangement = Arrangement.spacedBy(2.dp),
|
|
135
|
-
) {
|
|
136
|
-
// Title with a standalone informational icon to its right (its hint is exposed via
|
|
137
|
-
// contentDescription for accessibility).
|
|
138
|
-
Row(
|
|
139
|
-
modifier = Modifier
|
|
140
|
-
.defaultMinSize(minHeight = 48.dp)
|
|
141
|
-
.fillMaxWidth(),
|
|
142
|
-
verticalAlignment = Alignment.CenterVertically,
|
|
143
|
-
) {
|
|
144
|
-
Text(
|
|
145
|
-
text = "Create Your Video Memory",
|
|
146
|
-
fontSize = 24.sp,
|
|
147
|
-
fontWeight = FontWeight.Bold,
|
|
148
|
-
textAlign = TextAlign.Start,
|
|
149
|
-
modifier = Modifier.weight(1f),
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
// Only show the drag/select hint once there are images to act on
|
|
153
|
-
if (selectedImages.isNotEmpty()) {
|
|
154
|
-
Icon(
|
|
155
|
-
imageVector = IconPack.Info,
|
|
156
|
-
contentDescription = "Drag and drop images by long pressing; tap to select for deletion.",
|
|
157
|
-
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
158
|
-
modifier = Modifier.padding(12.dp),
|
|
159
|
-
)
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
Spacer(Modifier.height(8.dp))
|
|
164
|
-
|
|
165
|
-
// Video title input field
|
|
166
|
-
OutlinedTextField(
|
|
167
|
-
value = videoTitle,
|
|
168
|
-
onValueChange = { viewModel.setVideoTitle(it) },
|
|
169
|
-
label = { Text("Add a title") },
|
|
170
|
-
placeholder = { Text("My Amazing Memory") },
|
|
171
|
-
modifier = Modifier.fillMaxWidth(),
|
|
172
|
-
singleLine = true,
|
|
173
|
-
keyboardOptions = KeyboardOptions(
|
|
174
|
-
imeAction = ImeAction.Done,
|
|
175
|
-
),
|
|
176
|
-
shape = RoundedCornerShape(8.dp),
|
|
177
|
-
)
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Image Grid
|
|
181
|
-
Box(
|
|
182
|
-
modifier = Modifier
|
|
183
|
-
.fillMaxSize(),
|
|
184
|
-
) {
|
|
185
|
-
if (selectedImages.isEmpty()) {
|
|
186
|
-
// Empty state: launch the device media picker
|
|
187
|
-
CenteredAddImageButton(
|
|
188
|
-
modifier = Modifier.align(Alignment.TopCenter),
|
|
189
|
-
onClick = { photoPickerLauncher.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageAndVideo)) },
|
|
190
|
-
columnHeight = columnHeight,
|
|
191
|
-
)
|
|
192
|
-
} else {
|
|
193
|
-
ImageGrid(
|
|
194
|
-
modifier = Modifier
|
|
195
|
-
.align(Alignment.TopCenter),
|
|
196
|
-
images = selectedImages,
|
|
197
|
-
onImagesChanged = { viewModel.updateSelectedImages(it) },
|
|
198
|
-
contentPadding = PaddingValues(
|
|
199
|
-
top = with(density) { (columnHeight / density.density).dp + 32.dp },
|
|
200
|
-
bottom = 96.dp,
|
|
201
|
-
start = 16.dp,
|
|
202
|
-
end = 16.dp,
|
|
203
|
-
),
|
|
204
|
-
lazyGridState = lazyGridState,
|
|
205
|
-
selectedForDeletion = if (multiSelectMode) imagesToDelete else emptySet(),
|
|
206
|
-
onToggleSelection = if (multiSelectMode) {
|
|
207
|
-
{ viewModel.toggleImageForDeletion(it) }
|
|
208
|
-
} else {
|
|
209
|
-
null
|
|
210
|
-
},
|
|
211
|
-
)
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
Box(
|
|
215
|
-
modifier = Modifier
|
|
216
|
-
.fillMaxWidth()
|
|
217
|
-
.align(Alignment.TopCenter)
|
|
218
|
-
.height(12.dp)
|
|
219
|
-
.background(
|
|
220
|
-
brush = Brush.verticalGradient(
|
|
221
|
-
colors = listOf(
|
|
222
|
-
MaterialTheme.colorScheme.background,
|
|
223
|
-
MaterialTheme.colorScheme.background.copy(alpha = 0f),
|
|
224
|
-
),
|
|
225
|
-
),
|
|
226
|
-
),
|
|
227
|
-
)
|
|
228
|
-
|
|
229
|
-
Box(
|
|
230
|
-
modifier = Modifier
|
|
231
|
-
.fillMaxWidth()
|
|
232
|
-
.align(Alignment.BottomCenter)
|
|
233
|
-
.height(64.dp)
|
|
234
|
-
.background(
|
|
235
|
-
brush = Brush.verticalGradient(
|
|
236
|
-
colors = listOf(
|
|
237
|
-
MaterialTheme.colorScheme.background.copy(alpha = 0f),
|
|
238
|
-
MaterialTheme.colorScheme.background,
|
|
239
|
-
),
|
|
240
|
-
),
|
|
241
|
-
),
|
|
242
|
-
)
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Bottom toolbar - only show if there are images
|
|
246
|
-
if (selectedImages.isNotEmpty()) {
|
|
247
|
-
ImageToolbar(
|
|
248
|
-
modifier = Modifier
|
|
249
|
-
.align(Alignment.BottomCenter)
|
|
250
|
-
.padding(horizontal = 16.dp)
|
|
251
|
-
.navigationBarsPadding(),
|
|
252
|
-
onAddImage = { photoPickerLauncher.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageAndVideo)) },
|
|
253
|
-
onToggleSort = { viewModel.toggleSort() },
|
|
254
|
-
onDeleteAll = { viewModel.clearSelectedImages() },
|
|
255
|
-
onDeleteSelected = { viewModel.deleteSelectedImages() },
|
|
256
|
-
onProceed = onProceedToEditor,
|
|
257
|
-
isSortEnabled = isSortEnabled,
|
|
258
|
-
isSortAscending = isSortAscending,
|
|
259
|
-
hasImages = selectedImages.isNotEmpty(),
|
|
260
|
-
showDeleteButton = true,
|
|
261
|
-
selectedForDeletionCount = imagesToDelete.size,
|
|
262
|
-
totalCount = selectedImages.size,
|
|
263
|
-
multiSelectMode = multiSelectMode,
|
|
264
|
-
onToggleMultiSelect = { viewModel.toggleMultiSelect() },
|
|
265
|
-
)
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
@Composable
|
|
271
|
-
private fun createMockViewModel(
|
|
272
|
-
selectedImages: List<ImageItem> = emptyList(),
|
|
273
|
-
videoTitle: String = "",
|
|
274
|
-
): MemoriesViewModel {
|
|
275
|
-
val viewModel = remember { MemoriesViewModel() }
|
|
276
|
-
|
|
277
|
-
// Set the initial state synchronously in the preview
|
|
278
|
-
viewModel.updateSelectedImages(selectedImages)
|
|
279
|
-
if (videoTitle.isNotEmpty()) {
|
|
280
|
-
viewModel.setVideoTitle(videoTitle)
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
return viewModel
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
@Preview(
|
|
287
|
-
name = "Empty State",
|
|
288
|
-
showBackground = true,
|
|
289
|
-
)
|
|
290
|
-
@Composable
|
|
291
|
-
fun ImageSelectionScreen_EmptyState_Preview() {
|
|
292
|
-
val mockViewModel = createMockViewModel()
|
|
293
|
-
|
|
294
|
-
ImageSelectionScreen(
|
|
295
|
-
viewModel = mockViewModel,
|
|
296
|
-
onProceedToEditor = { },
|
|
297
|
-
)
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
@Preview(
|
|
301
|
-
name = "Unfocused with Sample Images",
|
|
302
|
-
showBackground = true,
|
|
303
|
-
)
|
|
304
|
-
@Composable
|
|
305
|
-
fun ImageSelectionScreen_WithImages_Preview() {
|
|
306
|
-
val sampleImages = listOf(
|
|
307
|
-
ImageItem(id = 1, url = ""),
|
|
308
|
-
ImageItem(id = 2, url = ""),
|
|
309
|
-
ImageItem(id = 3, url = ""),
|
|
310
|
-
)
|
|
311
|
-
val mockViewModel = createMockViewModel(selectedImages = sampleImages)
|
|
312
|
-
|
|
313
|
-
ImageSelectionScreen(
|
|
314
|
-
viewModel = mockViewModel,
|
|
315
|
-
onProceedToEditor = { },
|
|
316
|
-
)
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
@Preview(
|
|
320
|
-
name = "Focused Text Input",
|
|
321
|
-
showBackground = true,
|
|
322
|
-
)
|
|
323
|
-
@Composable
|
|
324
|
-
fun ImageSelectionScreen_FocusedInput_Preview() {
|
|
325
|
-
val sampleImages = listOf(
|
|
326
|
-
ImageItem(id = 1, url = ""),
|
|
327
|
-
ImageItem(id = 2, url = ""),
|
|
328
|
-
)
|
|
329
|
-
val mockViewModel = createMockViewModel(
|
|
330
|
-
selectedImages = sampleImages,
|
|
331
|
-
videoTitle = "My Amazing Memory",
|
|
332
|
-
)
|
|
333
|
-
|
|
334
|
-
ImageSelectionScreen(
|
|
335
|
-
viewModel = mockViewModel,
|
|
336
|
-
onProceedToEditor = { },
|
|
337
|
-
)
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
@Preview(
|
|
341
|
-
name = "User Photos with Many Images",
|
|
342
|
-
showBackground = true,
|
|
343
|
-
)
|
|
344
|
-
@Composable
|
|
345
|
-
fun ImageSelectionScreen_ManyUserPhotos_Preview() {
|
|
346
|
-
val sampleImages = listOf(
|
|
347
|
-
ImageItem(
|
|
348
|
-
id = 1,
|
|
349
|
-
url = "",
|
|
350
|
-
creationDate = System.currentTimeMillis() - 86400000,
|
|
351
|
-
),
|
|
352
|
-
ImageItem(
|
|
353
|
-
id = 2,
|
|
354
|
-
url = "",
|
|
355
|
-
creationDate = System.currentTimeMillis() - 172800000,
|
|
356
|
-
),
|
|
357
|
-
ImageItem(
|
|
358
|
-
id = 3,
|
|
359
|
-
url = "",
|
|
360
|
-
creationDate = System.currentTimeMillis() - 259200000,
|
|
361
|
-
),
|
|
362
|
-
ImageItem(
|
|
363
|
-
id = 4,
|
|
364
|
-
url = "",
|
|
365
|
-
creationDate = System.currentTimeMillis() - 345600000,
|
|
366
|
-
),
|
|
367
|
-
ImageItem(
|
|
368
|
-
id = 5,
|
|
369
|
-
url = "",
|
|
370
|
-
creationDate = System.currentTimeMillis() - 432000000,
|
|
371
|
-
),
|
|
372
|
-
ImageItem(
|
|
373
|
-
id = 6,
|
|
374
|
-
url = "",
|
|
375
|
-
creationDate = System.currentTimeMillis() - 518400000,
|
|
376
|
-
),
|
|
377
|
-
)
|
|
378
|
-
val mockViewModel = createMockViewModel(
|
|
379
|
-
selectedImages = sampleImages,
|
|
380
|
-
videoTitle = "Summer Vacation 2024",
|
|
381
|
-
)
|
|
382
|
-
|
|
383
|
-
ImageSelectionScreen(
|
|
384
|
-
viewModel = mockViewModel,
|
|
385
|
-
onProceedToEditor = { },
|
|
386
|
-
)
|
|
387
|
-
}
|
package/android/src/main/kotlin/ly/img/editor/configuration/memories/screen/LoadingScreen.kt
DELETED
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
package ly.img.editor.configuration.memories.screen
|
|
2
|
-
|
|
3
|
-
import androidx.compose.animation.core.animateFloatAsState
|
|
4
|
-
import androidx.compose.animation.core.spring
|
|
5
|
-
import androidx.compose.animation.core.tween
|
|
6
|
-
import androidx.compose.foundation.background
|
|
7
|
-
import androidx.compose.foundation.isSystemInDarkTheme
|
|
8
|
-
import androidx.compose.foundation.layout.Arrangement
|
|
9
|
-
import androidx.compose.foundation.layout.Box
|
|
10
|
-
import androidx.compose.foundation.layout.Column
|
|
11
|
-
import androidx.compose.foundation.layout.aspectRatio
|
|
12
|
-
import androidx.compose.foundation.layout.fillMaxSize
|
|
13
|
-
import androidx.compose.foundation.layout.padding
|
|
14
|
-
import androidx.compose.foundation.lazy.grid.GridCells
|
|
15
|
-
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
|
16
|
-
import androidx.compose.foundation.lazy.grid.items
|
|
17
|
-
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
18
|
-
import androidx.compose.material3.MaterialTheme
|
|
19
|
-
import androidx.compose.material3.Text
|
|
20
|
-
import androidx.compose.runtime.Composable
|
|
21
|
-
import androidx.compose.runtime.LaunchedEffect
|
|
22
|
-
import androidx.compose.runtime.getValue
|
|
23
|
-
import androidx.compose.runtime.mutableStateOf
|
|
24
|
-
import androidx.compose.runtime.remember
|
|
25
|
-
import androidx.compose.runtime.setValue
|
|
26
|
-
import androidx.compose.ui.Alignment
|
|
27
|
-
import androidx.compose.ui.Modifier
|
|
28
|
-
import androidx.compose.ui.draw.alpha
|
|
29
|
-
import androidx.compose.ui.draw.clip
|
|
30
|
-
import androidx.compose.ui.draw.drawBehind
|
|
31
|
-
import androidx.compose.ui.draw.scale
|
|
32
|
-
import androidx.compose.ui.graphics.Brush
|
|
33
|
-
import androidx.compose.ui.graphics.Color
|
|
34
|
-
import androidx.compose.ui.graphics.drawscope.scale
|
|
35
|
-
import androidx.compose.ui.input.pointer.pointerInput
|
|
36
|
-
import androidx.compose.ui.layout.ContentScale
|
|
37
|
-
import androidx.compose.ui.text.font.FontWeight
|
|
38
|
-
import androidx.compose.ui.text.style.TextAlign
|
|
39
|
-
import androidx.compose.ui.tooling.preview.Preview
|
|
40
|
-
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
|
41
|
-
import androidx.compose.ui.unit.dp
|
|
42
|
-
import coil.compose.AsyncImage
|
|
43
|
-
import kotlinx.coroutines.delay
|
|
44
|
-
import kotlinx.coroutines.launch
|
|
45
|
-
import ly.img.editor.configuration.memories.model.ImageItem
|
|
46
|
-
import ly.img.editor.core.theme.EditorTheme
|
|
47
|
-
import kotlin.random.Random
|
|
48
|
-
|
|
49
|
-
@Composable
|
|
50
|
-
fun LoadingScreen(
|
|
51
|
-
selectedImages: List<ImageItem>,
|
|
52
|
-
modifier: Modifier = Modifier,
|
|
53
|
-
) {
|
|
54
|
-
// Create a 4x4 grid (16 items)
|
|
55
|
-
val gridSize = 16
|
|
56
|
-
var flashingStates by remember { mutableStateOf(List(gridSize) { Random.nextBoolean() }) }
|
|
57
|
-
var currentImages by remember { mutableStateOf<List<ImageItem?>>(List(gridSize) { null }) }
|
|
58
|
-
|
|
59
|
-
// Staggered entrance: everything starts hidden, then the grid, title, and
|
|
60
|
-
// description fade in one after another. Each fade has the same duration but a
|
|
61
|
-
// later start (delayMillis), so they overlap instead of waiting for the
|
|
62
|
-
// previous one to finish.
|
|
63
|
-
var entered by remember { mutableStateOf(false) }
|
|
64
|
-
LaunchedEffect(Unit) { entered = true }
|
|
65
|
-
|
|
66
|
-
val entranceDuration = 600
|
|
67
|
-
val entranceStagger = 300
|
|
68
|
-
val gridAlpha by animateFloatAsState(
|
|
69
|
-
targetValue = if (entered) 1f else 0f,
|
|
70
|
-
animationSpec = tween(durationMillis = entranceDuration, delayMillis = 0),
|
|
71
|
-
label = "grid_entrance",
|
|
72
|
-
)
|
|
73
|
-
val titleAlpha by animateFloatAsState(
|
|
74
|
-
targetValue = if (entered) 1f else 0f,
|
|
75
|
-
animationSpec = tween(durationMillis = entranceDuration, delayMillis = entranceStagger),
|
|
76
|
-
label = "title_entrance",
|
|
77
|
-
)
|
|
78
|
-
val descriptionAlpha by animateFloatAsState(
|
|
79
|
-
targetValue = if (entered) 1f else 0f,
|
|
80
|
-
animationSpec = tween(durationMillis = entranceDuration, delayMillis = entranceStagger * 2),
|
|
81
|
-
label = "description_entrance",
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
// Staggered flashing with random timing for each cell
|
|
85
|
-
LaunchedEffect(selectedImages) {
|
|
86
|
-
// Nothing to flash without images — and selectedImages.random() below would throw on an
|
|
87
|
-
// empty list (e.g. the loading screen shown for an empty draft).
|
|
88
|
-
if (selectedImages.isEmpty()) return@LaunchedEffect
|
|
89
|
-
// Launch individual coroutines for each cell with random timing
|
|
90
|
-
for (i in 0 until gridSize) {
|
|
91
|
-
launch {
|
|
92
|
-
while (true) {
|
|
93
|
-
if (flashingStates[i]) {
|
|
94
|
-
// Hide image
|
|
95
|
-
flashingStates = flashingStates.toMutableList().apply {
|
|
96
|
-
this[i] = false
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
val hiddenTime = (300..500).random().toLong()
|
|
100
|
-
delay(hiddenTime)
|
|
101
|
-
} else {
|
|
102
|
-
// Select a new random image for this specific cell
|
|
103
|
-
val newImages = currentImages.toMutableList()
|
|
104
|
-
newImages[i] = selectedImages.random()
|
|
105
|
-
currentImages = newImages
|
|
106
|
-
|
|
107
|
-
// Show image
|
|
108
|
-
flashingStates = flashingStates.toMutableList().apply {
|
|
109
|
-
this[i] = true
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
val visibleTime = (300..400).random().toLong()
|
|
113
|
-
delay(visibleTime)
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
Box(
|
|
121
|
-
modifier = modifier
|
|
122
|
-
.fillMaxSize()
|
|
123
|
-
.background(MaterialTheme.colorScheme.background)
|
|
124
|
-
// Swallow all input so taps/gestures don't reach the still-composed (alpha 0) editor
|
|
125
|
-
// underneath while it is loading.
|
|
126
|
-
.pointerInput(Unit) {
|
|
127
|
-
awaitPointerEventScope {
|
|
128
|
-
while (true) {
|
|
129
|
-
awaitPointerEvent().changes.forEach { it.consume() }
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
},
|
|
133
|
-
contentAlignment = Alignment.Center,
|
|
134
|
-
) {
|
|
135
|
-
Column(
|
|
136
|
-
horizontalAlignment = Alignment.CenterHorizontally,
|
|
137
|
-
verticalArrangement = Arrangement.spacedBy(32.dp),
|
|
138
|
-
) {
|
|
139
|
-
// 4x4 Image Grid
|
|
140
|
-
Box {
|
|
141
|
-
LazyVerticalGrid(
|
|
142
|
-
columns = GridCells.Fixed(4),
|
|
143
|
-
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
|
144
|
-
verticalArrangement = Arrangement.spacedBy(12.dp),
|
|
145
|
-
modifier = Modifier
|
|
146
|
-
.alpha(gridAlpha)
|
|
147
|
-
.aspectRatio(1f)
|
|
148
|
-
.fillMaxSize()
|
|
149
|
-
.padding(32.dp),
|
|
150
|
-
userScrollEnabled = false,
|
|
151
|
-
) {
|
|
152
|
-
items(gridSize) { index ->
|
|
153
|
-
val isFlashing = flashingStates[index]
|
|
154
|
-
val imageToShow = currentImages[index]
|
|
155
|
-
|
|
156
|
-
val alpha by animateFloatAsState(
|
|
157
|
-
targetValue = if (isFlashing) 1f else 0f,
|
|
158
|
-
animationSpec = tween(300),
|
|
159
|
-
label = "flash_alpha",
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
val scale by animateFloatAsState(
|
|
163
|
-
targetValue = if (isFlashing) .9f else 1f,
|
|
164
|
-
animationSpec = spring(),
|
|
165
|
-
label = "flash_scale",
|
|
166
|
-
)
|
|
167
|
-
|
|
168
|
-
Box(
|
|
169
|
-
modifier = Modifier
|
|
170
|
-
.scale(scale)
|
|
171
|
-
.aspectRatio(1f)
|
|
172
|
-
.background(
|
|
173
|
-
MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f),
|
|
174
|
-
shape = RoundedCornerShape(8.dp),
|
|
175
|
-
),
|
|
176
|
-
) {
|
|
177
|
-
if (imageToShow != null) {
|
|
178
|
-
AsyncImage(
|
|
179
|
-
model = imageToShow.url,
|
|
180
|
-
contentDescription = null,
|
|
181
|
-
modifier = Modifier
|
|
182
|
-
.fillMaxSize()
|
|
183
|
-
.clip(RoundedCornerShape(8.dp))
|
|
184
|
-
.alpha(alpha),
|
|
185
|
-
contentScale = ContentScale.Crop,
|
|
186
|
-
)
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Loading Text
|
|
193
|
-
val isDark = isSystemInDarkTheme()
|
|
194
|
-
val backgroundColor = MaterialTheme.colorScheme.background
|
|
195
|
-
Column(
|
|
196
|
-
modifier = Modifier
|
|
197
|
-
.aspectRatio(1f)
|
|
198
|
-
.fillMaxSize()
|
|
199
|
-
.drawBehind {
|
|
200
|
-
scale(
|
|
201
|
-
scaleY = .8f,
|
|
202
|
-
scaleX = 1f,
|
|
203
|
-
) {
|
|
204
|
-
drawRect(
|
|
205
|
-
brush = Brush.radialGradient(
|
|
206
|
-
colors = listOf(
|
|
207
|
-
backgroundColor.copy(alpha = if (isDark) .85f else 1f),
|
|
208
|
-
Color.Transparent,
|
|
209
|
-
),
|
|
210
|
-
),
|
|
211
|
-
)
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
.padding(32.dp)
|
|
215
|
-
.align(Alignment.Center),
|
|
216
|
-
horizontalAlignment = Alignment.CenterHorizontally,
|
|
217
|
-
verticalArrangement = Arrangement.spacedBy(
|
|
218
|
-
space = 8.dp,
|
|
219
|
-
alignment = Alignment.CenterVertically,
|
|
220
|
-
),
|
|
221
|
-
) {
|
|
222
|
-
Text(
|
|
223
|
-
text = "Hold tight",
|
|
224
|
-
style = MaterialTheme.typography.headlineMedium,
|
|
225
|
-
fontWeight = FontWeight.Bold,
|
|
226
|
-
color = MaterialTheme.colorScheme.onBackground,
|
|
227
|
-
modifier = Modifier.alpha(titleAlpha),
|
|
228
|
-
)
|
|
229
|
-
Text(
|
|
230
|
-
text = "We're magically creating\nmemories for you ✨",
|
|
231
|
-
style = MaterialTheme.typography.bodyLarge,
|
|
232
|
-
textAlign = TextAlign.Center,
|
|
233
|
-
color = MaterialTheme.colorScheme.onBackground,
|
|
234
|
-
modifier = Modifier
|
|
235
|
-
.alpha(descriptionAlpha)
|
|
236
|
-
.padding(horizontal = 32.dp),
|
|
237
|
-
)
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
@PreviewLightDark
|
|
245
|
-
@Preview(name = "Loading Screen", showBackground = true)
|
|
246
|
-
@Composable
|
|
247
|
-
private fun LoadingScreen_Preview() {
|
|
248
|
-
EditorTheme {
|
|
249
|
-
LoadingScreen(
|
|
250
|
-
selectedImages = List(6) { index ->
|
|
251
|
-
ImageItem(
|
|
252
|
-
id = index + 1,
|
|
253
|
-
url = "",
|
|
254
|
-
)
|
|
255
|
-
},
|
|
256
|
-
)
|
|
257
|
-
}
|
|
258
|
-
}
|