From 3a4dd10555e99ba4d07395ae9274f0c1a6b90818 Mon Sep 17 00:00:00 2001 From: junhyeongleeee Date: Tue, 25 Jul 2023 21:12:41 +0900 Subject: [PATCH 1/7] =?UTF-8?q?issue=20#171=20chore:=20gray=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: junhyeongleeee --- .../java/com/boostcamp/dailyfilm/presentation/ui/theme/Color.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/ui/theme/Color.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/ui/theme/Color.kt index 62ddebb..d6b0429 100644 --- a/app/src/main/java/com/boostcamp/dailyfilm/presentation/ui/theme/Color.kt +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/ui/theme/Color.kt @@ -7,6 +7,7 @@ val blackBlur = Color(0x4D000000) val white = Color.White val lightBlack = Color(0xFF202022) val lightGray = Color(0xFFE1E1E1) +val gray = Color(0xFFCFCFCF) val primary = lightBlack val primaryVariant = blackBlur From 448349945d5d8b3028f27c74d670b50afefce041 Mon Sep 17 00:00:00 2001 From: junhyeongleeee Date: Tue, 25 Jul 2023 21:13:20 +0900 Subject: [PATCH 2/7] =?UTF-8?q?issue=20#171=20feat:=20rememberlifecyclerEv?= =?UTF-8?q?ent=20,=20noRippleClickable=20util=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 --- .../util/compose/noRippleClickable.kt | 14 ++++++++++ .../util/compose/rememberLifecycleEvent.kt | 27 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 app/src/main/java/com/boostcamp/dailyfilm/presentation/util/compose/noRippleClickable.kt create mode 100644 app/src/main/java/com/boostcamp/dailyfilm/presentation/util/compose/rememberLifecycleEvent.kt diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/util/compose/noRippleClickable.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/util/compose/noRippleClickable.kt new file mode 100644 index 0000000..d2d4793 --- /dev/null +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/util/compose/noRippleClickable.kt @@ -0,0 +1,14 @@ +package com.boostcamp.dailyfilm.presentation.util.compose + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.composed + +inline fun Modifier.noRippleClickable(crossinline onClick: ()->Unit): Modifier = composed { + clickable(indication = null, + interactionSource = remember { MutableInteractionSource() }) { + onClick() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/util/compose/rememberLifecycleEvent.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/util/compose/rememberLifecycleEvent.kt new file mode 100644 index 0000000..df2822c --- /dev/null +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/util/compose/rememberLifecycleEvent.kt @@ -0,0 +1,27 @@ +package com.boostcamp.dailyfilm.presentation.util.compose + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver +import androidx.lifecycle.LifecycleOwner + +@Composable +fun rememberLifecycleEvent(lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current): Lifecycle.Event { + var state by remember { mutableStateOf(Lifecycle.Event.ON_ANY) } + DisposableEffect(lifecycleOwner) { + val observer = LifecycleEventObserver { _, event -> + state = event + } + lifecycleOwner.lifecycle.addObserver(observer) + onDispose { + lifecycleOwner.lifecycle.removeObserver(observer) + } + } + return state +} \ No newline at end of file From 2dc079a87375a25317a3c34aab1e1491686c8e20 Mon Sep 17 00:00:00 2001 From: junhyeongleeee Date: Tue, 25 Jul 2023 21:15:58 +0900 Subject: [PATCH 3/7] =?UTF-8?q?issue=20#171=20chore:=20error=20=EC=A3=BC?= =?UTF-8?q?=EC=84=9D=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: junhyeongleeee --- .../uploadfilm/UploadFilmActivity.kt | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/uploadfilm/UploadFilmActivity.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/uploadfilm/UploadFilmActivity.kt index 02af928..c821628 100644 --- a/app/src/main/java/com/boostcamp/dailyfilm/presentation/uploadfilm/UploadFilmActivity.kt +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/uploadfilm/UploadFilmActivity.kt @@ -1,6 +1,5 @@ package com.boostcamp.dailyfilm.presentation.uploadfilm -import android.animation.ValueAnimator import android.content.Context import android.content.Intent import android.os.Build @@ -15,18 +14,13 @@ import androidx.lifecycle.repeatOnLifecycle import com.boostcamp.dailyfilm.R import com.boostcamp.dailyfilm.databinding.ActivityUploadFilmBinding import com.boostcamp.dailyfilm.presentation.BaseActivity -import com.boostcamp.dailyfilm.presentation.calendar.CalendarActivity import com.boostcamp.dailyfilm.presentation.calendar.DateFragment.Companion.KEY_CALENDAR_INDEX -import com.boostcamp.dailyfilm.presentation.playfilm.PlayFilmActivity -import com.boostcamp.dailyfilm.presentation.playfilm.PlayFilmFragment.Companion.KET_EDIT_TEXT -import com.boostcamp.dailyfilm.presentation.playfilm.PlayFilmFragment.Companion.KEY_DATE_MODEL -import com.boostcamp.dailyfilm.presentation.playfilm.model.EditState import com.boostcamp.dailyfilm.presentation.selectvideo.SelectVideoActivity import com.boostcamp.dailyfilm.presentation.trimvideo.TrimVideoActivity import com.boostcamp.dailyfilm.presentation.util.LottieDialogFragment +import com.boostcamp.dailyfilm.presentation.util.UiState import com.boostcamp.dailyfilm.presentation.util.network.NetworkManager import com.boostcamp.dailyfilm.presentation.util.network.NetworkState -import com.boostcamp.dailyfilm.presentation.util.UiState import com.boostcamp.dailyfilm.presentation.util.network.networkAlertDialog import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar @@ -72,7 +66,7 @@ class UploadFilmActivity : BaseActivity(R.layout.acti when (state) { is UiState.Success -> { loadingDialogFragment.hideProgressDialog() - when (viewModel.editState) { + /*when (viewModel.editState) { EditState.EDIT_CONTENT -> { setResult( RESULT_OK, @@ -96,7 +90,7 @@ class UploadFilmActivity : BaseActivity(R.layout.acti } ) } - } + }*/ finish() } is UiState.Loading -> { @@ -159,7 +153,7 @@ class UploadFilmActivity : BaseActivity(R.layout.acti putExtra(SelectVideoActivity.DATE_VIDEO_ITEM, viewModel.beforeItem) putExtra(TrimVideoActivity.KEY_DATE_MODEL, viewModel.dateModel) putExtra(KEY_CALENDAR_INDEX, viewModel.calendarIndex) - putExtra(CalendarActivity.KEY_EDIT_STATE, viewModel.editState) +// putExtra(CalendarActivity.KEY_EDIT_STATE, viewModel.editState) } ) } @@ -173,7 +167,7 @@ class UploadFilmActivity : BaseActivity(R.layout.acti private fun soundControl() { lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.clickSound.collect { check -> + /*viewModel.clickSound.collect { check -> if (check) { binding.backgroundPlayer.player?.volume = 0.5f val animator = ValueAnimator.ofFloat(0.5f, 1.0f).setDuration(500) @@ -191,7 +185,7 @@ class UploadFilmActivity : BaseActivity(R.layout.acti } animator.start() } - } + }*/ } } } From fee1db1a5455660001c7c70d601121c7586f9f5c Mon Sep 17 00:00:00 2001 From: junhyeongleeee Date: Tue, 25 Jul 2023 21:17:53 +0900 Subject: [PATCH 4/7] =?UTF-8?q?issue=20#171=20feat:=20content=20text=20rem?= =?UTF-8?q?ember=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: junhyeongleeee --- .../compose/PlayFilmFragmentCompose.kt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) 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 46efa95..04eb2bd 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 @@ -109,6 +109,7 @@ private fun PlayScreen( val muteState by viewModel.muteState.collectAsStateWithLifecycle() val contentShowState by viewModel.contentShowState.collectAsStateWithLifecycle() val dateModel = viewModel.dateModel + val contentText by viewModel.text.collectAsStateWithLifecycle() val soundComposition by rememberLottieComposition( LottieCompositionSpec.Asset(stringResource(R.string.lottie_sound)) @@ -189,7 +190,7 @@ private fun PlayScreen( ContentText( extendedSpans = extendedSpans, contentShowState = contentShowState, - dateModel = dateModel + text = contentText ) if (state != PlayState.Playing) { @@ -242,7 +243,7 @@ private fun MenuImage( private fun BoxScope.ContentText( extendedSpans: ExtendedSpans, contentShowState: ContentShowState, - dateModel: DateModel + text: String? ) { AnimatedVisibility( visible = contentShowState.state, @@ -251,8 +252,11 @@ private fun BoxScope.ContentText( exit = fadeOut() ) { Text( + modifier = Modifier + .drawBehind(extendedSpans) + .align(Alignment.Center), text = buildAnnotatedString { - (dateModel.text ?: "").split("\n").also { texts -> + (text ?: "").split("\n").also { texts -> texts.forEachIndexed { i, text -> append( extendedSpans.extend( @@ -272,12 +276,8 @@ private fun BoxScope.ContentText( } } }, - modifier = Modifier - .drawBehind(extendedSpans) - .align(Alignment.Center), - textAlign = TextAlign.Center, - color = Color.White, - fontSize = 16.sp + fontSize = 22.sp, + textAlign = TextAlign.Center ) } } From 6b5b20fe9df4cbda0137013df91f5ee94c79be80 Mon Sep 17 00:00:00 2001 From: junhyeongleeee Date: Tue, 25 Jul 2023 21:18:24 +0900 Subject: [PATCH 5/7] issue #171 feat: content text nullable Co-authored-by: junhyeongleeee --- .../dailyfilm/presentation/playfilm/PlayFilmViewModel.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 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 d13f5ca..e57b778 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 @@ -33,8 +33,8 @@ class PlayFilmViewModel @Inject constructor( var dateModel = savedStateHandle.get(PlayFilmFragment.KEY_DATE_MODEL) ?: throw IllegalStateException("PlayFilmViewModel - DateModel is null") - private val _text = MutableLiveData(dateModel.text) - val text: LiveData get() = _text + private val _text = MutableStateFlow(dateModel.text) + val text: StateFlow get() = _text private val _videoUri = MutableLiveData() val videoUri: LiveData get() = _videoUri From c2702957f0aa227a18d2c4f43c72f51587f65eaa Mon Sep 17 00:00:00 2001 From: junhyeongleeee Date: Tue, 25 Jul 2023 21:35:51 +0900 Subject: [PATCH 6/7] issue #171 feat: calendar compose migration Co-authored-by: junhyeongleeee --- .../presentation/calendar/DateViewModel.kt | 7 + .../calendar/adpater/CalendarPagerAdapter.kt | 9 +- .../calendar/compose/CalendarView.kt | 271 ++++++++++++++++++ .../calendar/compose/DateComposeFragment.kt | 111 +++++++ .../calendar/compose/DateState.kt | 20 ++ .../main/res/layout/fragment_date_compose.xml | 23 ++ 6 files changed, 437 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/compose/CalendarView.kt create mode 100644 app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/compose/DateComposeFragment.kt create mode 100644 app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/compose/DateState.kt create mode 100644 app/src/main/res/layout/fragment_date_compose.xml diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/DateViewModel.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/DateViewModel.kt index 18a79a2..b08e219 100644 --- a/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/DateViewModel.kt +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/DateViewModel.kt @@ -7,6 +7,7 @@ import com.boostcamp.dailyfilm.data.calendar.CalendarRepository import com.boostcamp.dailyfilm.data.model.DailyFilmItem import com.boostcamp.dailyfilm.data.sync.SyncRepository import com.boostcamp.dailyfilm.presentation.calendar.DateFragment.Companion.KEY_CALENDAR +import com.boostcamp.dailyfilm.presentation.calendar.compose.DateState import com.boostcamp.dailyfilm.presentation.calendar.model.DateModel import com.boostcamp.dailyfilm.presentation.util.* import dagger.hilt.android.lifecycle.HiltViewModel @@ -27,6 +28,12 @@ class DateViewModel @Inject constructor( val calendar = savedStateHandle.get(KEY_CALENDAR) ?: throw IllegalStateException("CalendarViewModel - calendar is null") + val todayCalendar = createCalendar(Locale.getDefault()).apply { + set(Calendar.HOUR_OF_DAY, 24) + } + private val _dateState = MutableStateFlow(DateState()) + val dateState : StateFlow get() = _dateState + private val dateFormat = SimpleDateFormat("yyyyMMdd", Locale.getDefault()) private val dayOfWeek = createCalendar(calendar, day = 1).dayOfWeek() private val prevCalendar = createCalendar(calendar, month = calendar.month() - 1) diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/adpater/CalendarPagerAdapter.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/adpater/CalendarPagerAdapter.kt index 7e72046..45a8414 100644 --- a/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/adpater/CalendarPagerAdapter.kt +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/adpater/CalendarPagerAdapter.kt @@ -2,8 +2,9 @@ package com.boostcamp.dailyfilm.presentation.calendar.adpater import androidx.fragment.app.FragmentActivity import androidx.viewpager2.adapter.FragmentStateAdapter -import com.boostcamp.dailyfilm.presentation.calendar.DateFragment -import java.util.* +import com.boostcamp.dailyfilm.presentation.calendar.compose.DateComposeFragment +import java.util.Calendar +import java.util.Locale class CalendarPagerAdapter( fragmentActivity: FragmentActivity @@ -11,13 +12,13 @@ class CalendarPagerAdapter( override fun getItemCount(): Int = Int.MAX_VALUE - override fun createFragment(position: Int): DateFragment { + override fun createFragment(position: Int): DateComposeFragment { val calendar = Calendar.getInstance(Locale.getDefault()) calendar.add(Calendar.MONTH, getItemId(position).toInt()) if (getItemId(position).toInt() != 0) { calendar.set(Calendar.DAY_OF_MONTH, 1) } - return DateFragment.newInstance(calendar) + return DateComposeFragment.newInstance(calendar) } override fun getItemId(position: Int): Long = (position - START_POSITION).toLong() diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/compose/CalendarView.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/compose/CalendarView.kt new file mode 100644 index 0000000..4c91686 --- /dev/null +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/compose/CalendarView.kt @@ -0,0 +1,271 @@ +package com.boostcamp.dailyfilm.presentation.calendar.compose + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.drawWithCache +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.layout.Layout +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.TextUnit +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.boostcamp.dailyfilm.data.model.DailyFilmItem +import com.boostcamp.dailyfilm.presentation.calendar.DateViewModel +import com.boostcamp.dailyfilm.presentation.calendar.model.DateModel +import com.boostcamp.dailyfilm.presentation.util.compose.noRippleClickable +import com.boostcamp.dailyfilm.presentation.util.compose.rememberLifecycleEvent +import com.boostcamp.dailyfilm.presentation.util.createCalendar +import com.boostcamp.dailyfilm.presentation.util.month +import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi +import com.bumptech.glide.integration.compose.GlideImage +import java.util.Calendar + +@Composable +fun CalendarView( + viewModel: DateViewModel, + resetFilm: (List) -> Unit, + imgClick: (Int, DateModel) -> Unit, +) { + val lifecycleEvent = rememberLifecycleEvent() + val itemList by viewModel.itemFlow.collectAsStateWithLifecycle(initialValue = null) + val reloadList by viewModel.dateFlow.collectAsStateWithLifecycle(minActiveState = Lifecycle.State.RESUMED) + val dateState by viewModel.dateState.collectAsStateWithLifecycle() + + CalendarView( + lifecycleEvent = lifecycleEvent, + itemList = itemList, + reloadList = reloadList, + dateState = dateState, + currentCalendar = viewModel.calendar, + todayCalendar = viewModel.todayCalendar, + reloadCalendar = { + viewModel.reloadCalendar(it) + }, + resetFilm = resetFilm, + imgClick = imgClick + ) +} + +@Composable +fun CalendarView( + lifecycleEvent: Lifecycle.Event, + itemList: List?, + reloadList: List, + dateState: DateState, + currentCalendar: Calendar, + todayCalendar: Calendar, + reloadCalendar: (List) -> Unit, + resetFilm: (List) -> Unit, + imgClick: (Int, DateModel) -> Unit, +) { + + val textSize = 12.sp + val textHeight = with(LocalDensity.current) { + textSize.toPx() + 10 + }.toInt() + + LaunchedEffect(lifecycleEvent) { + when (lifecycleEvent) { + Lifecycle.Event.ON_PAUSE -> { + dateState.selectedDay = null + } + + Lifecycle.Event.ON_RESUME -> { + // onResume 에서 가 아닌 repeatOnLifecycle 의 RESUMED 상태로 받아도 됐었음. + // LaunchedEffect(key1 = reloadList) 가 안됨 + resetFilm( + reloadList.filter { dateModel -> dateModel.videoUrl != null } + ) + } + + else -> {} + } + } + LaunchedEffect(key1 = reloadList) { + resetFilm( + reloadList.filter { dateModel -> dateModel.videoUrl != null } + ) + } + + LaunchedEffect(key1 = itemList) { + reloadCalendar(itemList ?: return@LaunchedEffect) + } + + CustomCalendarView( + textHeight = textHeight, + textSize = textSize, + reloadList = reloadList, + currentCalendar = currentCalendar, + todayCalendar = todayCalendar, + dateState = dateState, + imgClick = imgClick + ) +} + +@OptIn(ExperimentalGlideComposeApi::class) +@Composable +private fun DateImage(background: Color, alpha: Float, url: String?, onClick: () -> Unit) { + + GlideImage( + modifier = Modifier + .fillMaxSize() + .background(background) + .alpha(alpha) + .padding(2.dp) + .clip(RoundedCornerShape(5.dp)) + .noRippleClickable(onClick = onClick), + contentScale = ContentScale.Crop, + model = url, + contentDescription = "" + ) +} + +@Composable +private fun CustomCalendarView( + textHeight: Int, + textSize: TextUnit, + reloadList: List, + currentCalendar: Calendar, + todayCalendar: Calendar, + dateState: DateState, + imgClick: (Int, DateModel) -> Unit, +) { + + CustomCalendarView( + textHeight = textHeight + ) { + reloadList.forEachIndexed { index, dateModel -> + + val isNotCurrentMonth = isNotCurrentMonth( + dateModel, + currentCalendar.month(), + todayCalendar + ) + dateState.isCurrentMonth = isNotCurrentMonth + + Text( + modifier = Modifier + .alpha(dateState.alpha) + .background(dateState.isSelected(index)), + text = dateModel.day, + textAlign = TextAlign.Center, + color = MaterialTheme.colors.primary, + fontSize = textSize + ) + DateImage( + background = dateState.isSelected(index), + alpha = dateState.alpha, + url = dateModel.videoUrl + ) { + if (!isNotCurrentMonth) { + dateState.apply { + if (dateModel.videoUrl != null) { + selectedDay = null + imgClick(index, dateModel) + } else { + selectedDay = index + } + } + } + } + } + } +} + + +@Composable +private fun CustomCalendarView(textHeight: Int, content: @Composable () -> Unit) { + + val lineColor = MaterialTheme.colors.primary + + Layout( + modifier = Modifier + .fillMaxSize() + .drawWithCache { + onDrawWithContent { + drawContent() + repeat(5) { idx -> + val y = (idx + 1) * size.height / 6 + drawLine( + color = lineColor, + start = Offset(0f, y), + end = Offset(size.width, y), + strokeWidth = 2f + ) + } + } + }, + content = content, + ) { measureables, constraints -> + + val dayWidth = constraints.maxWidth / 7 + val dayHeight = constraints.maxHeight / 6 + + val placeables = measureables.mapIndexed { idx, measurable -> + measurable.measure( + when (idx % 2) { + 0 -> constraints.fixConstraints(width = dayWidth, height = textHeight) + 1 -> constraints.fixConstraints( + width = dayWidth, + height = dayHeight - textHeight + ) + + else -> constraints + } + ) + } + + layout(constraints.maxWidth, constraints.maxHeight) { + + placeables.forEachIndexed { index, placeable -> + val idx = index / 2 + + val verticalIdx = idx / 7 + val horizontalIdx = idx % 7 + + val left = horizontalIdx * dayWidth + val top = verticalIdx * dayHeight + + when (index % 2) { + 0 -> placeable.placeRelative(x = left, y = top) + 1 -> placeable.placeRelative(x = left, y = top + textHeight) + } + } + } + } +} + +private fun isNotCurrentMonth( + dateModel: DateModel, + currentMonth: Int, + todayCalendar: Calendar +): Boolean { + val itemCalendar = with(dateModel) { + createCalendar(year.toInt(), month.toInt() - 1, day.toInt()) + } + return dateModel.month.toInt() != currentMonth || + itemCalendar.timeInMillis > todayCalendar.timeInMillis +} + +private fun Constraints.fixConstraints(width: Int = maxWidth, height: Int = maxHeight) = copy( + minWidth = width, + maxWidth = width, + minHeight = height, + maxHeight = height +) \ No newline at end of file diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/compose/DateComposeFragment.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/compose/DateComposeFragment.kt new file mode 100644 index 0000000..3322073 --- /dev/null +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/compose/DateComposeFragment.kt @@ -0,0 +1,111 @@ +package com.boostcamp.dailyfilm.presentation.calendar.compose + +import android.annotation.SuppressLint +import android.app.Activity +import android.content.Intent +import android.os.Build +import android.os.Bundle +import android.util.Log +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 +import com.boostcamp.dailyfilm.databinding.FragmentDateComposeBinding +import com.boostcamp.dailyfilm.presentation.BaseFragment +import com.boostcamp.dailyfilm.presentation.calendar.CalendarActivity.Companion.KEY_FILM_ARRAY +import com.boostcamp.dailyfilm.presentation.calendar.CalendarViewModel +import com.boostcamp.dailyfilm.presentation.calendar.DateViewModel +import com.boostcamp.dailyfilm.presentation.calendar.model.DateModel +import com.boostcamp.dailyfilm.presentation.playfilm.PlayFilmActivity +import com.boostcamp.dailyfilm.presentation.playfilm.PlayFilmFragment.Companion.KEY_DATE_MODEL +import com.boostcamp.dailyfilm.presentation.ui.theme.DailyFilmTheme +import dagger.hilt.android.AndroidEntryPoint +import java.util.* + +@AndroidEntryPoint +class DateComposeFragment : + BaseFragment(R.layout.fragment_date_compose) { + + private val viewModel: DateViewModel by viewModels() + private val activityViewModel: CalendarViewModel by activityViewModels() + + private val startForResult: ActivityResultLauncher = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult -> + if (result.resultCode == Activity.RESULT_OK && result.data != null) { + val calendarIndex = result.data?.getIntExtra(KEY_CALENDAR_INDEX, -1) + ?: return@registerForActivityResult + val dateModel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + result.data?.getParcelableExtra(KEY_DATE_MODEL, DateModel::class.java) + } else { + result.data?.getParcelableExtra(KEY_DATE_MODEL) + } + dateModel ?: return@registerForActivityResult + viewModel.setVideo(calendarIndex, dateModel) + } + } + + @SuppressLint("ClickableViewAccessibility") + override fun initView() { + initSync() + initBinding() + } + + private fun initBinding() { + binding.composeView.apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + DailyFilmTheme( + requireActivity() + ) { + CalendarView( + viewModel = viewModel, + resetFilm = { + activityViewModel.emitFilm(it) + }, + imgClick = { idx, dateModel -> + Log.d("CalendarView", "${ArrayList(activityViewModel.filmFlow.value)}") + startForResult.launch( + Intent(requireContext(), PlayFilmActivity::class.java).apply { + putExtra( + KEY_CALENDAR_INDEX, + idx + ) + putExtra( + KEY_DATE_MODEL_INDEX, + activityViewModel.filmFlow.value.indexOf(dateModel) + ) + putParcelableArrayListExtra( + KEY_FILM_ARRAY, + ArrayList(activityViewModel.filmFlow.value) + ) + } + ) + } + ) + } + } + } + } + + private fun initSync() { + if (viewModel.isSynced(viewModel.calendar.get(Calendar.YEAR)).not()) { + viewModel.syncFilmItem() + } + } + + companion object { + const val KEY_CALENDAR = "calendar" + const val KEY_DATE_MODEL_INDEX = "dateModelIndex" + const val KEY_CALENDAR_INDEX = "calendarIndex" + fun newInstance(calendar: Calendar): DateComposeFragment { + return DateComposeFragment().apply { + arguments = Bundle().apply { + putSerializable(KEY_CALENDAR, calendar) + } + } + } + } +} diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/compose/DateState.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/compose/DateState.kt new file mode 100644 index 0000000..335602c --- /dev/null +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/compose/DateState.kt @@ -0,0 +1,20 @@ +package com.boostcamp.dailyfilm.presentation.calendar.compose + +import androidx.compose.material.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.graphics.Color + + +class DateState { + var isCurrentMonth: Boolean = true + var selectedDay by mutableStateOf(null) + + val alpha: Float get() = if (isCurrentMonth) 0.3f else 1f + + @Composable + fun isSelected(idx: Int) = + if (selectedDay == idx) Color.Gray else MaterialTheme.colors.background +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_date_compose.xml b/app/src/main/res/layout/fragment_date_compose.xml new file mode 100644 index 0000000..22681e1 --- /dev/null +++ b/app/src/main/res/layout/fragment_date_compose.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + \ No newline at end of file From e7774c92636645fefdaf68b3375bd20fc987420c Mon Sep 17 00:00:00 2001 From: junhyeongleeee Date: Wed, 26 Jul 2023 17:08:49 +0900 Subject: [PATCH 7/7] =?UTF-8?q?issue=20#171=20refactor:=20measure=20?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EC=95=84=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: junhyeongleeee --- .../presentation/calendar/custom/CalendarView.kt | 8 ++------ .../presentation/calendar/custom/DateImgView.kt | 9 +-------- .../presentation/calendar/custom/DateTextView.kt | 11 ++--------- 3 files changed, 5 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/custom/CalendarView.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/custom/CalendarView.kt index 48a7657..21694ca 100644 --- a/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/custom/CalendarView.kt +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/custom/CalendarView.kt @@ -137,9 +137,7 @@ class CalendarView( } private fun createDateTextView(dateModel: DateModel) = DateTextView( - context, - tmpHorizontal, - textHeight + context ).apply { text = dateModel.day setTextColor(ContextCompat.getColor(this.context, R.color.OnSurface)) @@ -151,9 +149,7 @@ class CalendarView( private fun createDateImgView(dateModel: DateModel) = DateImgView( context, dateModel, - requestManager, - tmpHorizontal, - tmpVertical - textHeight + requestManager ).apply { setPadding(IMG_PADDING) } diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/custom/DateImgView.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/custom/DateImgView.kt index bfcc851..ff6df05 100644 --- a/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/custom/DateImgView.kt +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/custom/DateImgView.kt @@ -13,17 +13,10 @@ import com.bumptech.glide.request.transition.DrawableCrossFadeFactory class DateImgView constructor( context: Context, var dateModel: DateModel, - private val requestManager: RequestManager, - private val staticWidth: Int, - private val staticHeight: Int + private val requestManager: RequestManager ) : AppCompatImageView(context) { private val factory = DrawableCrossFadeFactory.Builder().setCrossFadeEnabled(true).build() - - override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { - setMeasuredDimension(staticWidth, staticHeight) - } - fun setVideoUrl(dateModel: DateModel) { this.dateModel = dateModel load(dateModel.videoUrl) diff --git a/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/custom/DateTextView.kt b/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/custom/DateTextView.kt index d40f017..33c41a1 100644 --- a/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/custom/DateTextView.kt +++ b/app/src/main/java/com/boostcamp/dailyfilm/presentation/calendar/custom/DateTextView.kt @@ -4,12 +4,5 @@ import android.content.Context import androidx.appcompat.widget.AppCompatTextView class DateTextView constructor( - context: Context, - private val staticWidth: Int, - private val staticHeight: Int, -) : AppCompatTextView(context) { - - override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { - setMeasuredDimension(staticWidth, staticHeight) - } -} + context: Context +) : AppCompatTextView(context)