From a1fea5a0ae439af38bf1befff6deb39ce0e0c90e Mon Sep 17 00:00:00 2001 From: DongHyeon Park Date: Fri, 26 May 2023 15:35:52 +0900 Subject: [PATCH 01/13] =?UTF-8?q?docs:=20README=20=EA=B0=B1=EC=8B=A0=20(Te?= =?UTF-8?q?ch=20Stacks=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c1aedb56..76bbf54f 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,7 @@ DailyFilm에 마음껏 기록해두실 수 있습니다.* |Image&Video Loading| | |CI/CD|| |Asynchronous|| -|UI| | +|UI| | |Service| | |QA & App Publishing|| From 4ecb9646ff3114d7e70e279a139ff3a4d224bd3a Mon Sep 17 00:00:00 2001 From: junhyeongleeee Date: Thu, 15 Jun 2023 19:47:26 +0900 Subject: [PATCH 02/13] =?UTF-8?q?issue=20#160=20chore:=20OnBackground=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: junhyeongleeee --- app/src/main/res/values/colors.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 99574093..c3a710ba 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -19,7 +19,7 @@ #B00020 #FFFFFF #000000 - #FFFFFF + #000000 #000000 #FFFFFF \ No newline at end of file From f503ced2d9e4e709f8575395dd3951ad5b0de644 Mon Sep 17 00:00:00 2001 From: junhyeongleeee Date: Thu, 15 Jun 2023 19:49:16 +0900 Subject: [PATCH 03/13] =?UTF-8?q?issue=20#160=20chore:=20java=20version=20?= =?UTF-8?q?17=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: junhyeongleeee --- app/build.gradle | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 81103d26..3c685f4e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -30,11 +30,11 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_11 - targetCompatibility JavaVersion.VERSION_11 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = '11' + jvmTarget = '17' } testOptions { unitTests.returnDefaultValues = true @@ -110,6 +110,8 @@ dependencies { // Exoplayer implementation 'com.google.android.exoplayer:exoplayer:2.18.1' + implementation 'androidx.media3:media3-exoplayer:1.1.0-alpha01' + implementation "androidx.media3:media3-ui:1.1.0-alpha01" // Coordinator-layout implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0' // ffmpeg @@ -117,6 +119,7 @@ dependencies { implementation 'com.arthenica:mobile-ffmpeg-min-gpl:4.4' // lottie implementation 'com.airbnb.android:lottie:5.0.2' + implementation "com.airbnb.android:lottie-compose:5.0.2" // dataStore implementation("androidx.datastore:datastore-preferences:1.0.0") implementation("androidx.datastore:datastore-core:1.0.0") From 0ecaf21a6c8e9b4c31ab282714b1ab98e253552a Mon Sep 17 00:00:00 2001 From: junhyeongleeee Date: Thu, 15 Jun 2023 19:49:55 +0900 Subject: [PATCH 04/13] =?UTF-8?q?issue=20#160=20feat:=20theme=20=EC=97=90?= =?UTF-8?q?=20=EB=A7=9E=EA=B2=8C=20color=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: junhyeongleeee --- .../dailyfilm/presentation/util/dialog/CustomDialog.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/util/dialog/CustomDialog.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/util/dialog/CustomDialog.kt index 1900eec2..15309d5f 100644 --- a/app/src/main/java/com/boostcamp/dailyfilm/presentation/util/dialog/CustomDialog.kt +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/util/dialog/CustomDialog.kt @@ -13,7 +13,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -29,14 +29,14 @@ fun CustomDialog(text: String, onDismiss: () -> Unit, confirm: () -> Unit) { .fillMaxWidth() .wrapContentHeight() .clip(RoundedCornerShape(12.dp)) - .background(color = Color.White), + .background(color = colorResource(id = R.color.Background)), ) { Text( modifier = Modifier .padding(top = 24.dp) .padding(horizontal = 24.dp), text = text, - color = Color.Black, + color = colorResource(id = R.color.OnBackground), ) Row( modifier = Modifier @@ -49,13 +49,13 @@ fun CustomDialog(text: String, onDismiss: () -> Unit, confirm: () -> Unit) { modifier = Modifier .padding(end = 20.dp) .clickable(onClick = onDismiss), - color = Color.Black, + color = colorResource(id = R.color.OnBackground), ) Text( text = stringResource(id = R.string.confirm), modifier = Modifier .clickable(onClick = confirm), - color = Color.Black, + color = colorResource(id = R.color.OnBackground), ) } } From d890530b491894fd8428b36a30319e7d3230763b Mon Sep 17 00:00:00 2001 From: junhyeongleeee Date: Thu, 15 Jun 2023 19:50:21 +0900 Subject: [PATCH 05/13] =?UTF-8?q?issue=20#160=20chore:=20sound=20lottie=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: junhyeongleeee --- app/src/main/res/values/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 552ad814..9bd10eb7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -49,4 +49,5 @@ 정말 탈퇴 하시겠습니까? 계정 설정 화면 + sound_lottie.json \ No newline at end of file From 2585704aabba39d1bab60454d75c53be8d61eddb Mon Sep 17 00:00:00 2001 From: junhyeongleeee Date: Thu, 15 Jun 2023 19:52:51 +0900 Subject: [PATCH 06/13] =?UTF-8?q?issue=20#160=20refactor:=20confirm=20?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EB=B0=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: junhyeongleeee --- .../presentation/settings/SettingsViewModel.kt | 17 +++++++---------- .../settings/compose/SettingComposeActivity.kt | 2 +- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/settings/SettingsViewModel.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/settings/SettingsViewModel.kt index a2f0a96f..1f709c8c 100644 --- a/app/src/main/java/com/boostcamp/dailyfilm/presentation/settings/SettingsViewModel.kt +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/settings/SettingsViewModel.kt @@ -22,20 +22,17 @@ class SettingsViewModel @Inject constructor( private val settingsRepository: SettingsRepository, private val syncRepository: SyncRepository ) : ViewModel() { - private val _settingsEventFlow = MutableStateFlow(SettingsEvent.Initialized) val settingsEventFlow: StateFlow = _settingsEventFlow.asStateFlow() - private val _openDialog = MutableStateFlow(DialogState(false, "") {}) - val openDialog: StateFlow get() = _openDialog - - fun openDialog(content: String, execution: () -> Unit) { - _openDialog.value = - openDialog.value.copy(openDialog = true, content = content, execution = execution) - } + private val _openDialog = MutableStateFlow(DialogState(false, "", {})) + val openDialog : StateFlow get() = _openDialog fun closeDialog() { - _openDialog.value = openDialog.value.copy(openDialog = false) + _openDialog.value = _openDialog.value.copy(openDialog = false) + } + fun openDialog(content: String, confirm: () -> Unit) { + _openDialog.value = _openDialog.value.copy(content = content, confirm = confirm) } fun backToPrevious() = event(SettingsEvent.Back) @@ -82,7 +79,7 @@ class SettingsViewModel @Inject constructor( data class DialogState( val openDialog: Boolean, val content: String, - val execution: () -> Unit, + val confirm: () -> Unit, ) sealed class SettingsEvent { diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/settings/compose/SettingComposeActivity.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/settings/compose/SettingComposeActivity.kt index 8c04405d..1850c57a 100644 --- a/app/src/main/java/com/boostcamp/dailyfilm/presentation/settings/compose/SettingComposeActivity.kt +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/settings/compose/SettingComposeActivity.kt @@ -179,7 +179,7 @@ fun DialogUI(viewModel: SettingsViewModel) { onDismiss = { viewModel.closeDialog() }, - confirm = openDialog.execution, + confirm = openDialog.confirm, ) } } From e138aea5d62c1da79bd67e384cee249f9dab30ec Mon Sep 17 00:00:00 2001 From: junhyeongleeee Date: Thu, 15 Jun 2023 19:55:21 +0900 Subject: [PATCH 07/13] =?UTF-8?q?issue=20#160=20feat:=20liveData=20->=20st?= =?UTF-8?q?ateFlow,=20dialog=20=ED=99=80=EB=8D=94=EB=B3=80=EC=88=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: junhyeongleeee --- .../presentation/playfilm/PlayFilmFragment.kt | 8 +--- .../playfilm/PlayFilmViewModel.kt | 41 +++++++++++++------ 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/PlayFilmFragment.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/PlayFilmFragment.kt index 8e47acb2..f3576578 100644 --- a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/PlayFilmFragment.kt +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/PlayFilmFragment.kt @@ -14,13 +14,9 @@ import androidx.fragment.app.viewModels import com.boostcamp.dailyfilm.R import com.boostcamp.dailyfilm.databinding.FragmentPlayFilmBinding import com.boostcamp.dailyfilm.presentation.BaseFragment -import com.boostcamp.dailyfilm.presentation.calendar.CalendarActivity -import com.boostcamp.dailyfilm.presentation.calendar.DateFragment.Companion.KEY_CALENDAR_INDEX import com.boostcamp.dailyfilm.presentation.calendar.model.DateModel import com.boostcamp.dailyfilm.presentation.util.network.NetworkManager import com.boostcamp.dailyfilm.presentation.util.network.NetworkState -import com.boostcamp.dailyfilm.presentation.util.PlayState -import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint @@ -97,7 +93,7 @@ class PlayFilmFragment : BaseFragment(R.layout.fragment } }*/ - viewModel.playState.observe(viewLifecycleOwner) { + /*viewModel.playState.observe(viewLifecycleOwner) { when(it) { is PlayState.Uninitialized -> {} is PlayState.Loading -> {} @@ -121,7 +117,7 @@ class PlayFilmFragment : BaseFragment(R.layout.fragment } } } - } + }*/ } private fun initListener() { diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/PlayFilmViewModel.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/PlayFilmViewModel.kt index c9fd7b9b..288de989 100644 --- a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/PlayFilmViewModel.kt +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/PlayFilmViewModel.kt @@ -2,16 +2,22 @@ package com.boostcamp.dailyfilm.presentation.playfilm import android.net.Uri import android.util.Log -import androidx.lifecycle.* +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import com.boostcamp.dailyfilm.data.delete.DeleteFilmRepository import com.boostcamp.dailyfilm.data.model.Result import com.boostcamp.dailyfilm.data.playfilm.PlayFilmRepository import com.boostcamp.dailyfilm.presentation.calendar.model.DateModel -import com.boostcamp.dailyfilm.presentation.util.network.NetworkManager -import com.boostcamp.dailyfilm.presentation.util.network.NetworkState import com.boostcamp.dailyfilm.presentation.util.PlayState import com.boostcamp.dailyfilm.presentation.util.UiState +import com.boostcamp.dailyfilm.presentation.util.network.NetworkManager +import com.boostcamp.dailyfilm.presentation.util.network.NetworkState import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import javax.inject.Inject @@ -31,14 +37,14 @@ class PlayFilmViewModel @Inject constructor( private val _videoUri = MutableLiveData() val videoUri: LiveData get() = _videoUri - private val _isContentShowed = MutableLiveData(true) - val isContentShowed: LiveData get() = _isContentShowed + private val _isContentShowed = MutableStateFlow(true) + val isContentShowed: StateFlow get() = _isContentShowed - private val _isMuted = MutableLiveData(false) - val isMuted: LiveData get() = _isMuted + private val _isMuted = MutableStateFlow(false) + val isMuted: StateFlow get() = _isMuted - private val _playState = MutableLiveData(PlayState.Uninitialized) - val playState: LiveData get() = _playState + private val _playState = MutableStateFlow(PlayState.Uninitialized) + val playState: StateFlow get() = _playState private val _networkState = MutableLiveData(NetworkManager.checkNetwork()) val networkState: LiveData get() = _networkState @@ -46,12 +52,21 @@ class PlayFilmViewModel @Inject constructor( private val _isNetworkConnectShowed = MutableLiveData(true) val isNetworkConnectShowed: LiveData get() = _isNetworkConnectShowed - private val _isProgressed = MutableLiveData(false) - val isProgressed: LiveData get() = _isProgressed + private val _isProgressed = MutableStateFlow(false) + val isProgressed: StateFlow get() = _isProgressed + + private val _openDialog = MutableStateFlow(false) + val openDialog : StateFlow get() = _openDialog init { loadVideo() } + fun openDialog() { + _openDialog.value = true + } + fun closeDialog() { + _openDialog.value = false + } private fun checkNetwork() { _networkState.value = NetworkManager.checkNetwork() @@ -63,11 +78,11 @@ class PlayFilmViewModel @Inject constructor( } fun changeShowState() { - _isContentShowed.value = _isContentShowed.value?.not() + _isContentShowed.value = isContentShowed.value.not() } fun changeMuteState() { - _isMuted.value = _isMuted.value?.not() + _isMuted.value = isMuted.value.not() } fun setNetworkState(state: NetworkState) { From 0c233b7f999254d9fdae0d906c637bc79d6c4ef0 Mon Sep 17 00:00:00 2001 From: junhyeongleeee Date: Thu, 15 Jun 2023 19:55:58 +0900 Subject: [PATCH 08/13] =?UTF-8?q?issue=20#160=20feat:=20playfilmComposeFra?= =?UTF-8?q?gment=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: junhyeongleeee --- .../playfilm/adapter/PlayFilmPageAdapter.kt | 5 +- .../compose/PlayFilmComposeFragment.kt | 120 ++++++ .../compose/PlayFilmFragmentCompose.kt | 367 ++++++++++++++++++ .../res/layout/fragment_play_film_compose.xml | 44 +++ 4 files changed, 534 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmComposeFragment.kt create mode 100644 app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmFragmentCompose.kt create mode 100644 app/src/main/res/layout/fragment_play_film_compose.xml diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/adapter/PlayFilmPageAdapter.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/adapter/PlayFilmPageAdapter.kt index 365e71f7..54365b36 100644 --- a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/adapter/PlayFilmPageAdapter.kt +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/adapter/PlayFilmPageAdapter.kt @@ -4,6 +4,7 @@ import androidx.fragment.app.FragmentActivity import androidx.viewpager2.adapter.FragmentStateAdapter import com.boostcamp.dailyfilm.presentation.calendar.model.DateModel import com.boostcamp.dailyfilm.presentation.playfilm.PlayFilmFragment +import com.boostcamp.dailyfilm.presentation.playfilm.compose.PlayFilmComposeFragment class PlayFilmPageAdapter( private val dateList: ArrayList, @@ -13,7 +14,7 @@ class PlayFilmPageAdapter( override fun getItemCount(): Int = dateList.size - override fun createFragment(position: Int): PlayFilmFragment { - return PlayFilmFragment.newInstance(dateList[position]) + override fun createFragment(position: Int): PlayFilmComposeFragment { + return PlayFilmComposeFragment.newInstance(dateList[position]) } } \ No newline at end of file diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmComposeFragment.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmComposeFragment.kt new file mode 100644 index 00000000..691cea54 --- /dev/null +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmComposeFragment.kt @@ -0,0 +1,120 @@ +package com.boostcamp.dailyfilm.presentation.playfilm.compose + +import android.annotation.SuppressLint +import android.app.Activity.RESULT_OK +import android.content.Intent +import android.net.ConnectivityManager +import android.net.Network +import android.os.Bundle +import androidx.activity.result.ActivityResult +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts +import androidx.fragment.app.activityViewModels +import androidx.fragment.app.viewModels +import com.boostcamp.dailyfilm.R +import com.boostcamp.dailyfilm.databinding.FragmentPlayFilmComposeBinding +import com.boostcamp.dailyfilm.presentation.BaseFragment +import com.boostcamp.dailyfilm.presentation.calendar.model.DateModel +import com.boostcamp.dailyfilm.presentation.playfilm.PlayFilmActivityViewModel +import com.boostcamp.dailyfilm.presentation.playfilm.PlayFilmBottomSheetDialog +import com.boostcamp.dailyfilm.presentation.playfilm.PlayFilmViewModel +import com.boostcamp.dailyfilm.presentation.util.network.NetworkManager +import com.boostcamp.dailyfilm.presentation.util.network.NetworkState +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class PlayFilmComposeFragment : + BaseFragment(R.layout.fragment_play_film_compose) { + + private val viewModel: PlayFilmViewModel by viewModels() + private val activityViewModel: PlayFilmActivityViewModel by activityViewModels() + private lateinit var playFilmBottomSheetDialog: PlayFilmBottomSheetDialog + + private val startForResult: ActivityResultLauncher = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult -> + if (result.resultCode == RESULT_OK && result.data != null) { + val text = result.data?.getStringExtra(KET_EDIT_TEXT) ?: "" + viewModel.setDateModel(text) + } + } + + private val networkCallback = object : ConnectivityManager.NetworkCallback() { + override fun onAvailable(network: Network) { + super.onAvailable(network) + viewModel.setNetworkState(NetworkState.AVAILABLE) + } + + override fun onLost(network: Network) { + super.onLost(network) + viewModel.setNetworkState(NetworkState.LOST) + } + } + + @SuppressLint("ShowToast") + override fun initView() { + binding.playFilmCompose.setContent { +// DailyFilmTheme { + PlayFilmUI( + requireActivity(), + startForResult, + activityViewModel = activityViewModel, + viewModel = viewModel + ) +// } + } + initBinding() + initDialog() + } + + private fun initBinding() { + binding.viewModel = viewModel + } + + private fun initDialog() { + playFilmBottomSheetDialog = + PlayFilmBottomSheetDialog(viewModel, activityViewModel, startForResult) + } + + override fun onStart() { + super.onStart() + NetworkManager.registerNetworkCallback(networkCallback) + } + + override fun onResume() { + super.onResume() + binding.backgroundPlayer.player?.play() + } + + override fun onPause() { + binding.backgroundPlayer.player?.let { player -> + if (player.isPlaying) { + player.seekTo(0L) + player.pause() + } + } + super.onPause() + } + + override fun onStop() { + super.onStop() + NetworkManager.terminateNetworkCallback(networkCallback) + } + + override fun onDestroyView() { + binding.backgroundPlayer.player?.release() + binding.backgroundPlayer.player = null + super.onDestroyView() + } + + companion object { + const val KEY_DATE_MODEL = "dateModel" + const val BOTTOM_SHEET_TAG = "bottomSheet" + const val KET_EDIT_TEXT = "editText" + fun newInstance(dateModel: DateModel) = + PlayFilmComposeFragment().apply { + arguments = Bundle().apply { + putParcelable(KEY_DATE_MODEL, dateModel) + } + } + } +} diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmFragmentCompose.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmFragmentCompose.kt new file mode 100644 index 00000000..f805da73 --- /dev/null +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmFragmentCompose.kt @@ -0,0 +1,367 @@ +package com.boostcamp.dailyfilm.presentation.playfilm.compose + +import android.app.Activity +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.CircularProgressIndicator +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.MaterialTheme +import androidx.compose.material.ModalBottomSheetLayout +import androidx.compose.material.ModalBottomSheetValue +import androidx.compose.material.Text +import androidx.compose.material.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.airbnb.lottie.LottieComposition +import com.airbnb.lottie.compose.LottieAnimatable +import com.airbnb.lottie.compose.LottieAnimation +import com.airbnb.lottie.compose.LottieClipSpec +import com.airbnb.lottie.compose.LottieCompositionSpec +import com.airbnb.lottie.compose.rememberLottieAnimatable +import com.airbnb.lottie.compose.rememberLottieComposition +import com.boostcamp.dailyfilm.R +import com.boostcamp.dailyfilm.presentation.calendar.CalendarActivity +import com.boostcamp.dailyfilm.presentation.calendar.DateFragment +import com.boostcamp.dailyfilm.presentation.playfilm.PlayFilmActivityViewModel +import com.boostcamp.dailyfilm.presentation.playfilm.PlayFilmFragment +import com.boostcamp.dailyfilm.presentation.playfilm.PlayFilmViewModel +import com.boostcamp.dailyfilm.presentation.playfilm.model.BottomSheetModel +import com.boostcamp.dailyfilm.presentation.playfilm.model.EditState +import com.boostcamp.dailyfilm.presentation.selectvideo.SelectVideoActivity +import com.boostcamp.dailyfilm.presentation.ui.theme.blackBlur +import com.boostcamp.dailyfilm.presentation.uploadfilm.UploadFilmActivity +import com.boostcamp.dailyfilm.presentation.uploadfilm.model.DateAndVideoModel +import com.boostcamp.dailyfilm.presentation.util.PlayState +import com.boostcamp.dailyfilm.presentation.util.dialog.CustomDialog +import com.google.android.material.snackbar.Snackbar +import kotlinx.coroutines.launch + +val playFilmBottomSheetModelList = listOf( + BottomSheetModel(R.drawable.ic_delete, R.string.delete), + BottomSheetModel(R.drawable.ic_re_upload, R.string.re_upload), + BottomSheetModel(R.drawable.ic_edit_text, R.string.edit_text) +) + +@Composable +fun PlayFilmUI( + activity: Activity, + startForResult: ActivityResultLauncher, + activityViewModel: PlayFilmActivityViewModel, + viewModel: PlayFilmViewModel +) { + val state = viewModel.playState.collectAsStateWithLifecycle().value + + DialogUI(viewModel = viewModel) + + PlayView(state, viewModel) { title -> + onDialogClick( + activity, + title, + startForResult, + activityViewModel, + viewModel + ) + } + + when (state) { + is PlayState.Uninitialized -> {} + is PlayState.Loading -> {} + is PlayState.Playing -> {} + is PlayState.Deleted -> setResultCalendar(state, activity, activityViewModel) + is PlayState.Failure -> FailurePlay(activity, state) + } +} + +@OptIn(ExperimentalMaterialApi::class) +@Composable +fun PlayView( + state: PlayState, + viewModel: PlayFilmViewModel, + menuClick: (Int) -> Unit, +) { + val bottomState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden) + val scope = rememberCoroutineScope() + + val isMuted = viewModel.isMuted.collectAsStateWithLifecycle().value + val isContentShowed = viewModel.isContentShowed.collectAsStateWithLifecycle().value + val dateModel = viewModel.dateModel + + val soundComposition by rememberLottieComposition( + LottieCompositionSpec.Asset(stringResource(R.string.lottie_sound)) + ) + val textComposition by rememberLottieComposition( + LottieCompositionSpec.RawRes(resId = R.raw.lottie_textstate) + ) + val soundAnimatable = rememberLottieAnimatable() + val textAnimatable = rememberLottieAnimatable() + + // LottieAnimation + LaunchedEffect(isMuted) { + soundAnimatable.animate( + composition = soundComposition, + clipSpec = if (isMuted) { + LottieClipSpec.Progress(0.0f, 0.5f) + } else { + LottieClipSpec.Progress(0.5f, 1.0f) + }, + ) + } + + LaunchedEffect(isContentShowed) { + textAnimatable.animate( + composition = textComposition, clipSpec = if (isContentShowed) { + LottieClipSpec.Progress(0.67f, 0.25f) + } else { + LottieClipSpec.Progress(0.25f, 0.67f) + } + ) + } + + // BottomSheetDialog + ModalBottomSheetLayout( + sheetState = bottomState, + sheetContent = { + playFilmBottomSheetModelList.forEach { model -> + Box( + modifier = Modifier + .fillMaxWidth() + .background(color = colorResource(id = R.color.Background)) + ) { + BottomSheetView(model, menuClick) + } + } + }) { + + Box( + modifier = Modifier + .fillMaxSize() + .background(Color.Transparent) + .padding(dimensionResource(id = R.dimen.normal_100)) + ) { + + Box( + modifier = Modifier + .background(blackBlur, RoundedCornerShape(4.dp)) + .align(Alignment.TopStart) + .padding(start = 6.dp, end = 6.dp, top = 4.dp, bottom = 4.dp) + .height(dimensionResource(id = R.dimen.normal_175)) + ) { + Text( + modifier = Modifier.align(Alignment.Center), + text = stringResource( + R.string.date, dateModel.year, dateModel.month, dateModel.day + ), + color = Color.White, + fontSize = 16.sp + ) + } + + Row( + modifier = Modifier.align(Alignment.TopEnd), + horizontalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.normal_100)) + ) { + + Image( + painter = painterResource(id = R.drawable.ic_menu), + contentDescription = "Menu", + modifier = Modifier + .background(blackBlur, RoundedCornerShape(4.dp)) + .padding(4.dp) + .size(dimensionResource(id = R.dimen.normal_175)) + .clickable { + scope.launch { + bottomState.show() + } + }) + + LottieAnimation( + composition = soundComposition, + progress = soundAnimatable.progress, + modifier = Modifier + .background(blackBlur, RoundedCornerShape(4.dp)) + .padding(4.dp) + .size(dimensionResource(id = R.dimen.normal_175)) + .clickable { viewModel.changeMuteState() }) + } + + AnimatedVisibility( + visible = isContentShowed, + modifier = Modifier.align(Alignment.Center), + enter = fadeIn(), + exit = fadeOut() + ) { + Text( + text = dateModel.text ?: "테스트", + modifier = Modifier + .background( + Color.Black.copy(alpha = 0.5f), RoundedCornerShape(4.dp) + ) + .padding(4.dp), + color = Color.White, + fontSize = 16.sp + ) + } + + if (state != PlayState.Playing) { + CircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) + } + + LottieAnimation( + composition = textComposition, + progress = textAnimatable.progress, + modifier = Modifier + .align(Alignment.BottomCenter) + .background(blackBlur, RoundedCornerShape(50.dp)) + .size(dimensionResource(id = R.dimen.large_200)) + .padding(4.dp) + .clickable { viewModel.changeShowState() }) + } + } + +} + +fun onDialogClick( + activity: Activity, + resId: Int, + startForResult: ActivityResultLauncher, + activityViewModel: PlayFilmActivityViewModel, + viewModel: PlayFilmViewModel +) { + + when (resId) { + R.string.delete -> { + viewModel.openDialog() + } + + R.string.re_upload -> { + activity.startActivity( + Intent( + activity.applicationContext, SelectVideoActivity::class.java + ).apply { + putExtra(DateFragment.KEY_CALENDAR_INDEX, activityViewModel.calendarIndex) + putExtra(PlayFilmFragment.KEY_DATE_MODEL, viewModel.dateModel) + putExtra(CalendarActivity.KEY_EDIT_STATE, EditState.RE_UPLOAD) + putExtra( + SelectVideoActivity.DATE_VIDEO_ITEM, + DateAndVideoModel( + viewModel.videoUri.value ?: return, + viewModel.dateModel.getDate() + ) + ) + } + ) + activity.finish() + } + + R.string.edit_text -> { + startForResult.launch( + Intent(activity.applicationContext, UploadFilmActivity::class.java).apply { + putExtra(DateFragment.KEY_CALENDAR_INDEX, activityViewModel.calendarIndex) + putExtra( + SelectVideoActivity.DATE_VIDEO_ITEM, + DateAndVideoModel( + viewModel.videoUri.value ?: return, + viewModel.dateModel.getDate() + ) + ) + putExtra(CalendarActivity.KEY_EDIT_STATE, EditState.EDIT_CONTENT) + putExtra(PlayFilmFragment.KEY_DATE_MODEL, viewModel.dateModel) + } + ) + } + } +} + +@Preview(showBackground = true, widthDp = 320, heightDp = 80) +@Composable +fun BottomSheetPreView() { + BottomSheetView(playFilmBottomSheetModelList[0], {}) +} + +@Composable +fun BottomSheetView(model: BottomSheetModel, onClick: (Int) -> Unit) { + + Row( + modifier = Modifier + .padding(dimensionResource(id = R.dimen.normal_100)) + .clickable { onClick(model.title) }, + horizontalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.normal_125)), + verticalAlignment = Alignment.CenterVertically + ) { + Image( + modifier = Modifier.size(dimensionResource(id = R.dimen.large_125)), + painter = painterResource(id = model.icon), + contentDescription = stringResource(id = model.title), + colorFilter = ColorFilter.tint(colorResource(id = R.color.OnBackground)) + ) + Text( + text = stringResource(id = model.title), + color = colorResource(id = R.color.OnBackground), + fontSize = 20.sp + ) + } +} + +@Composable +fun FailurePlay(activity: Activity, state: PlayState.Failure) { + state.throwable.message?.let { + Snackbar.make( + activity.findViewById(android.R.id.content), it, Snackbar.LENGTH_SHORT + ) + } +} + +@Composable +fun DialogUI(viewModel: PlayFilmViewModel) { + val openDialog = viewModel.openDialog.collectAsStateWithLifecycle().value + + // CustomDialog + if (openDialog) { + CustomDialog( + stringResource(id = R.string.delete_dialog), + { viewModel.closeDialog() }, + { viewModel.deleteVideo() } + ) + } +} + +fun setResultCalendar( + state: PlayState.Deleted, activity: Activity, activityViewModel: PlayFilmActivityViewModel +) { + activity.setResult(Activity.RESULT_OK, Intent( + activity, CalendarActivity::class.java + ).apply { + putExtra(DateFragment.KEY_CALENDAR_INDEX, activityViewModel.calendarIndex) + putExtra(PlayFilmComposeFragment.KEY_DATE_MODEL, state.dateModel) + }) + activity.finish() +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_play_film_compose.xml b/app/src/main/res/layout/fragment_play_film_compose.xml new file mode 100644 index 00000000..c00e757c --- /dev/null +++ b/app/src/main/res/layout/fragment_play_film_compose.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + \ No newline at end of file From 37a4d29a86756ba6eeeb20b9421d8464f5600d67 Mon Sep 17 00:00:00 2001 From: junhyeongleeee Date: Wed, 5 Jul 2023 14:35:18 +0900 Subject: [PATCH 09/13] =?UTF-8?q?issue=20#160=20refactor:=20mute,=20conten?= =?UTF-8?q?tShow=20=EC=83=81=ED=83=9C=20=ED=99=80=EB=8D=94=20=EB=A7=8C?= =?UTF-8?q?=EB=93=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: junhyeongleeee --- .../playfilm/PlayFilmViewModel.kt | 18 ++++------- .../playfilm/base/ContentShowState.kt | 18 +++++++++++ .../presentation/playfilm/base/LottieState.kt | 16 ++++++++++ .../presentation/playfilm/base/MuteState.kt | 19 ++++++++++++ .../compose/PlayFilmFragmentCompose.kt | 30 ++++++------------- .../main/res/layout/fragment_play_film.xml | 12 ++++---- .../res/layout/fragment_play_film_compose.xml | 2 +- 7 files changed, 75 insertions(+), 40 deletions(-) create mode 100644 app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/base/ContentShowState.kt create mode 100644 app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/base/LottieState.kt create mode 100644 app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/base/MuteState.kt diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/PlayFilmViewModel.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/PlayFilmViewModel.kt index 288de989..e9ef6e4b 100644 --- a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/PlayFilmViewModel.kt +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/PlayFilmViewModel.kt @@ -11,6 +11,8 @@ import com.boostcamp.dailyfilm.data.delete.DeleteFilmRepository import com.boostcamp.dailyfilm.data.model.Result import com.boostcamp.dailyfilm.data.playfilm.PlayFilmRepository import com.boostcamp.dailyfilm.presentation.calendar.model.DateModel +import com.boostcamp.dailyfilm.presentation.playfilm.base.ContentShowState +import com.boostcamp.dailyfilm.presentation.playfilm.base.MuteState import com.boostcamp.dailyfilm.presentation.util.PlayState import com.boostcamp.dailyfilm.presentation.util.UiState import com.boostcamp.dailyfilm.presentation.util.network.NetworkManager @@ -37,11 +39,11 @@ class PlayFilmViewModel @Inject constructor( private val _videoUri = MutableLiveData() val videoUri: LiveData get() = _videoUri - private val _isContentShowed = MutableStateFlow(true) - val isContentShowed: StateFlow get() = _isContentShowed + private val _contentShowState = MutableStateFlow(ContentShowState(true)) + val contentShowState: StateFlow get() = _contentShowState - private val _isMuted = MutableStateFlow(false) - val isMuted: StateFlow get() = _isMuted + private val _muteState = MutableStateFlow(MuteState(false)) + val muteState: StateFlow get() = _muteState private val _playState = MutableStateFlow(PlayState.Uninitialized) val playState: StateFlow get() = _playState @@ -77,14 +79,6 @@ class PlayFilmViewModel @Inject constructor( _text.value = text } - fun changeShowState() { - _isContentShowed.value = isContentShowed.value.not() - } - - fun changeMuteState() { - _isMuted.value = isMuted.value.not() - } - fun setNetworkState(state: NetworkState) { viewModelScope.launch { // isNetworkConnected 는 연결 여부를 떠나 Playing 중이면 보여 주지 않는다. diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/base/ContentShowState.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/base/ContentShowState.kt new file mode 100644 index 00000000..7ca3f360 --- /dev/null +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/base/ContentShowState.kt @@ -0,0 +1,18 @@ +package com.boostcamp.dailyfilm.presentation.playfilm.base + +import com.airbnb.lottie.compose.LottieClipSpec + +class ContentShowState(init: Boolean): LottieState(init) { + override val clipSpec: LottieClipSpec + get() = if (state) { + LottieClipSpec.Progress(START, MID) + } else { + LottieClipSpec.Progress(MID, FINISH) + } + + companion object { + const val START = 0.67f + const val MID = 0.25f + const val FINISH = 0.67f + } +} \ No newline at end of file diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/base/LottieState.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/base/LottieState.kt new file mode 100644 index 00000000..08202d5a --- /dev/null +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/base/LottieState.kt @@ -0,0 +1,16 @@ +package com.boostcamp.dailyfilm.presentation.playfilm.base + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import com.airbnb.lottie.compose.LottieClipSpec + +abstract class LottieState(initial: Boolean) { + var state by mutableStateOf(initial) + + abstract val clipSpec: LottieClipSpec + + fun updateState() { + state = !state + } +} \ No newline at end of file diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/base/MuteState.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/base/MuteState.kt new file mode 100644 index 00000000..5e4bf857 --- /dev/null +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/base/MuteState.kt @@ -0,0 +1,19 @@ +package com.boostcamp.dailyfilm.presentation.playfilm.base + +import com.airbnb.lottie.compose.LottieClipSpec + +class MuteState(init: Boolean): LottieState(init) { + + override val clipSpec: LottieClipSpec + get() = if (state) { + LottieClipSpec.Progress(START, MID) + } else { + LottieClipSpec.Progress(MID, FINISH) + } + + companion object { + const val START = 0.0f + const val MID = 0.5f + const val FINISH = 1.0f + } +} \ No newline at end of file diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmFragmentCompose.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmFragmentCompose.kt index f805da73..62c0fe5a 100644 --- a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmFragmentCompose.kt +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmFragmentCompose.kt @@ -20,7 +20,6 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.MaterialTheme import androidx.compose.material.ModalBottomSheetLayout import androidx.compose.material.ModalBottomSheetValue import androidx.compose.material.Text @@ -41,10 +40,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.airbnb.lottie.LottieComposition -import com.airbnb.lottie.compose.LottieAnimatable import com.airbnb.lottie.compose.LottieAnimation -import com.airbnb.lottie.compose.LottieClipSpec import com.airbnb.lottie.compose.LottieCompositionSpec import com.airbnb.lottie.compose.rememberLottieAnimatable import com.airbnb.lottie.compose.rememberLottieComposition @@ -111,8 +107,8 @@ fun PlayView( val bottomState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden) val scope = rememberCoroutineScope() - val isMuted = viewModel.isMuted.collectAsStateWithLifecycle().value - val isContentShowed = viewModel.isContentShowed.collectAsStateWithLifecycle().value + val muteState = viewModel.muteState.collectAsStateWithLifecycle().value + val contentShowState = viewModel.contentShowState.collectAsStateWithLifecycle().value val dateModel = viewModel.dateModel val soundComposition by rememberLottieComposition( @@ -125,24 +121,16 @@ fun PlayView( val textAnimatable = rememberLottieAnimatable() // LottieAnimation - LaunchedEffect(isMuted) { + LaunchedEffect(muteState.state) { soundAnimatable.animate( composition = soundComposition, - clipSpec = if (isMuted) { - LottieClipSpec.Progress(0.0f, 0.5f) - } else { - LottieClipSpec.Progress(0.5f, 1.0f) - }, + clipSpec = muteState.clipSpec, ) } - LaunchedEffect(isContentShowed) { + LaunchedEffect(contentShowState.state) { textAnimatable.animate( - composition = textComposition, clipSpec = if (isContentShowed) { - LottieClipSpec.Progress(0.67f, 0.25f) - } else { - LottieClipSpec.Progress(0.25f, 0.67f) - } + composition = textComposition, clipSpec = contentShowState.clipSpec ) } @@ -210,11 +198,11 @@ fun PlayView( .background(blackBlur, RoundedCornerShape(4.dp)) .padding(4.dp) .size(dimensionResource(id = R.dimen.normal_175)) - .clickable { viewModel.changeMuteState() }) + .clickable { muteState.updateState() }) } AnimatedVisibility( - visible = isContentShowed, + visible = contentShowState.state, modifier = Modifier.align(Alignment.Center), enter = fadeIn(), exit = fadeOut() @@ -243,7 +231,7 @@ fun PlayView( .background(blackBlur, RoundedCornerShape(50.dp)) .size(dimensionResource(id = R.dimen.large_200)) .padding(4.dp) - .clickable { viewModel.changeShowState() }) + .clickable { contentShowState.updateState() }) } } diff --git a/app/src/main/res/layout/fragment_play_film.xml b/app/src/main/res/layout/fragment_play_film.xml index cbdeee9a..0d2adf81 100644 --- a/app/src/main/res/layout/fragment_play_film.xml +++ b/app/src/main/res/layout/fragment_play_film.xml @@ -39,7 +39,7 @@ android:id="@+id/backgroundPlayer" android:layout_width="0dp" android:layout_height="0dp" - app:changeVolume="@{viewModel.isMuted()}" + app:changeVolume="@{viewModel.muteState.state}" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" @@ -78,7 +78,7 @@ android:layout_marginEnd="@dimen/normal_100" android:alpha="0.5" android:background="@drawable/background_rounded" - android:onClick="@{() -> viewModel.changeMuteState()}" + android:onClick="@{() -> viewModel.muteState.updateState()}" android:padding="@dimen/small_100" android:scaleType="fitStart" app:layout_constraintBottom_toBottomOf="@+id/tv_date" @@ -87,7 +87,7 @@ app:lottie_autoPlay="false" app:lottie_fileName="sound_lottie.json" app:lottie_loop="false" - app:syncMuteIcon="@{viewModel.isMuted()}" /> + app:syncMuteIcon="@{viewModel.muteState.state}" /> + app:visibilityAnimation="@{viewModel.contentShowState.state}" /> + app:syncViewState="@{viewModel.contentShowState.state}" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_play_film_compose.xml b/app/src/main/res/layout/fragment_play_film_compose.xml index c00e757c..b8ed3e57 100644 --- a/app/src/main/res/layout/fragment_play_film_compose.xml +++ b/app/src/main/res/layout/fragment_play_film_compose.xml @@ -18,7 +18,7 @@ android:id="@+id/backgroundPlayer" android:layout_width="0dp" android:layout_height="0dp" - app:changeVolume="@{viewModel.isMuted()}" + app:changeVolume="@{viewModel.muteState.state}" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" From e97dcdeb4cfd0a4eb3631ad0b8a0a4b3a9ca6ac9 Mon Sep 17 00:00:00 2001 From: junhyeongleeee Date: Wed, 5 Jul 2023 15:53:30 +0900 Subject: [PATCH 10/13] =?UTF-8?q?issue=20#160=20feat:=20composeView=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EC=A0=84=EB=9E=B5=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20DailyFilmTheme=20=EC=97=90=EB=9F=AC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: junhyeongleeee --- .../compose/PlayFilmComposeFragment.kt | 23 +++++++++++-------- .../dailyfilm/presentation/ui/theme/Theme.kt | 3 ++- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmComposeFragment.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmComposeFragment.kt index 691cea54..08b6b507 100644 --- a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmComposeFragment.kt +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmComposeFragment.kt @@ -9,6 +9,7 @@ import android.os.Bundle import androidx.activity.result.ActivityResult import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import com.boostcamp.dailyfilm.R @@ -18,6 +19,7 @@ import com.boostcamp.dailyfilm.presentation.calendar.model.DateModel import com.boostcamp.dailyfilm.presentation.playfilm.PlayFilmActivityViewModel import com.boostcamp.dailyfilm.presentation.playfilm.PlayFilmBottomSheetDialog import com.boostcamp.dailyfilm.presentation.playfilm.PlayFilmViewModel +import com.boostcamp.dailyfilm.presentation.ui.theme.DailyFilmTheme import com.boostcamp.dailyfilm.presentation.util.network.NetworkManager import com.boostcamp.dailyfilm.presentation.util.network.NetworkState import dagger.hilt.android.AndroidEntryPoint @@ -52,15 +54,18 @@ class PlayFilmComposeFragment : @SuppressLint("ShowToast") override fun initView() { - binding.playFilmCompose.setContent { -// DailyFilmTheme { - PlayFilmUI( - requireActivity(), - startForResult, - activityViewModel = activityViewModel, - viewModel = viewModel - ) -// } + binding.playFilmCompose.apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + DailyFilmTheme(requireActivity()) { + PlayFilmUI( + requireActivity(), + startForResult, + activityViewModel = activityViewModel, + viewModel = viewModel + ) + } + } } initBinding() initDialog() diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/ui/theme/Theme.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/ui/theme/Theme.kt index 320793b6..2369d07f 100644 --- a/app/src/main/java/com/boostcamp/dailyfilm/presentation/ui/theme/Theme.kt +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/ui/theme/Theme.kt @@ -37,6 +37,7 @@ private val DarkColorPalette = darkColors( @Composable fun DailyFilmTheme( + activity: Activity = (LocalView.current.context as Activity), darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit, ) { @@ -49,7 +50,7 @@ fun DailyFilmTheme( val view = LocalView.current if (!view.isInEditMode) { SideEffect { - val window = (view.context as Activity).window + val window = activity.window window.statusBarColor = colors.primaryVariant.toArgb() window.navigationBarColor = colors.primaryVariant.toArgb() WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme.not() From 61d27b5a5a67c0e16804824426dc2ac6a0d39e9a Mon Sep 17 00:00:00 2001 From: junhyeongleeee Date: Mon, 17 Jul 2023 19:49:09 +0900 Subject: [PATCH 11/13] =?UTF-8?q?issue=20#160=20refactor:=20=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=82=B4=EC=9A=A9=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: junhyeongleeee --- .../playfilm/PlayFilmViewModel.kt | 7 +- .../compose/PlayFilmComposeFragment.kt | 75 ++++- .../compose/PlayFilmFragmentCompose.kt | 283 ++++++++---------- 3 files changed, 201 insertions(+), 164 deletions(-) diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/PlayFilmViewModel.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/PlayFilmViewModel.kt index e9ef6e4b..d13f5ca6 100644 --- a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/PlayFilmViewModel.kt +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/PlayFilmViewModel.kt @@ -63,11 +63,8 @@ class PlayFilmViewModel @Inject constructor( init { loadVideo() } - fun openDialog() { - _openDialog.value = true - } - fun closeDialog() { - _openDialog.value = false + fun setDialog(value: Boolean) { + _openDialog.value = value } private fun checkNetwork() { diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmComposeFragment.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmComposeFragment.kt index 08b6b507..61405e27 100644 --- a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmComposeFragment.kt +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmComposeFragment.kt @@ -15,11 +15,19 @@ import androidx.fragment.app.viewModels import com.boostcamp.dailyfilm.R import com.boostcamp.dailyfilm.databinding.FragmentPlayFilmComposeBinding import com.boostcamp.dailyfilm.presentation.BaseFragment +import com.boostcamp.dailyfilm.presentation.calendar.CalendarActivity +import com.boostcamp.dailyfilm.presentation.calendar.CalendarActivity.Companion.KEY_EDIT_STATE +import com.boostcamp.dailyfilm.presentation.calendar.DateFragment.Companion.KEY_CALENDAR_INDEX import com.boostcamp.dailyfilm.presentation.calendar.model.DateModel import com.boostcamp.dailyfilm.presentation.playfilm.PlayFilmActivityViewModel import com.boostcamp.dailyfilm.presentation.playfilm.PlayFilmBottomSheetDialog import com.boostcamp.dailyfilm.presentation.playfilm.PlayFilmViewModel +import com.boostcamp.dailyfilm.presentation.playfilm.model.EditState +import com.boostcamp.dailyfilm.presentation.selectvideo.SelectVideoActivity +import com.boostcamp.dailyfilm.presentation.selectvideo.SelectVideoActivity.Companion.DATE_VIDEO_ITEM import com.boostcamp.dailyfilm.presentation.ui.theme.DailyFilmTheme +import com.boostcamp.dailyfilm.presentation.uploadfilm.UploadFilmActivity +import com.boostcamp.dailyfilm.presentation.uploadfilm.model.DateAndVideoModel import com.boostcamp.dailyfilm.presentation.util.network.NetworkManager import com.boostcamp.dailyfilm.presentation.util.network.NetworkState import dagger.hilt.android.AndroidEntryPoint @@ -60,9 +68,69 @@ class PlayFilmComposeFragment : DailyFilmTheme(requireActivity()) { PlayFilmUI( requireActivity(), - startForResult, - activityViewModel = activityViewModel, - viewModel = viewModel + viewModel = viewModel, + setResultCalendar = { state -> + activity?.let { + it.setResult( + RESULT_OK, Intent( + it, CalendarActivity::class.java + ).apply { + putExtra( + KEY_CALENDAR_INDEX, + activityViewModel.calendarIndex + ) + putExtra(KEY_DATE_MODEL, state.dateModel) + }) + it.finish() + } + }, + dialogEvent = { resId -> + when(resId) { + R.string.delete -> { + viewModel.setDialog(true) + } + R.string.re_upload -> { + activity?.let { + it.startActivity( + Intent( + it.applicationContext, SelectVideoActivity::class.java + ).apply { + putExtra( + KEY_CALENDAR_INDEX, + activityViewModel.calendarIndex + ) + putExtra(KEY_DATE_MODEL, viewModel.dateModel) + putExtra(KEY_EDIT_STATE, EditState.RE_UPLOAD) + putExtra( + DATE_VIDEO_ITEM, + DateAndVideoModel( + viewModel.videoUri.value ?: return@PlayFilmUI, + viewModel.dateModel.getDate() + ) + ) + } + ) + it.finish() + } + } + R.string.edit_text -> { + startForResult.launch( + Intent(activity?.applicationContext, UploadFilmActivity::class.java).apply { + putExtra(KEY_CALENDAR_INDEX, activityViewModel.calendarIndex) + putExtra( + DATE_VIDEO_ITEM, + DateAndVideoModel( + viewModel.videoUri.value ?: return@PlayFilmUI, + viewModel.dateModel.getDate() + ) + ) + putExtra(KEY_EDIT_STATE, EditState.EDIT_CONTENT) + putExtra(KEY_DATE_MODEL, viewModel.dateModel) + } + ) + } + } + } ) } } @@ -113,7 +181,6 @@ class PlayFilmComposeFragment : companion object { const val KEY_DATE_MODEL = "dateModel" - const val BOTTOM_SHEET_TAG = "bottomSheet" const val KET_EDIT_TEXT = "editText" fun newInstance(dateModel: DateModel) = PlayFilmComposeFragment().apply { diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmFragmentCompose.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmFragmentCompose.kt index 62c0fe5a..70de1d97 100644 --- a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmFragmentCompose.kt +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmFragmentCompose.kt @@ -1,8 +1,6 @@ package com.boostcamp.dailyfilm.presentation.playfilm.compose import android.app.Activity -import android.content.Intent -import androidx.activity.result.ActivityResultLauncher import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut @@ -11,6 +9,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -40,22 +39,18 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.airbnb.lottie.LottieComposition +import com.airbnb.lottie.compose.LottieAnimatable import com.airbnb.lottie.compose.LottieAnimation import com.airbnb.lottie.compose.LottieCompositionSpec import com.airbnb.lottie.compose.rememberLottieAnimatable import com.airbnb.lottie.compose.rememberLottieComposition import com.boostcamp.dailyfilm.R -import com.boostcamp.dailyfilm.presentation.calendar.CalendarActivity -import com.boostcamp.dailyfilm.presentation.calendar.DateFragment -import com.boostcamp.dailyfilm.presentation.playfilm.PlayFilmActivityViewModel -import com.boostcamp.dailyfilm.presentation.playfilm.PlayFilmFragment +import com.boostcamp.dailyfilm.presentation.calendar.model.DateModel import com.boostcamp.dailyfilm.presentation.playfilm.PlayFilmViewModel +import com.boostcamp.dailyfilm.presentation.playfilm.base.ContentShowState import com.boostcamp.dailyfilm.presentation.playfilm.model.BottomSheetModel -import com.boostcamp.dailyfilm.presentation.playfilm.model.EditState -import com.boostcamp.dailyfilm.presentation.selectvideo.SelectVideoActivity import com.boostcamp.dailyfilm.presentation.ui.theme.blackBlur -import com.boostcamp.dailyfilm.presentation.uploadfilm.UploadFilmActivity -import com.boostcamp.dailyfilm.presentation.uploadfilm.model.DateAndVideoModel import com.boostcamp.dailyfilm.presentation.util.PlayState import com.boostcamp.dailyfilm.presentation.util.dialog.CustomDialog import com.google.android.material.snackbar.Snackbar @@ -70,45 +65,36 @@ val playFilmBottomSheetModelList = listOf( @Composable fun PlayFilmUI( activity: Activity, - startForResult: ActivityResultLauncher, - activityViewModel: PlayFilmActivityViewModel, - viewModel: PlayFilmViewModel + viewModel: PlayFilmViewModel, + setResultCalendar: (PlayState.Deleted) -> Unit, + dialogEvent: (Int) -> Unit ) { val state = viewModel.playState.collectAsStateWithLifecycle().value DialogUI(viewModel = viewModel) - - PlayView(state, viewModel) { title -> - onDialogClick( - activity, - title, - startForResult, - activityViewModel, - viewModel - ) - } + PlayScreen(state, viewModel, dialogEvent) when (state) { is PlayState.Uninitialized -> {} is PlayState.Loading -> {} is PlayState.Playing -> {} - is PlayState.Deleted -> setResultCalendar(state, activity, activityViewModel) + is PlayState.Deleted -> setResultCalendar(state) is PlayState.Failure -> FailurePlay(activity, state) } } @OptIn(ExperimentalMaterialApi::class) @Composable -fun PlayView( +private fun PlayScreen( state: PlayState, viewModel: PlayFilmViewModel, - menuClick: (Int) -> Unit, + dialogEvent: (Int) -> Unit, ) { val bottomState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden) val scope = rememberCoroutineScope() - val muteState = viewModel.muteState.collectAsStateWithLifecycle().value - val contentShowState = viewModel.contentShowState.collectAsStateWithLifecycle().value + val muteState by viewModel.muteState.collectAsStateWithLifecycle() + val contentShowState by viewModel.contentShowState.collectAsStateWithLifecycle() val dateModel = viewModel.dateModel val soundComposition by rememberLottieComposition( @@ -144,7 +130,7 @@ fun PlayView( .fillMaxWidth() .background(color = colorResource(id = R.color.Background)) ) { - BottomSheetView(model, menuClick) + BottomSheetView(model, dialogEvent) } } }) { @@ -155,137 +141,136 @@ fun PlayView( .background(Color.Transparent) .padding(dimensionResource(id = R.dimen.normal_100)) ) { - - Box( - modifier = Modifier - .background(blackBlur, RoundedCornerShape(4.dp)) - .align(Alignment.TopStart) - .padding(start = 6.dp, end = 6.dp, top = 4.dp, bottom = 4.dp) - .height(dimensionResource(id = R.dimen.normal_175)) - ) { - Text( - modifier = Modifier.align(Alignment.Center), - text = stringResource( - R.string.date, dateModel.year, dateModel.month, dateModel.day - ), - color = Color.White, - fontSize = 16.sp - ) - } + DateText(dateModel) Row( modifier = Modifier.align(Alignment.TopEnd), horizontalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.normal_100)) ) { - Image( - painter = painterResource(id = R.drawable.ic_menu), - contentDescription = "Menu", - modifier = Modifier - .background(blackBlur, RoundedCornerShape(4.dp)) - .padding(4.dp) - .size(dimensionResource(id = R.dimen.normal_175)) - .clickable { - scope.launch { - bottomState.show() - } - }) - - LottieAnimation( - composition = soundComposition, - progress = soundAnimatable.progress, - modifier = Modifier - .background(blackBlur, RoundedCornerShape(4.dp)) - .padding(4.dp) - .size(dimensionResource(id = R.dimen.normal_175)) - .clickable { muteState.updateState() }) - } + MenuImage(onClick = { + scope.launch { + bottomState.show() + } + }) - AnimatedVisibility( - visible = contentShowState.state, - modifier = Modifier.align(Alignment.Center), - enter = fadeIn(), - exit = fadeOut() - ) { - Text( - text = dateModel.text ?: "테스트", - modifier = Modifier - .background( - Color.Black.copy(alpha = 0.5f), RoundedCornerShape(4.dp) - ) - .padding(4.dp), - color = Color.White, - fontSize = 16.sp + SoundAnimation( + soundComposition = soundComposition, + soundAnimatable = soundAnimatable, + onClick = { muteState.updateState() } ) } + ContentText( + contentShowState = contentShowState, + dateModel = dateModel + ) + if (state != PlayState.Playing) { CircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) } - LottieAnimation( - composition = textComposition, - progress = textAnimatable.progress, - modifier = Modifier - .align(Alignment.BottomCenter) - .background(blackBlur, RoundedCornerShape(50.dp)) - .size(dimensionResource(id = R.dimen.large_200)) - .padding(4.dp) - .clickable { contentShowState.updateState() }) + TextLottieAnimation( + textComposition = textComposition, + textAnimatable = textAnimatable, + onClick = { contentShowState.updateState() } + ) } } } -fun onDialogClick( - activity: Activity, - resId: Int, - startForResult: ActivityResultLauncher, - activityViewModel: PlayFilmActivityViewModel, - viewModel: PlayFilmViewModel +@Composable +private fun SoundAnimation( + soundComposition: LottieComposition?, + soundAnimatable: LottieAnimatable, + onClick: () -> Unit ) { + LottieAnimation( + composition = soundComposition, + progress = soundAnimatable.progress, + modifier = Modifier + .background(blackBlur, RoundedCornerShape(4.dp)) + .padding(4.dp) + .size(dimensionResource(id = R.dimen.normal_175)) + .clickable(onClick = onClick) + ) +} - when (resId) { - R.string.delete -> { - viewModel.openDialog() - } +@Composable +private fun MenuImage( + onClick: () -> Unit +) { + Image( + painter = painterResource(id = R.drawable.ic_menu), + contentDescription = "Menu", + modifier = Modifier + .background(blackBlur, RoundedCornerShape(4.dp)) + .padding(4.dp) + .size(dimensionResource(id = R.dimen.normal_175)) + .clickable(onClick = onClick) + ) +} - R.string.re_upload -> { - activity.startActivity( - Intent( - activity.applicationContext, SelectVideoActivity::class.java - ).apply { - putExtra(DateFragment.KEY_CALENDAR_INDEX, activityViewModel.calendarIndex) - putExtra(PlayFilmFragment.KEY_DATE_MODEL, viewModel.dateModel) - putExtra(CalendarActivity.KEY_EDIT_STATE, EditState.RE_UPLOAD) - putExtra( - SelectVideoActivity.DATE_VIDEO_ITEM, - DateAndVideoModel( - viewModel.videoUri.value ?: return, - viewModel.dateModel.getDate() - ) - ) - } - ) - activity.finish() - } +@Composable +private fun BoxScope.ContentText( + contentShowState: ContentShowState, + dateModel: DateModel +) { + AnimatedVisibility( + visible = contentShowState.state, + modifier = Modifier.align(Alignment.Center), + enter = fadeIn(), + exit = fadeOut() + ) { + Text( + text = dateModel.text ?: "테스트", + modifier = Modifier + .background( + Color.Black.copy(alpha = 0.5f), RoundedCornerShape(4.dp) + ) + .padding(4.dp), + color = Color.White, + fontSize = 16.sp + ) + } +} - R.string.edit_text -> { - startForResult.launch( - Intent(activity.applicationContext, UploadFilmActivity::class.java).apply { - putExtra(DateFragment.KEY_CALENDAR_INDEX, activityViewModel.calendarIndex) - putExtra( - SelectVideoActivity.DATE_VIDEO_ITEM, - DateAndVideoModel( - viewModel.videoUri.value ?: return, - viewModel.dateModel.getDate() - ) - ) - putExtra(CalendarActivity.KEY_EDIT_STATE, EditState.EDIT_CONTENT) - putExtra(PlayFilmFragment.KEY_DATE_MODEL, viewModel.dateModel) - } - ) - } +@Composable +private fun BoxScope.TextLottieAnimation( + textComposition: LottieComposition?, + textAnimatable: LottieAnimatable, + onClick: () -> Unit +) { + LottieAnimation( + composition = textComposition, + progress = textAnimatable.progress, + modifier = Modifier + .align(Alignment.BottomCenter) + .background(blackBlur, RoundedCornerShape(50.dp)) + .size(dimensionResource(id = R.dimen.large_200)) + .padding(4.dp) + .clickable(onClick = onClick) + ) +} + +@Composable +private fun BoxScope.DateText(dateModel: DateModel) { + Box( + modifier = Modifier + .background(blackBlur, RoundedCornerShape(4.dp)) + .align(Alignment.TopStart) + .padding(start = 6.dp, end = 6.dp, top = 4.dp, bottom = 4.dp) + .height(dimensionResource(id = R.dimen.normal_175)) + ) { + Text( + modifier = Modifier.align(Alignment.Center), + text = stringResource( + R.string.date, dateModel.year, dateModel.month, dateModel.day + ), + color = Color.White, + fontSize = 16.sp + ) } } @@ -296,12 +281,12 @@ fun BottomSheetPreView() { } @Composable -fun BottomSheetView(model: BottomSheetModel, onClick: (Int) -> Unit) { +private fun BottomSheetView(model: BottomSheetModel, dialogEvent: (Int) -> Unit) { Row( modifier = Modifier .padding(dimensionResource(id = R.dimen.normal_100)) - .clickable { onClick(model.title) }, + .clickable { dialogEvent(model.title) }, horizontalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.normal_125)), verticalAlignment = Alignment.CenterVertically ) { @@ -320,7 +305,7 @@ fun BottomSheetView(model: BottomSheetModel, onClick: (Int) -> Unit) { } @Composable -fun FailurePlay(activity: Activity, state: PlayState.Failure) { +private fun FailurePlay(activity: Activity, state: PlayState.Failure) { state.throwable.message?.let { Snackbar.make( activity.findViewById(android.R.id.content), it, Snackbar.LENGTH_SHORT @@ -329,27 +314,15 @@ fun FailurePlay(activity: Activity, state: PlayState.Failure) { } @Composable -fun DialogUI(viewModel: PlayFilmViewModel) { - val openDialog = viewModel.openDialog.collectAsStateWithLifecycle().value +private fun DialogUI(viewModel: PlayFilmViewModel) { + val openDialog by viewModel.openDialog.collectAsStateWithLifecycle() // CustomDialog if (openDialog) { CustomDialog( stringResource(id = R.string.delete_dialog), - { viewModel.closeDialog() }, + { viewModel.setDialog(false) }, { viewModel.deleteVideo() } ) } -} - -fun setResultCalendar( - state: PlayState.Deleted, activity: Activity, activityViewModel: PlayFilmActivityViewModel -) { - activity.setResult(Activity.RESULT_OK, Intent( - activity, CalendarActivity::class.java - ).apply { - putExtra(DateFragment.KEY_CALENDAR_INDEX, activityViewModel.calendarIndex) - putExtra(PlayFilmComposeFragment.KEY_DATE_MODEL, state.dateModel) - }) - activity.finish() } \ No newline at end of file From 6529a5988347245db4ab68c41a16be1484a67a6c Mon Sep 17 00:00:00 2001 From: junhyeongleeee Date: Mon, 17 Jul 2023 21:54:19 +0900 Subject: [PATCH 12/13] issue #160 chore: javaVersion 11 Co-authored-by: junhyeongleeee --- app/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 3c685f4e..a370235e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -30,11 +30,11 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 } kotlinOptions { - jvmTarget = '17' + jvmTarget = '11' } testOptions { unitTests.returnDefaultValues = true From 7552be073e50b1722b9c668aa3fe6aaefd810748 Mon Sep 17 00:00:00 2001 From: junhyeongleeee Date: Fri, 21 Jul 2023 14:40:42 +0900 Subject: [PATCH 13/13] =?UTF-8?q?issue=20#160=20feat:=20extendedSpans=20?= =?UTF-8?q?=EC=9D=B4=EC=9A=A9=ED=95=9C=20text=20=EB=B3=B4=EC=97=AC?= =?UTF-8?q?=EC=A3=BC=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: junhyeongleeee --- app/build.gradle | 2 + .../compose/PlayFilmFragmentCompose.kt | 56 +++++++++++++++++-- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index a370235e..c102cfdc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,6 +76,8 @@ dependencies { implementation 'androidx.compose.runtime:runtime-livedata' // collectAsStateWithLifecycle() implementation "androidx.lifecycle:lifecycle-runtime-compose:2.6.1" + // Compose Text Effect + implementation "me.saket.extendedspans:extendedspans:1.3.0" // Navigation implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmFragmentCompose.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmFragmentCompose.kt index 70de1d97..46efa95b 100644 --- a/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmFragmentCompose.kt +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/playfilm/compose/PlayFilmFragmentCompose.kt @@ -19,6 +19,7 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.MaterialTheme import androidx.compose.material.ModalBottomSheetLayout import androidx.compose.material.ModalBottomSheetValue import androidx.compose.material.Text @@ -26,6 +27,7 @@ import androidx.compose.material.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -35,6 +37,11 @@ import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -55,6 +62,12 @@ import com.boostcamp.dailyfilm.presentation.util.PlayState import com.boostcamp.dailyfilm.presentation.util.dialog.CustomDialog import com.google.android.material.snackbar.Snackbar import kotlinx.coroutines.launch +import me.saket.extendedspans.ExtendedSpans +import me.saket.extendedspans.RoundedCornerSpanPainter +import me.saket.extendedspans.SquigglyUnderlineSpanPainter +import me.saket.extendedspans.drawBehind +import me.saket.extendedspans.rememberSquigglyUnderlineAnimator +import kotlin.time.Duration val playFilmBottomSheetModelList = listOf( BottomSheetModel(R.drawable.ic_delete, R.string.delete), @@ -106,6 +119,18 @@ private fun PlayScreen( val soundAnimatable = rememberLottieAnimatable() val textAnimatable = rememberLottieAnimatable() + val squigglyAniamtor = rememberSquigglyUnderlineAnimator(duration = Duration.parse("3s")) + val extendedSpans = remember { + ExtendedSpans( + RoundedCornerSpanPainter( + padding = RoundedCornerSpanPainter.TextPaddingValues(6.sp, 6.sp), + topMargin = 2.sp, + bottomMargin = 2.sp + ), + SquigglyUnderlineSpanPainter(wavelength = 20.sp, animator = squigglyAniamtor) + ) + } + // LottieAnimation LaunchedEffect(muteState.state) { soundAnimatable.animate( @@ -162,6 +187,7 @@ private fun PlayScreen( } ContentText( + extendedSpans = extendedSpans, contentShowState = contentShowState, dateModel = dateModel ) @@ -214,6 +240,7 @@ private fun MenuImage( @Composable private fun BoxScope.ContentText( + extendedSpans: ExtendedSpans, contentShowState: ContentShowState, dateModel: DateModel ) { @@ -224,12 +251,31 @@ private fun BoxScope.ContentText( exit = fadeOut() ) { Text( - text = dateModel.text ?: "테스트", + text = buildAnnotatedString { + (dateModel.text ?: "").split("\n").also { texts -> + texts.forEachIndexed { i, text -> + append( + extendedSpans.extend( + AnnotatedString( + text, + spanStyle = SpanStyle( + textDecoration = TextDecoration.Underline, + color = MaterialTheme.colors.surface, + background = MaterialTheme.colors.primary + ), + ) + ) + ) + if (i < texts.size) { + appendLine() + } + } + } + }, modifier = Modifier - .background( - Color.Black.copy(alpha = 0.5f), RoundedCornerShape(4.dp) - ) - .padding(4.dp), + .drawBehind(extendedSpans) + .align(Alignment.Center), + textAlign = TextAlign.Center, color = Color.White, fontSize = 16.sp )