Skip to content

Commit

Permalink
Merge pull request #156 from TTC1018/feature/#150
Browse files Browse the repository at this point in the history
영상 선택 화면 Compose 전환
  • Loading branch information
TTC1018 authored May 29, 2023
2 parents 10e5c6d + 4ee6b89 commit 5dd650f
Show file tree
Hide file tree
Showing 12 changed files with 127 additions and 287 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ interface GalleryVideoRepository {
class GalleryVideoRepositoryImpl @Inject constructor(
private val contentResolver: ContentResolver
) : GalleryVideoRepository {
override fun loadVideo(): Flow<PagingData<VideoItem>> {
return Pager(config = PagingConfig(pageSize = GalleryPagingSource.PAGING_SIZE)) {
GalleryPagingSource(contentResolver)
}.flow
}

override fun loadVideo(): Flow<PagingData<VideoItem>> = Pager(config = PagingConfig(pageSize = GalleryPagingSource.PAGING_SIZE)) {
GalleryPagingSource(contentResolver)
}.flow

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import androidx.lifecycle.repeatOnLifecycle
import com.boostcamp.dailyfilm.R
import com.boostcamp.dailyfilm.databinding.ActivitySelectVideoBinding
import com.boostcamp.dailyfilm.presentation.BaseActivity
import com.boostcamp.dailyfilm.presentation.calendar.CalendarActivity
import com.boostcamp.dailyfilm.presentation.calendar.CalendarActivity.Companion.FLAG_FROM_VIEW
import com.boostcamp.dailyfilm.presentation.calendar.CalendarActivity.Companion.KEY_DATE_MODEL
import com.boostcamp.dailyfilm.presentation.calendar.CalendarActivity.Companion.KEY_EDIT_STATE
Expand All @@ -42,6 +41,11 @@ class SelectVideoActivity :
override fun initView() {

binding.viewModel = viewModel
binding.mediaListCompose.setContent {
VideoLists(
viewModel = viewModel
)
}
checkPermission()
setObserveUserEvent()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,8 @@ package com.boostcamp.dailyfilm.presentation.selectvideo
import android.net.Uri
import android.widget.ImageView
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.databinding.BindingAdapter
import androidx.lifecycle.*
import androidx.paging.PagingData
import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.ConcatAdapter.Config
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.boostcamp.dailyfilm.data.model.VideoItem
import com.boostcamp.dailyfilm.presentation.selectvideo.adapter.SelectVideoAdapter
import com.boostcamp.dailyfilm.presentation.selectvideo.adapter.VideoLoadStateAdapter
import com.boostcamp.dailyfilm.presentation.selectvideo.adapter.VideoSelectListener
import com.bumptech.glide.Glide
import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.MediaItem
Expand All @@ -24,29 +14,6 @@ import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

@BindingAdapter(value = ["thisItem", "selectedVideo"], requireAll = false)
fun ConstraintLayout.chooseVideoAndUpdateAlpha(
videoItem: VideoItem,
clickListener: VideoSelectListener
) {
with(clickListener) {
viewTreeLifecycleScope.launch {
selectedVideo.collect {
alpha = if (videoItem.uri == it?.uri)
0.5f
else
1.0f
}
}

if (selectedVideo.value == null) {
alpha = 0.5f
clickListener.chooseVideo(videoItem)
}

}
}

@BindingAdapter("onUploaded")
fun TextView.showResultOnSnackBar(uploadResult: SharedFlow<Boolean>) {
findViewTreeLifecycleOwner()?.lifecycleScope?.launch {
Expand Down Expand Up @@ -76,44 +43,6 @@ fun StyledPlayerView.playVideo(uri: Uri?) {
}
}

@BindingAdapter(value = ["setVideoSelectListener", "updateAdapter"], requireAll = true)
fun RecyclerView.updateAdapter(
videoClickListener: VideoSelectListener,
videosState: StateFlow<PagingData<VideoItem>>
) {
if (adapter == null) {
itemAnimator = null
val newAdapter = SelectVideoAdapter(videoClickListener)
adapter = newAdapter.withLoadStateFooter(
footer = VideoLoadStateAdapter(newAdapter::retry)
).apply {
Config.Builder().apply {
setIsolateViewTypes(false)
}.build()
}

this.layoutManager = GridLayoutManager(this.context, 3).apply {
spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return when ((adapter as ConcatAdapter).getItemViewType(position)) {
1 -> {
3
}
else -> {
1
}
}
}
}
}
}
findViewTreeLifecycleOwner()?.lifecycleScope?.launch {
findViewTreeLifecycleOwner()?.repeatOnLifecycle(Lifecycle.State.STARTED) {
((adapter as ConcatAdapter).adapters[0] as SelectVideoAdapter).submitData(videosState.value)
}
}
}

@BindingAdapter("updateThumbnails")
fun ImageView.updateThumbnails(uri: Uri?) {
uri?.let {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package com.boostcamp.dailyfilm.presentation.selectvideo

import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.paging.LoadState
import androidx.paging.compose.collectAsLazyPagingItems
import com.boostcamp.dailyfilm.R
import com.boostcamp.dailyfilm.data.model.VideoItem
import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi
import com.bumptech.glide.integration.compose.GlideImage


@Composable
fun VideoLists(
modifier: Modifier = Modifier,
viewModel: SelectVideoViewModel
) {
val selectedVideo = viewModel.selectedVideo.collectAsStateWithLifecycle()
val nestedScrollInterop = rememberNestedScrollInteropConnection()
val lazyPagingItems = viewModel.videoItems.collectAsLazyPagingItems()

if (lazyPagingItems.itemCount > 0) {
LaunchedEffect(selectedVideo.value == null) {
viewModel.chooseVideo(lazyPagingItems[0])
}
}

// RecyclerView 역할 (GridLayout)
LazyVerticalGrid(
modifier = modifier
.padding(top = 3.dp, start = 3.dp, end = 3.dp)
.nestedScroll(nestedScrollInterop),
verticalArrangement = Arrangement.spacedBy(3.dp),
horizontalArrangement = Arrangement.spacedBy(3.dp),
columns = GridCells.Adaptive(minSize = 120.dp),
) {

items(lazyPagingItems.itemCount) { idx ->

val isSelected = selectedVideo.value == lazyPagingItems[idx]
val alpha by animateFloatAsState(if (isSelected) 0.5f else 1f)

VideoGrid(
modifier = Modifier
.clickable { viewModel.chooseVideo(lazyPagingItems[idx]) }
.alpha(alpha),
videoItem = lazyPagingItems[idx],
)

}

// Loading시 Indicator 보이기
if (lazyPagingItems.loadState.append == LoadState.Loading) {
item {
CircularProgressIndicator(
modifier = Modifier
.fillMaxWidth()
.wrapContentWidth(Alignment.CenterHorizontally),
color = colorResource(id = R.color.Primary)
)
}
}
}

}

@OptIn(ExperimentalGlideComposeApi::class)
@Composable
fun VideoGrid(
modifier: Modifier = Modifier,
videoItem: VideoItem?
) {

if (videoItem != null) {
GlideImage(
modifier = modifier
.aspectRatio(1f),
contentScale = ContentScale.Crop,
model = videoItem.uri,
contentDescription = null
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.boostcamp.dailyfilm.presentation.selectvideo
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
import androidx.paging.PagingData
import androidx.paging.cachedIn
import com.boostcamp.dailyfilm.data.model.VideoItem
Expand All @@ -12,10 +13,8 @@ import com.boostcamp.dailyfilm.presentation.calendar.CalendarActivity.Companion.
import com.boostcamp.dailyfilm.presentation.calendar.DateFragment
import com.boostcamp.dailyfilm.presentation.calendar.model.DateModel
import com.boostcamp.dailyfilm.presentation.playfilm.model.EditState
import com.boostcamp.dailyfilm.presentation.selectvideo.adapter.VideoSelectListener
import com.boostcamp.dailyfilm.presentation.uploadfilm.model.DateAndVideoModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import javax.inject.Inject
Expand All @@ -24,26 +23,23 @@ import javax.inject.Inject
class SelectVideoViewModel @Inject constructor(
private val selectVideoRepository: GalleryVideoRepository,
savedStateHandle: SavedStateHandle
) : ViewModel(), VideoSelectListener {
) : ViewModel() {

val dateModel = savedStateHandle.get<DateModel>(CalendarActivity.KEY_DATE_MODEL)
val calendarIndex = savedStateHandle.get<Int>(DateFragment.KEY_CALENDAR_INDEX)
val editState = savedStateHandle.get<EditState>(KEY_EDIT_STATE)

override val viewTreeLifecycleScope: CoroutineScope
get() = viewModelScope

private val _videosState = MutableStateFlow<PagingData<VideoItem>>(PagingData.empty())
val videosState: StateFlow<PagingData<VideoItem>> get() = _videosState

private val _selectedVideo = MutableStateFlow<VideoItem?>(null)
override val selectedVideo = _selectedVideo.asStateFlow()
val selectedVideo = _selectedVideo.asStateFlow()

private var clickSound = false

private val _eventFlow = MutableSharedFlow<SelectVideoEvent>()
val eventFlow: SharedFlow<SelectVideoEvent> = _eventFlow.asSharedFlow()

private val _videoItems = MutableStateFlow<PagingData<VideoItem>>(PagingData.empty())
val videoItems: StateFlow<PagingData<VideoItem>> get() = _videoItems

fun navigateToUpload() {
viewModelScope.launch {
selectedVideo.value?.let { selectedVideoItem ->
Expand Down Expand Up @@ -72,7 +68,7 @@ class SelectVideoViewModel @Inject constructor(

fun loadVideo() {
selectVideoRepository.loadVideo().cachedIn(viewModelScope).onEach { pagingData ->
_videosState.value = pagingData
_videoItems.value = pagingData
}.launchIn(viewModelScope)
}

Expand All @@ -82,7 +78,7 @@ class SelectVideoViewModel @Inject constructor(
}
}

override fun chooseVideo(videoItem: VideoItem?) {
fun chooseVideo(videoItem: VideoItem?) {
viewModelScope.launch {
_selectedVideo.emit(videoItem)
}
Expand Down

This file was deleted.

This file was deleted.

Loading

0 comments on commit 5dd650f

Please sign in to comment.