From 26a20d6c9c701dca3e0982229a5174cfcdc41aa3 Mon Sep 17 00:00:00 2001 From: Yashraj254 Date: Sun, 30 Jul 2023 23:35:27 +0530 Subject: [PATCH 1/4] shuffle feature added --- .../core/model/ApplicationPreferences.kt | 3 ++- .../core/ui/designsystem/NextIcons.kt | 4 +++ .../feature/player/PlayerActivity.kt | 13 ++++++--- .../feature/player/PlayerViewModel.kt | 16 ++++++++--- .../mediaFolder/MediaPickerFolderScreen.kt | 27 +++++++++++++++++-- .../mediaFolder/MediaPickerFolderViewModel.kt | 22 ++++++++++++++- 6 files changed, 74 insertions(+), 11 deletions(-) diff --git a/core/model/src/main/java/dev/anilbeesetti/nextplayer/core/model/ApplicationPreferences.kt b/core/model/src/main/java/dev/anilbeesetti/nextplayer/core/model/ApplicationPreferences.kt index 30486caf2..50531f282 100644 --- a/core/model/src/main/java/dev/anilbeesetti/nextplayer/core/model/ApplicationPreferences.kt +++ b/core/model/src/main/java/dev/anilbeesetti/nextplayer/core/model/ApplicationPreferences.kt @@ -9,5 +9,6 @@ data class ApplicationPreferences( val groupVideosByFolder: Boolean = true, val themeConfig: ThemeConfig = ThemeConfig.SYSTEM, val useDynamicColors: Boolean = true, - val excludeFolders: List = emptyList() + val excludeFolders: List = emptyList(), + val isShuffleOn:Boolean = false ) diff --git a/core/ui/src/main/java/dev/anilbeesetti/nextplayer/core/ui/designsystem/NextIcons.kt b/core/ui/src/main/java/dev/anilbeesetti/nextplayer/core/ui/designsystem/NextIcons.kt index 0059499be..57affc2a2 100644 --- a/core/ui/src/main/java/dev/anilbeesetti/nextplayer/core/ui/designsystem/NextIcons.kt +++ b/core/ui/src/main/java/dev/anilbeesetti/nextplayer/core/ui/designsystem/NextIcons.kt @@ -37,6 +37,8 @@ import androidx.compose.material.icons.rounded.ResetTv import androidx.compose.material.icons.rounded.ScreenRotationAlt import androidx.compose.material.icons.rounded.Settings import androidx.compose.material.icons.rounded.Share +import androidx.compose.material.icons.rounded.Shuffle +import androidx.compose.material.icons.rounded.ShuffleOn import androidx.compose.material.icons.rounded.Speed import androidx.compose.material.icons.rounded.Straighten import androidx.compose.material.icons.rounded.Style @@ -91,6 +93,8 @@ object NextIcons { val Style = Icons.Rounded.Style val Subtitle = Icons.Rounded.Subtitles val Size = Icons.Rounded.CompareArrows + val Shuffle = Icons.Rounded.Shuffle + val ShuffleOn = Icons.Rounded.ShuffleOn val Speed = Icons.Rounded.Speed val SwipeHorizontal = Icons.Rounded.Swipe val SwipeVertical = Icons.Rounded.SwipeVertical diff --git a/feature/player/src/main/java/dev/anilbeesetti/nextplayer/feature/player/PlayerActivity.kt b/feature/player/src/main/java/dev/anilbeesetti/nextplayer/feature/player/PlayerActivity.kt index bf83448c2..2d2ac1604 100644 --- a/feature/player/src/main/java/dev/anilbeesetti/nextplayer/feature/player/PlayerActivity.kt +++ b/feature/player/src/main/java/dev/anilbeesetti/nextplayer/feature/player/PlayerActivity.kt @@ -75,6 +75,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import timber.log.Timber +import java.util.Collections @SuppressLint("UnsafeOptInUsageError") @AndroidEntryPoint @@ -254,13 +255,15 @@ class PlayerActivity : AppCompatActivity() { subtitleView?.let { val style = CaptionStyleCompat( Color.WHITE, - Color.BLACK.takeIf { playerPreferences.subtitleBackground } ?: Color.TRANSPARENT, + Color.BLACK.takeIf { playerPreferences.subtitleBackground } + ?: Color.TRANSPARENT, Color.TRANSPARENT, CaptionStyleCompat.EDGE_TYPE_DROP_SHADOW, Color.BLACK, Typeface.create( playerPreferences.subtitleFont.toTypeface(), - Typeface.BOLD.takeIf { playerPreferences.subtitleTextBold } ?: Typeface.NORMAL + Typeface.BOLD.takeIf { playerPreferences.subtitleTextBold } + ?: Typeface.NORMAL ) ) it.setStyle(style) @@ -372,7 +375,11 @@ class PlayerActivity : AppCompatActivity() { if (mediaUri != null) { launch(Dispatchers.IO) { - val playlist = viewModel.getPlaylistFromUri(mediaUri) + var playlist = viewModel.getPlaylistFromUri(mediaUri) + if (applicationPreferences.isShuffleOn) { + playlist = viewModel.getShuffledPlaylist(mediaUri) + Collections.swap(playlist, 0, playlist.indexOf(mediaUri)) + } playlistManager.setPlaylist(playlist) } } diff --git a/feature/player/src/main/java/dev/anilbeesetti/nextplayer/feature/player/PlayerViewModel.kt b/feature/player/src/main/java/dev/anilbeesetti/nextplayer/feature/player/PlayerViewModel.kt index 39f089c30..2538271df 100644 --- a/feature/player/src/main/java/dev/anilbeesetti/nextplayer/feature/player/PlayerViewModel.kt +++ b/feature/player/src/main/java/dev/anilbeesetti/nextplayer/feature/player/PlayerViewModel.kt @@ -55,9 +55,12 @@ class PlayerViewModel @Inject constructor( val prefs = playerPrefs.value currentPlaybackPosition = currentVideoState?.position.takeIf { prefs.resume == Resume.YES } - currentAudioTrackIndex = currentVideoState?.audioTrackIndex.takeIf { prefs.rememberSelections } - currentSubtitleTrackIndex = currentVideoState?.subtitleTrackIndex.takeIf { prefs.rememberSelections } - currentPlaybackSpeed = currentVideoState?.playbackSpeed.takeIf { prefs.rememberSelections } ?: prefs.defaultPlaybackSpeed + currentAudioTrackIndex = + currentVideoState?.audioTrackIndex.takeIf { prefs.rememberSelections } + currentSubtitleTrackIndex = + currentVideoState?.subtitleTrackIndex.takeIf { prefs.rememberSelections } + currentPlaybackSpeed = currentVideoState?.playbackSpeed.takeIf { prefs.rememberSelections } + ?: prefs.defaultPlaybackSpeed // TODO: update subs when stored in local storage } @@ -66,6 +69,10 @@ class PlayerViewModel @Inject constructor( return getSortedPlaylistUseCase.invoke(uri) } + suspend fun getShuffledPlaylist(uri: Uri): List { + return getPlaylistFromUri(uri).shuffled() + } + fun saveState( path: String?, position: Long, @@ -91,7 +98,8 @@ class PlayerViewModel @Inject constructor( position = newPosition, audioTrackIndex = audioTrackIndex, subtitleTrackIndex = subtitleTrackIndex, - playbackSpeed = playbackSpeed.takeIf { isPlaybackSpeedChanged } ?: currentVideoState?.playbackSpeed + playbackSpeed = playbackSpeed.takeIf { isPlaybackSpeedChanged } + ?: currentVideoState?.playbackSpeed ) } } diff --git a/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderScreen.kt b/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderScreen.kt index 2fffb2e25..48db1025e 100644 --- a/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderScreen.kt +++ b/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderScreen.kt @@ -1,16 +1,19 @@ package dev.anilbeesetti.nextplayer.feature.videopicker.screens.mediaFolder import android.net.Uri +import android.widget.Toast import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.Lifecycle @@ -39,7 +42,8 @@ fun MediaPickerFolderRoute( folderPath = viewModel.folderPath, videosState = videosState, onVideoClick = onVideoClick, - onNavigateUp = onNavigateUp + onNavigateUp = onNavigateUp, + viewModel = viewModel ) } @@ -49,8 +53,11 @@ internal fun MediaPickerFolderScreen( folderPath: String, videosState: VideosState, onVideoClick: (uri: Uri) -> Unit, - onNavigateUp: () -> Unit + onNavigateUp: () -> Unit, + viewModel: MediaPickerFolderViewModel ) { + val prefs = viewModel.appPrefs.collectAsStateWithLifecycle() + val context = LocalContext.current Column { NextTopAppBar( title = File(folderPath).prettyName, @@ -61,6 +68,22 @@ internal fun MediaPickerFolderScreen( contentDescription = stringResource(id = R.string.navigate_up) ) } + }, + actions = { + IconButton(onClick = { + if(!prefs.value.isShuffleOn) + Toast.makeText(context,"Shuffle disabled",Toast.LENGTH_SHORT).show() + else + Toast.makeText(context,"Shuffle enabled",Toast.LENGTH_SHORT).show() + viewModel.toggleShuffle() + }) { + Icon( + imageVector = if (prefs.value.isShuffleOn) + NextIcons.ShuffleOn else NextIcons.Shuffle, + contentDescription = "", + tint = if (prefs.value.isShuffleOn) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.secondary + ) + } } ) Box( diff --git a/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderViewModel.kt b/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderViewModel.kt index e4537799d..ff7f3f13b 100644 --- a/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderViewModel.kt +++ b/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderViewModel.kt @@ -4,24 +4,34 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import dev.anilbeesetti.nextplayer.core.data.repository.PreferencesRepository import dev.anilbeesetti.nextplayer.core.domain.GetSortedVideosUseCase +import dev.anilbeesetti.nextplayer.core.model.ApplicationPreferences import dev.anilbeesetti.nextplayer.feature.videopicker.navigation.FolderArgs import dev.anilbeesetti.nextplayer.feature.videopicker.screens.VideosState import javax.inject.Inject import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch @HiltViewModel class MediaPickerFolderViewModel @Inject constructor( getSortedVideosUseCase: GetSortedVideosUseCase, - savedStateHandle: SavedStateHandle + savedStateHandle: SavedStateHandle, + private val preferencesRepository: PreferencesRepository ) : ViewModel() { private val folderArgs = FolderArgs(savedStateHandle) val folderPath = folderArgs.folderId + val appPrefs = preferencesRepository.applicationPreferences.stateIn( + scope = viewModelScope, + started = SharingStarted.Eagerly, + initialValue = ApplicationPreferences() + ) + val videos = getSortedVideosUseCase.invoke(folderPath) .map { VideosState.Success(it) } .stateIn( @@ -29,4 +39,14 @@ class MediaPickerFolderViewModel @Inject constructor( started = SharingStarted.WhileSubscribed(5000), initialValue = VideosState.Loading ) + + fun toggleShuffle() { + viewModelScope.launch { + preferencesRepository.updateApplicationPreferences { + it.copy( + isShuffleOn = !it.isShuffleOn + ) + } + } + } } From 3ad304388dc916a9704015ce2db578975d6c34e0 Mon Sep 17 00:00:00 2001 From: Yashraj254 Date: Mon, 31 Jul 2023 16:00:38 +0530 Subject: [PATCH 2/4] shuffling toast message bug fixed --- core/ui/src/main/res/values/strings.xml | 2 ++ .../mediaFolder/MediaPickerFolderScreen.kt | 25 ++++++++++++------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/core/ui/src/main/res/values/strings.xml b/core/ui/src/main/res/values/strings.xml index c2b05705f..134ce7258 100644 --- a/core/ui/src/main/res/values/strings.xml +++ b/core/ui/src/main/res/values/strings.xml @@ -140,4 +140,6 @@ Delete Delete the following file Subtitle text encoding + Shuffle enabled + Shuffle disabled \ No newline at end of file diff --git a/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderScreen.kt b/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderScreen.kt index 48db1025e..08703463e 100644 --- a/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderScreen.kt +++ b/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderScreen.kt @@ -71,18 +71,25 @@ internal fun MediaPickerFolderScreen( }, actions = { IconButton(onClick = { - if(!prefs.value.isShuffleOn) - Toast.makeText(context,"Shuffle disabled",Toast.LENGTH_SHORT).show() + if (prefs.value.isShuffleOn) + Toast.makeText(context, R.string.shuffle_disabled, Toast.LENGTH_SHORT) + .show() else - Toast.makeText(context,"Shuffle enabled",Toast.LENGTH_SHORT).show() + Toast.makeText(context, R.string.shuffle_enabled, Toast.LENGTH_SHORT).show() viewModel.toggleShuffle() }) { - Icon( - imageVector = if (prefs.value.isShuffleOn) - NextIcons.ShuffleOn else NextIcons.Shuffle, - contentDescription = "", - tint = if (prefs.value.isShuffleOn) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.secondary - ) + if (prefs.value.isShuffleOn) + Icon( + imageVector = NextIcons.ShuffleOn, + contentDescription = stringResource(id = R.string.shuffle_enabled), + tint = MaterialTheme.colorScheme.primary + ) + else + Icon( + imageVector = NextIcons.Shuffle, + contentDescription = stringResource(id = R.string.shuffle_enabled), + tint = MaterialTheme.colorScheme.secondary + ) } } ) From 5a7be07dea8ed9de76a9cc42b233d95de915d2a9 Mon Sep 17 00:00:00 2001 From: Yashraj254 Date: Mon, 31 Jul 2023 17:07:28 +0530 Subject: [PATCH 3/4] play on shuffle --- .../screens/mediaFolder/MediaPickerFolderScreen.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderScreen.kt b/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderScreen.kt index 08703463e..46170ccab 100644 --- a/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderScreen.kt +++ b/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderScreen.kt @@ -74,8 +74,15 @@ internal fun MediaPickerFolderScreen( if (prefs.value.isShuffleOn) Toast.makeText(context, R.string.shuffle_disabled, Toast.LENGTH_SHORT) .show() - else + else { Toast.makeText(context, R.string.shuffle_enabled, Toast.LENGTH_SHORT).show() + when(videosState){ + is VideosState.Success->{ + onVideoClick(Uri.parse(videosState.data.shuffled().first().uriString)) + } + else -> { } + } + } viewModel.toggleShuffle() }) { if (prefs.value.isShuffleOn) From f866a26c10938770c85901d1d307541b012783c4 Mon Sep 17 00:00:00 2001 From: Yashraj254 Date: Tue, 1 Aug 2023 15:16:19 +0530 Subject: [PATCH 4/4] code styling improved --- .../core/model/ApplicationPreferences.kt | 2 +- .../feature/player/PlayerActivity.kt | 6 +- .../mediaFolder/MediaPickerFolderScreen.kt | 83 ++++++++++--------- .../mediaFolder/MediaPickerFolderViewModel.kt | 12 ++- 4 files changed, 51 insertions(+), 52 deletions(-) diff --git a/core/model/src/main/java/dev/anilbeesetti/nextplayer/core/model/ApplicationPreferences.kt b/core/model/src/main/java/dev/anilbeesetti/nextplayer/core/model/ApplicationPreferences.kt index 50531f282..5e8d9315f 100644 --- a/core/model/src/main/java/dev/anilbeesetti/nextplayer/core/model/ApplicationPreferences.kt +++ b/core/model/src/main/java/dev/anilbeesetti/nextplayer/core/model/ApplicationPreferences.kt @@ -10,5 +10,5 @@ data class ApplicationPreferences( val themeConfig: ThemeConfig = ThemeConfig.SYSTEM, val useDynamicColors: Boolean = true, val excludeFolders: List = emptyList(), - val isShuffleOn:Boolean = false + val isShuffleOn: Boolean = false ) diff --git a/feature/player/src/main/java/dev/anilbeesetti/nextplayer/feature/player/PlayerActivity.kt b/feature/player/src/main/java/dev/anilbeesetti/nextplayer/feature/player/PlayerActivity.kt index 2d2ac1604..9bf308dcb 100644 --- a/feature/player/src/main/java/dev/anilbeesetti/nextplayer/feature/player/PlayerActivity.kt +++ b/feature/player/src/main/java/dev/anilbeesetti/nextplayer/feature/player/PlayerActivity.kt @@ -255,15 +255,13 @@ class PlayerActivity : AppCompatActivity() { subtitleView?.let { val style = CaptionStyleCompat( Color.WHITE, - Color.BLACK.takeIf { playerPreferences.subtitleBackground } - ?: Color.TRANSPARENT, + Color.BLACK.takeIf { playerPreferences.subtitleBackground } ?: Color.TRANSPARENT, Color.TRANSPARENT, CaptionStyleCompat.EDGE_TYPE_DROP_SHADOW, Color.BLACK, Typeface.create( playerPreferences.subtitleFont.toTypeface(), - Typeface.BOLD.takeIf { playerPreferences.subtitleTextBold } - ?: Typeface.NORMAL + Typeface.BOLD.takeIf { playerPreferences.subtitleTextBold } ?: Typeface.NORMAL ) ) it.setStyle(style) diff --git a/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderScreen.kt b/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderScreen.kt index 46170ccab..369092d93 100644 --- a/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderScreen.kt +++ b/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderScreen.kt @@ -59,51 +59,54 @@ internal fun MediaPickerFolderScreen( val prefs = viewModel.appPrefs.collectAsStateWithLifecycle() val context = LocalContext.current Column { - NextTopAppBar( - title = File(folderPath).prettyName, - navigationIcon = { - IconButton(onClick = onNavigateUp) { - Icon( - imageVector = NextIcons.ArrowBack, - contentDescription = stringResource(id = R.string.navigate_up) - ) - } - }, - actions = { - IconButton(onClick = { - if (prefs.value.isShuffleOn) - Toast.makeText(context, R.string.shuffle_disabled, Toast.LENGTH_SHORT) - .show() - else { - Toast.makeText(context, R.string.shuffle_enabled, Toast.LENGTH_SHORT).show() - when(videosState){ - is VideosState.Success->{ - onVideoClick(Uri.parse(videosState.data.shuffled().first().uriString)) - } - else -> { } + NextTopAppBar(title = File(folderPath).prettyName, navigationIcon = { + IconButton(onClick = onNavigateUp) { + Icon( + imageVector = NextIcons.ArrowBack, + contentDescription = stringResource(id = R.string.navigate_up) + ) + } + }, actions = { + IconButton(onClick = { + if (prefs.value.isShuffleOn) { + Toast.makeText( + context, + R.string.shuffle_disabled, + Toast.LENGTH_SHORT + ).show() + } else { + Toast.makeText(context, R.string.shuffle_enabled, Toast.LENGTH_SHORT).show() + when (videosState) { + is VideosState.Success -> { + onVideoClick( + Uri.parse( + videosState.data.shuffled().first().uriString + ) + ) } + + else -> {} } - viewModel.toggleShuffle() - }) { - if (prefs.value.isShuffleOn) - Icon( - imageVector = NextIcons.ShuffleOn, - contentDescription = stringResource(id = R.string.shuffle_enabled), - tint = MaterialTheme.colorScheme.primary - ) - else - Icon( - imageVector = NextIcons.Shuffle, - contentDescription = stringResource(id = R.string.shuffle_enabled), - tint = MaterialTheme.colorScheme.secondary - ) + } + viewModel.toggleShuffle() + }) { + if (prefs.value.isShuffleOn) { + Icon( + imageVector = NextIcons.ShuffleOn, + contentDescription = stringResource(id = R.string.shuffle_enabled), + tint = MaterialTheme.colorScheme.primary + ) + } else { + Icon( + imageVector = NextIcons.Shuffle, + contentDescription = stringResource(id = R.string.shuffle_enabled), + tint = MaterialTheme.colorScheme.secondary + ) } } - ) + }) Box( - modifier = Modifier - .fillMaxSize(), - contentAlignment = Alignment.Center + modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { VideosListFromState(videosState = videosState, onVideoClick = onVideoClick) } diff --git a/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderViewModel.kt b/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderViewModel.kt index ff7f3f13b..27e52a516 100644 --- a/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderViewModel.kt +++ b/feature/videopicker/src/main/java/dev/anilbeesetti/nextplayer/feature/videopicker/screens/mediaFolder/MediaPickerFolderViewModel.kt @@ -32,13 +32,11 @@ class MediaPickerFolderViewModel @Inject constructor( initialValue = ApplicationPreferences() ) - val videos = getSortedVideosUseCase.invoke(folderPath) - .map { VideosState.Success(it) } - .stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5000), - initialValue = VideosState.Loading - ) + val videos = getSortedVideosUseCase.invoke(folderPath).map { VideosState.Success(it) }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = VideosState.Loading + ) fun toggleShuffle() { viewModelScope.launch {