From 41d601189ab2850cca90a451b8dee4fb3bc64b32 Mon Sep 17 00:00:00 2001 From: AhmedEhab1 Date: Tue, 27 May 2025 19:33:48 +0300 Subject: [PATCH] add audio and video player samplers --- .../metacto/catalogapp/di/ViewModelsModule.kt | 4 + .../AudioPlayerSampleContract.kt | 18 +++++ .../AudioPlayerSampleScreen.kt | 28 +++++++ .../AudioPlayerSampleViewModel.kt | 25 ++++++ .../components/AudioPlayerSampleContent.kt | 76 +++++++++++++++++++ .../main/components/MainContent.kt | 21 ++++- .../VideoPlayerSampleContract.kt | 18 +++++ .../VideoPlayerSampleScreen.kt | 28 +++++++ .../VideoPlayerSampleViewModel.kt | 25 ++++++ .../components/VideoPlayerSampleContent.kt | 68 +++++++++++++++++ 10 files changed, 310 insertions(+), 1 deletion(-) create mode 100644 samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/audioPlayer/audioplayersample/AudioPlayerSampleContract.kt create mode 100644 samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/audioPlayer/audioplayersample/AudioPlayerSampleScreen.kt create mode 100644 samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/audioPlayer/audioplayersample/AudioPlayerSampleViewModel.kt create mode 100644 samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/audioPlayer/audioplayersample/components/AudioPlayerSampleContent.kt create mode 100644 samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/videoPlayer/videoplayersample/VideoPlayerSampleContract.kt create mode 100644 samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/videoPlayer/videoplayersample/VideoPlayerSampleScreen.kt create mode 100644 samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/videoPlayer/videoplayersample/VideoPlayerSampleViewModel.kt create mode 100644 samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/videoPlayer/videoplayersample/components/VideoPlayerSampleContent.kt diff --git a/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/di/ViewModelsModule.kt b/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/di/ViewModelsModule.kt index 94602694..5c0b497d 100644 --- a/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/di/ViewModelsModule.kt +++ b/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/di/ViewModelsModule.kt @@ -1,6 +1,8 @@ package com.metacto.catalogapp.di // MARK: Add imports +import com.metacto.catalogapp.presentation.videoPlayer.videoplayersample.VideoPlayerSampleViewModel +import com.metacto.catalogapp.presentation.audioPlayer.audioplayersample.AudioPlayerSampleViewModel import com.metacto.catalogapp.presentation.app.app.AppViewModel import com.metacto.catalogapp.presentation.files.FilesSamplesViewModel import com.metacto.catalogapp.presentation.imagePicker.ImagePickerSamplesViewModel @@ -14,6 +16,8 @@ import org.koin.dsl.module val viewModelsModule = module { // MARK: Add view model definitions + commonViewModel { VideoPlayerSampleViewModel() } + commonViewModel { AudioPlayerSampleViewModel() } commonViewModel { ImagePreloaderViewModel() } commonViewModel { MediaManagerViewModel() } commonViewModel { PhoneNumberViewModel() } diff --git a/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/audioPlayer/audioplayersample/AudioPlayerSampleContract.kt b/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/audioPlayer/audioplayersample/AudioPlayerSampleContract.kt new file mode 100644 index 00000000..28b70341 --- /dev/null +++ b/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/audioPlayer/audioplayersample/AudioPlayerSampleContract.kt @@ -0,0 +1,18 @@ +package com.metacto.catalogapp.presentation.audioPlayer.audioplayersample + +import com.metacto.core.ui.base.ViewEvent +import com.metacto.core.ui.base.ViewSideEffect +import com.metacto.core.ui.base.ViewState + +class AudioPlayerSampleContract { + + data class State( + val isInitialized: Boolean = false + ) : ViewState + + sealed class Event : ViewEvent { + data object Init : Event() + } + + sealed class Effect : ViewSideEffect +} diff --git a/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/audioPlayer/audioplayersample/AudioPlayerSampleScreen.kt b/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/audioPlayer/audioplayersample/AudioPlayerSampleScreen.kt new file mode 100644 index 00000000..f74a3859 --- /dev/null +++ b/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/audioPlayer/audioplayersample/AudioPlayerSampleScreen.kt @@ -0,0 +1,28 @@ +package com.metacto.catalogapp.presentation.audioPlayer.audioplayersample + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import com.metacto.catalogapp.presentation.audioPlayer.audioplayersample.AudioPlayerSampleContract.Event +import com.metacto.catalogapp.presentation.audioPlayer.audioplayersample.components.AudioPlayerSampleContent +import com.metacto.core.ui.base.BaseScreen +import com.metacto.core.ui.base.SIDE_EFFECTS_KEY +import com.metacto.core.ui.base.rememberViewModel + +internal class AudioPlayerSampleScreen : BaseScreen() { + @Composable + override fun Content() { + // Get the view model + val viewModel = rememberViewModel() + + // Init view model + LaunchedEffect(SIDE_EFFECTS_KEY) { + viewModel.setEvent(Event.Init) + } + + // Render content + AudioPlayerSampleContent( + state = viewModel.viewState.value, + onEvent = viewModel::setEvent + ) + } +} diff --git a/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/audioPlayer/audioplayersample/AudioPlayerSampleViewModel.kt b/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/audioPlayer/audioplayersample/AudioPlayerSampleViewModel.kt new file mode 100644 index 00000000..147a277c --- /dev/null +++ b/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/audioPlayer/audioplayersample/AudioPlayerSampleViewModel.kt @@ -0,0 +1,25 @@ +package com.metacto.catalogapp.presentation.audioPlayer.audioplayersample + +import com.metacto.catalogapp.presentation.base.BaseViewModel +import com.metacto.catalogapp.presentation.audioPlayer.audioplayersample.AudioPlayerSampleContract.Effect +import com.metacto.catalogapp.presentation.audioPlayer.audioplayersample.AudioPlayerSampleContract.Event +import com.metacto.catalogapp.presentation.audioPlayer.audioplayersample.AudioPlayerSampleContract.State + +class AudioPlayerSampleViewModel : BaseViewModel() { + + override fun setInitialState() = State() + + override fun handleEvents(event: Event): Any = when (event) { + Event.Init -> init() + } + + private fun init() { + // Validate if already initialized + if (currentState.isInitialized) return + + // Init + + // Update the flag + setState { copy(isInitialized = true) } + } +} diff --git a/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/audioPlayer/audioplayersample/components/AudioPlayerSampleContent.kt b/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/audioPlayer/audioplayersample/components/AudioPlayerSampleContent.kt new file mode 100644 index 00000000..bf3dbabf --- /dev/null +++ b/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/audioPlayer/audioplayersample/components/AudioPlayerSampleContent.kt @@ -0,0 +1,76 @@ +package com.metacto.catalogapp.presentation.audioPlayer.audioplayersample.components + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import com.metacto.catalogapp.presentation.audioPlayer.audioplayersample.AudioPlayerSampleContract.Event +import com.metacto.catalogapp.presentation.audioPlayer.audioplayersample.AudioPlayerSampleContract.State +import com.metacto.catalogapp.presentation.components.containers.AppScreenColumn +import com.metacto.catalogapp.presentation.theme.spacings +import com.metacto.core.ui.components.inputFields.PrimaryTextInputField +import com.metacto.core.ui.mediaplayers.audioPlayer.AudioPlayer +import com.metacto.core.ui.mediaplayers.audioPlayer.AudioPlayerStatusListener +import com.metacto.core.ui.navigation.NavManager +import org.koin.compose.koinInject + +@Composable +internal fun AudioPlayerSampleContent( + state: State, + onEvent: (Event) -> Unit +) { + // audio test link + // https://www.learningcontainer.com/wp-content/uploads/2020/02/Kalimba.mp3 + + // Di + val navManager = koinInject() + + // states + var audioUrl by remember { mutableStateOf("") } + + // Container column + AppScreenColumn( + title = "AudioPlayerSample", + isScrollable = true, + showToolbar = true, + showBack = true, + onBackClick = { + navManager.goBack() + }, + ) { + // Audio url input field + PrimaryTextInputField( + text = audioUrl, + label = "Audio Url", + onValueChange = { + audioUrl = it + }, + modifier = Modifier + .fillMaxWidth() + .padding(top = spacings.spacing16) + ) + + // Audio player + AudioPlayer( + audioUrl = audioUrl, + title = "audio title", + thumbnailUrl = "", + audioPlayerStatusListener = object : AudioPlayerStatusListener { + override fun onAudioPlayed() { + } + + override fun onAudioPaused() { + + } + }, + modifier = Modifier + .fillMaxWidth() + .padding(top = spacings.spacing16) + ) + + } +} diff --git a/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/main/components/MainContent.kt b/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/main/components/MainContent.kt index 1a439eae..e679eae3 100644 --- a/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/main/components/MainContent.kt +++ b/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/main/components/MainContent.kt @@ -3,10 +3,10 @@ package com.metacto.catalogapp.presentation.main.components import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import com.metacto.catalogapp.presentation.audioPlayer.audioplayersample.AudioPlayerSampleScreen import com.metacto.catalogapp.presentation.files.FilesSamplesScreen import com.metacto.catalogapp.presentation.imagePicker.ImagePickerSamplesScreen import com.metacto.catalogapp.presentation.imagePreloader.imagepreloader.ImagePreloaderScreen @@ -16,6 +16,7 @@ import com.metacto.catalogapp.presentation.mediaManager.MediaManagerScreen import com.metacto.catalogapp.presentation.notifications.NotificationsSamplesScreen import com.metacto.catalogapp.presentation.phoneNumber.PhoneNumberScreen import com.metacto.catalogapp.presentation.theme.spacings +import com.metacto.catalogapp.presentation.videoPlayer.videoplayersample.VideoPlayerSampleScreen import com.metacto.core.ui.components.buttons.PrimaryFilledButton import com.metacto.core.ui.components.containers.ScreenColumn import com.metacto.core.ui.navigation.NavManager @@ -83,5 +84,23 @@ internal fun MainContent( }, modifier = Modifier.fillMaxWidth() ) + + // Audio Player samples + PrimaryFilledButton( + text = "Audio Player samples", + onClick = { + navManage.navigate(AudioPlayerSampleScreen()) + }, + modifier = Modifier.fillMaxWidth() + ) + + // Video Player samples + PrimaryFilledButton( + text = "Video Player samples", + onClick = { + navManage.navigate(VideoPlayerSampleScreen()) + }, + modifier = Modifier.fillMaxWidth() + ) } } \ No newline at end of file diff --git a/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/videoPlayer/videoplayersample/VideoPlayerSampleContract.kt b/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/videoPlayer/videoplayersample/VideoPlayerSampleContract.kt new file mode 100644 index 00000000..3f179d3b --- /dev/null +++ b/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/videoPlayer/videoplayersample/VideoPlayerSampleContract.kt @@ -0,0 +1,18 @@ +package com.metacto.catalogapp.presentation.videoPlayer.videoplayersample + +import com.metacto.core.ui.base.ViewEvent +import com.metacto.core.ui.base.ViewSideEffect +import com.metacto.core.ui.base.ViewState + +class VideoPlayerSampleContract { + + data class State( + val isInitialized: Boolean = false + ) : ViewState + + sealed class Event : ViewEvent { + data object Init : Event() + } + + sealed class Effect : ViewSideEffect +} diff --git a/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/videoPlayer/videoplayersample/VideoPlayerSampleScreen.kt b/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/videoPlayer/videoplayersample/VideoPlayerSampleScreen.kt new file mode 100644 index 00000000..4d1ff8cb --- /dev/null +++ b/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/videoPlayer/videoplayersample/VideoPlayerSampleScreen.kt @@ -0,0 +1,28 @@ +package com.metacto.catalogapp.presentation.videoPlayer.videoplayersample + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import com.metacto.catalogapp.presentation.videoPlayer.videoplayersample.VideoPlayerSampleContract.Event +import com.metacto.catalogapp.presentation.videoPlayer.videoplayersample.components.VideoPlayerSampleContent +import com.metacto.core.ui.base.BaseScreen +import com.metacto.core.ui.base.SIDE_EFFECTS_KEY +import com.metacto.core.ui.base.rememberViewModel + +internal class VideoPlayerSampleScreen : BaseScreen() { + @Composable + override fun Content() { + // Get the view model + val viewModel = rememberViewModel() + + // Init view model + LaunchedEffect(SIDE_EFFECTS_KEY) { + viewModel.setEvent(Event.Init) + } + + // Render content + VideoPlayerSampleContent( + state = viewModel.viewState.value, + onEvent = viewModel::setEvent + ) + } +} diff --git a/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/videoPlayer/videoplayersample/VideoPlayerSampleViewModel.kt b/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/videoPlayer/videoplayersample/VideoPlayerSampleViewModel.kt new file mode 100644 index 00000000..6d59c0de --- /dev/null +++ b/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/videoPlayer/videoplayersample/VideoPlayerSampleViewModel.kt @@ -0,0 +1,25 @@ +package com.metacto.catalogapp.presentation.videoPlayer.videoplayersample + +import com.metacto.catalogapp.presentation.base.BaseViewModel +import com.metacto.catalogapp.presentation.videoPlayer.videoplayersample.VideoPlayerSampleContract.Effect +import com.metacto.catalogapp.presentation.videoPlayer.videoplayersample.VideoPlayerSampleContract.Event +import com.metacto.catalogapp.presentation.videoPlayer.videoplayersample.VideoPlayerSampleContract.State + +class VideoPlayerSampleViewModel : BaseViewModel() { + + override fun setInitialState() = State() + + override fun handleEvents(event: Event): Any = when (event) { + Event.Init -> init() + } + + private fun init() { + // Validate if already initialized + if (currentState.isInitialized) return + + // Init + + // Update the flag + setState { copy(isInitialized = true) } + } +} diff --git a/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/videoPlayer/videoplayersample/components/VideoPlayerSampleContent.kt b/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/videoPlayer/videoplayersample/components/VideoPlayerSampleContent.kt new file mode 100644 index 00000000..7eaf67af --- /dev/null +++ b/samples/catalog-app-shared/src/commonMain/kotlin/com/metacto/catalogapp/presentation/videoPlayer/videoplayersample/components/VideoPlayerSampleContent.kt @@ -0,0 +1,68 @@ +package com.metacto.catalogapp.presentation.videoPlayer.videoplayersample.components + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import com.metacto.catalogapp.presentation.components.containers.AppScreenColumn +import com.metacto.catalogapp.presentation.theme.spacings +import com.metacto.catalogapp.presentation.videoPlayer.videoplayersample.VideoPlayerSampleContract.Event +import com.metacto.catalogapp.presentation.videoPlayer.videoplayersample.VideoPlayerSampleContract.State +import com.metacto.core.ui.components.inputFields.PrimaryTextInputField +import com.metacto.core.ui.mediaplayers.videoPlayer.VideoPlayer +import com.metacto.core.ui.navigation.NavManager +import org.koin.compose.koinInject + +@Composable +internal fun VideoPlayerSampleContent( + state: State, + onEvent: (Event) -> Unit +) { + // test video url + // http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4 + + // Di + val navManager = koinInject() + + // states + var videoUrl by remember { mutableStateOf("") } + + // Container column + AppScreenColumn( + title = "VideoPlayerSample", + isScrollable = true, + showToolbar = true, + showBack = true, + onBackClick = { + navManager.goBack() + }, + ) { + // Video url input field + PrimaryTextInputField( + text = videoUrl, + label = "Video Url", + onValueChange = { + videoUrl = it + }, + modifier = Modifier + .fillMaxWidth() + .padding(top = spacings.spacing16) + ) + + // Video Player + if (videoUrl.isNotEmpty()) { + VideoPlayer( + videoUrl = videoUrl, + modifier = Modifier + .fillMaxWidth() + .height(spacings.spacing500) + .padding(top = spacings.spacing32) + ) + } + } +}