From f72f894727c239bfc9a4b71674b786f2ca2c60cd Mon Sep 17 00:00:00 2001 From: klaus Date: Thu, 28 Nov 2024 17:58:10 +0900 Subject: [PATCH] =?UTF-8?q?=EC=9E=AC=EC=83=9D=EB=AA=A9=EB=A1=9D=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AudioContentPlaylistListAdapter.kt | 58 +++++++++++ .../AudioContentPlaylistListFragment.kt | 76 +++++++++++++++ .../AudioContentPlaylistListRepository.kt | 5 + .../AudioContentPlaylistListViewModel.kt | 57 +++++++++++ .../playlist/GetPlaylistsResponse.kt | 16 +++ .../audio_content/playlist/PlaylistApi.kt | 13 +++ .../java/kr/co/vividnext/sodalive/di/AppDI.kt | 6 ++ .../fragment_audio_content_playlist_list.xml | 97 +++++++++++++++++++ .../main/res/layout/item_playlist_list.xml | 67 +++++++++++++ 9 files changed, 395 insertions(+) create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/AudioContentPlaylistListAdapter.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/AudioContentPlaylistListFragment.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/AudioContentPlaylistListRepository.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/AudioContentPlaylistListViewModel.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/GetPlaylistsResponse.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/PlaylistApi.kt create mode 100644 app/src/main/res/layout/fragment_audio_content_playlist_list.xml create mode 100644 app/src/main/res/layout/item_playlist_list.xml diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/AudioContentPlaylistListAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/AudioContentPlaylistListAdapter.kt new file mode 100644 index 0000000..6dddc63 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/AudioContentPlaylistListAdapter.kt @@ -0,0 +1,58 @@ +package kr.co.vividnext.sodalive.audio_content.playlist + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import coil.load +import coil.transform.RoundedCornersTransformation +import kr.co.vividnext.sodalive.R +import kr.co.vividnext.sodalive.databinding.ItemPlaylistListBinding +import kr.co.vividnext.sodalive.extensions.dpToPx + +class AudioContentPlaylistListAdapter( + private val onClickItem: (Long) -> Unit +) : RecyclerView.Adapter() { + + val items = mutableListOf() + + inner class ViewHolder( + private val binding: ItemPlaylistListBinding + ) : RecyclerView.ViewHolder(binding.root) { + @SuppressLint("SetTextI18n") + fun bind(item: GetPlaylistsItem) { + binding.ivCover.load(item.coverImageUrl) { + crossfade(true) + placeholder(R.drawable.bg_placeholder) + transformations(RoundedCornersTransformation(5.3f.dpToPx())) + } + + binding.tvTitle.text = item.title + binding.tvContentCount.text = "총 ${item.contentCount}개" + + if (item.desc.isNotBlank()) { + binding.tvDesc.text = item.desc + binding.tvDesc.visibility = View.VISIBLE + } else { + binding.tvDesc.visibility = View.GONE + } + + binding.root.setOnClickListener { onClickItem(item.id) } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( + ItemPlaylistListBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.bind(items[position]) + } + + override fun getItemCount() = items.count() +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/AudioContentPlaylistListFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/AudioContentPlaylistListFragment.kt new file mode 100644 index 0000000..cdde680 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/AudioContentPlaylistListFragment.kt @@ -0,0 +1,76 @@ +package kr.co.vividnext.sodalive.audio_content.playlist + +import android.annotation.SuppressLint +import android.os.Bundle +import android.view.View +import androidx.recyclerview.widget.LinearLayoutManager +import kr.co.vividnext.sodalive.base.BaseFragment +import kr.co.vividnext.sodalive.common.LoadingDialog +import kr.co.vividnext.sodalive.databinding.FragmentAudioContentPlaylistListBinding +import org.koin.android.ext.android.inject + +class AudioContentPlaylistListFragment : BaseFragment( + FragmentAudioContentPlaylistListBinding::inflate +) { + + private val viewModel: AudioContentPlaylistListViewModel by inject() + + private lateinit var loadingDialog: LoadingDialog + private lateinit var adapter: AudioContentPlaylistListAdapter + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + setupView() + bindData() + + viewModel.getPlaylistList() + } + + private fun setupView() { + loadingDialog = LoadingDialog(requireActivity(), layoutInflater) + adapter = AudioContentPlaylistListAdapter { } + + val recyclerView = binding.rvPlaylistList + recyclerView.setHasFixedSize(true) + recyclerView.layoutManager = LinearLayoutManager( + activity, + LinearLayoutManager.VERTICAL, + false + ) + recyclerView.adapter = adapter + } + + @SuppressLint("SetTextI18n") + private fun bindData() { + viewModel.isLoading.observe(viewLifecycleOwner) { + if (it) { + loadingDialog.show(screenWidth) + } else { + loadingDialog.dismiss() + } + } + + viewModel.toastLiveData.observe(viewLifecycleOwner) { + it?.let { showToast(it) } + } + + viewModel.totalCountLiveData.observe(viewLifecycleOwner) { + binding.tvCreatePlaylist.text = "${it}개" + } + + viewModel.playlistLiveData.observe(viewLifecycleOwner) { + if (it.isEmpty()) { + binding.tvTotalCount.visibility = View.GONE + binding.rvPlaylistList.visibility = View.GONE + binding.llNoPlaylist.visibility = View.VISIBLE + } else { + binding.llNoPlaylist.visibility = View.GONE + binding.tvTotalCount.visibility = View.VISIBLE + binding.rvPlaylistList.visibility = View.VISIBLE + + adapter.items.addAll(it) + } + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/AudioContentPlaylistListRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/AudioContentPlaylistListRepository.kt new file mode 100644 index 0000000..9093868 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/AudioContentPlaylistListRepository.kt @@ -0,0 +1,5 @@ +package kr.co.vividnext.sodalive.audio_content.playlist + +class AudioContentPlaylistListRepository(private val api: PlaylistApi) { + fun getPlaylistList(token: String) = api.getPlaylistList(authHeader = token) +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/AudioContentPlaylistListViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/AudioContentPlaylistListViewModel.kt new file mode 100644 index 0000000..47a48c1 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/AudioContentPlaylistListViewModel.kt @@ -0,0 +1,57 @@ +package kr.co.vividnext.sodalive.audio_content.playlist + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.orhanobut.logger.Logger +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.schedulers.Schedulers +import kr.co.vividnext.sodalive.base.BaseViewModel +import kr.co.vividnext.sodalive.common.SharedPreferenceManager + +class AudioContentPlaylistListViewModel( + private val repository: AudioContentPlaylistListRepository +) : BaseViewModel() { + private val _toastLiveData = MutableLiveData() + val toastLiveData: LiveData + get() = _toastLiveData + + private var _isLoading = MutableLiveData(false) + val isLoading: LiveData + get() = _isLoading + + private val _totalCountLiveData = MutableLiveData() + val totalCountLiveData: LiveData + get() = _totalCountLiveData + + private val _playlistLiveData = MutableLiveData>() + val playlistLiveData: LiveData> + get() = _playlistLiveData + + fun getPlaylistList() { + compositeDisposable.add( + repository.getPlaylistList(token = "Bearer ${SharedPreferenceManager.token}") + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + if (it.success && it.data != null) { + _totalCountLiveData.value = it.data.totalCount + _playlistLiveData.value = it.data.items + } else { + if (it.message != null) { + _toastLiveData.postValue(it.message) + } else { + _toastLiveData.postValue( + "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." + ) + } + } + }, + { + it.message?.let { message -> Logger.e(message) } + _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.") + } + ) + ) + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/GetPlaylistsResponse.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/GetPlaylistsResponse.kt new file mode 100644 index 0000000..942b819 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/GetPlaylistsResponse.kt @@ -0,0 +1,16 @@ +package kr.co.vividnext.sodalive.audio_content.playlist + +import com.google.gson.annotations.SerializedName + +data class GetPlaylistsResponse( + @SerializedName("totalCount") val totalCount: Int, + @SerializedName("items") val items: List +) + +data class GetPlaylistsItem( + @SerializedName("id") val id: Long, + @SerializedName("title") val title: String, + @SerializedName("desc") val desc: String, + @SerializedName("contentCount") val contentCount: Int, + @SerializedName("coverImageUrl") val coverImageUrl: String +) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/PlaylistApi.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/PlaylistApi.kt new file mode 100644 index 0000000..94ccc55 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/playlist/PlaylistApi.kt @@ -0,0 +1,13 @@ +package kr.co.vividnext.sodalive.audio_content.playlist + +import io.reactivex.rxjava3.core.Single +import kr.co.vividnext.sodalive.common.ApiResponse +import retrofit2.http.GET +import retrofit2.http.Header + +interface PlaylistApi { + @GET("/audio-content/playlist") + fun getPlaylistList( + @Header("Authorization") authHeader: String + ): Single> +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt b/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt index 47579a6..6a04810 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt @@ -24,6 +24,9 @@ import kr.co.vividnext.sodalive.audio_content.main.ranking.AudioContentMainRanki import kr.co.vividnext.sodalive.audio_content.main.recommend_series.AudioContentMainRecommendSeriesViewModel import kr.co.vividnext.sodalive.audio_content.modify.AudioContentModifyViewModel import kr.co.vividnext.sodalive.audio_content.order.AudioContentOrderListViewModel +import kr.co.vividnext.sodalive.audio_content.playlist.AudioContentPlaylistListRepository +import kr.co.vividnext.sodalive.audio_content.playlist.AudioContentPlaylistListViewModel +import kr.co.vividnext.sodalive.audio_content.playlist.PlaylistApi import kr.co.vividnext.sodalive.audio_content.series.SeriesApi import kr.co.vividnext.sodalive.audio_content.series.SeriesListAllViewModel import kr.co.vividnext.sodalive.audio_content.series.SeriesRepository @@ -191,6 +194,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) { single { ApiBuilder().build(get(), RouletteApi::class.java) } single { ApiBuilder().build(get(), CreatorCommunityApi::class.java) } single { ApiBuilder().build(get(), CategoryApi::class.java) } + single { ApiBuilder().build(get(), PlaylistApi::class.java) } } private val viewModelModule = module { @@ -265,6 +269,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) { viewModel { BlockMemberViewModel(get()) } viewModel { UserViewModel(get(), get()) } viewModel { MenuConfigViewModel(get()) } + viewModel { AudioContentPlaylistListViewModel(get()) } } private val repositoryModule = module { @@ -293,6 +298,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) { factory { CreatorCommunityRepository(get()) } factory { AlarmListRepository(get()) } factory { MenuConfigRepository(get()) } + factory { AudioContentPlaylistListRepository(get()) } } private val moduleList = listOf( diff --git a/app/src/main/res/layout/fragment_audio_content_playlist_list.xml b/app/src/main/res/layout/fragment_audio_content_playlist_list.xml new file mode 100644 index 0000000..2765d38 --- /dev/null +++ b/app/src/main/res/layout/fragment_audio_content_playlist_list.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_playlist_list.xml b/app/src/main/res/layout/item_playlist_list.xml new file mode 100644 index 0000000..6ece239 --- /dev/null +++ b/app/src/main/res/layout/item_playlist_list.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + +