From f75134c7e7aba454f6e4a3f251382db22d82e449 Mon Sep 17 00:00:00 2001 From: klaus Date: Thu, 13 Feb 2025 13:27:45 +0900 Subject: [PATCH] =?UTF-8?q?=EC=BD=98=ED=85=90=EC=B8=A0=20=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20-=20=EC=8B=9C=EB=A6=AC=EC=A6=88=20=ED=83=AD=20UI=20?= =?UTF-8?q?=EA=B5=AC=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/audio_content/AudioContentApi.kt | 6 + .../home/AudioContentMainTabHomeFragment.kt | 7 +- .../home/AudioContentMainTabHomeViewModel.kt | 2 - .../AudioContentMainTabSeriesFragment.kt | 744 ++++++++++++++++++ .../AudioContentMainTabSeriesRepository.kt | 7 + .../AudioContentMainTabSeriesViewModel.kt | 120 +++ .../series/GetContentMainTabSeriesResponse.kt | 34 + .../series/GetRecommendSeriesListResponse.kt | 14 + .../v2/series/GetSeriesCurationResponse.kt | 11 + .../v2/series/GetSeriesGenreListResponse.kt | 10 + .../AudioContentMainSeriesCurationAdapter.kt | 105 +++ .../AudioContentMainNewSeriesAdapter.kt | 77 ++ ...tMainTabSeriesOriginalAudioDramaAdapter.kt | 89 +++ .../AudioContentMainSeriesRankingAdapter.kt | 57 ++ ...oContentMainRecommendSeriesGenreAdapter.kt | 74 ++ .../role/AuditionRoleDetailActivity.kt | 2 - .../java/kr/co/vividnext/sodalive/di/AppDI.kt | 4 + .../sodalive/live/now/LiveNowAdapter.kt | 4 - .../fragment_audio_content_main_tab_home.xml | 2 +- ...fragment_audio_content_main_tab_series.xml | 254 +++++- .../item_audio_content_main_new_series.xml | 54 ++ ...item_audio_content_main_series_ranking.xml | 60 ++ .../item_series_original_audio_drama.xml | 103 +++ 23 files changed, 1823 insertions(+), 17 deletions(-) create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/AudioContentMainTabSeriesRepository.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/AudioContentMainTabSeriesViewModel.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/GetContentMainTabSeriesResponse.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/GetRecommendSeriesListResponse.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/GetSeriesCurationResponse.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/GetSeriesGenreListResponse.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/curation/AudioContentMainSeriesCurationAdapter.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/new_series/AudioContentMainNewSeriesAdapter.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/origianl_audio_drama/AudioContentMainTabSeriesOriginalAudioDramaAdapter.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/rank_series/AudioContentMainSeriesRankingAdapter.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/recommend_by_genre/AudioContentMainRecommendSeriesGenreAdapter.kt create mode 100644 app/src/main/res/layout/item_audio_content_main_new_series.xml create mode 100644 app/src/main/res/layout/item_audio_content_main_series_ranking.xml create mode 100644 app/src/main/res/layout/item_series_original_audio_drama.xml diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/AudioContentApi.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/AudioContentApi.kt index 8046c3b..75cd06b 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/AudioContentApi.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/AudioContentApi.kt @@ -17,6 +17,7 @@ import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainItem import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentRanking import kr.co.vividnext.sodalive.audio_content.main.v2.GetPopularContentByCreatorResponse import kr.co.vividnext.sodalive.audio_content.main.v2.home.GetContentMainTabHomeResponse +import kr.co.vividnext.sodalive.audio_content.main.v2.series.GetContentMainTabSeriesResponse import kr.co.vividnext.sodalive.audio_content.order.GetAudioContentOrderListResponse import kr.co.vividnext.sodalive.audio_content.order.OrderRequest import kr.co.vividnext.sodalive.audio_content.player.GenerateUrlResponse @@ -249,4 +250,9 @@ interface AudioContentApi { @Query("creatorId") creatorId: Long, @Header("Authorization") authHeader: String ): Single> + + @GET("/v2/audio-content/main/series") + fun getContentMainSeries( + @Header("Authorization") authHeader: String + ): Single> } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/home/AudioContentMainTabHomeFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/home/AudioContentMainTabHomeFragment.kt index bf83678..e8359b0 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/home/AudioContentMainTabHomeFragment.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/home/AudioContentMainTabHomeFragment.kt @@ -337,10 +337,7 @@ class AudioContentMainTabHomeFragment : BaseFragment( FragmentAudioContentMainTabSeriesBinding::inflate ) { + private val viewModel: AudioContentMainTabSeriesViewModel by inject() + + private lateinit var loadingDialog: LoadingDialog + private lateinit var contentBannerAdapter: AudioContentMainBannerAdapter + private lateinit var audioDramaAdapter: AudioContentMainTabSeriesOriginalAudioDramaAdapter + private lateinit var rankDailySeriesAdapter: AudioContentMainSeriesRankingAdapter + private lateinit var seriesGenreAdapter: AudioContentMainRecommendSeriesGenreAdapter + private lateinit var recommendSeriesByGenreAdapter: UserProfileSeriesListAdapter + private lateinit var newSeriesAdapter: AudioContentMainNewSeriesAdapter + private lateinit var completedSeriesAdapter: UserProfileSeriesListAdapter + private lateinit var recommendSeriesCreatorAdapter: ContentRankCreatorAdapter + private lateinit var recommendSeriesByChannelAdapter: UserProfileSeriesListAdapter + private lateinit var curationAdapter: AudioContentMainSeriesCurationAdapter + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupView() + bindData() + + viewModel.fetchData() + } + + private fun setupView() { + loadingDialog = LoadingDialog(requireActivity(), layoutInflater) + + setupContentBanner() + setupOriginalAudioDrama() + setupRankSeries() + setupRecommendSeriesGenre() + setupRecommendSeriesByGenre() + setupNewSeries() + setupCompleteSeries() + setupRecommendSeriesByChannelCreator() + setupRecommendSeriesByChannel() + setupEventBanner() + setupCuration() + } + + private fun setupContentBanner() { + val layoutParams = binding + .rvBanner + .layoutParams as LinearLayout.LayoutParams + + val pagerWidth = screenWidth.toDouble() - 26.7f.dpToPx() + val pagerHeight = (pagerWidth * 0.53).roundToInt() + layoutParams.width = pagerWidth.roundToInt() + layoutParams.height = pagerHeight + + contentBannerAdapter = AudioContentMainBannerAdapter( + requireContext(), + pagerWidth.roundToInt(), + pagerHeight + ) { + when (it.type) { + AudioContentBannerType.EVENT -> { + startActivity( + Intent(requireContext(), EventDetailActivity::class.java).apply { + putExtra(Constants.EXTRA_EVENT, it.eventItem!!) + } + ) + } + + AudioContentBannerType.CREATOR -> { + startActivity( + Intent(requireContext(), UserProfileActivity::class.java).apply { + putExtra(Constants.EXTRA_USER_ID, it.creatorId!!) + } + ) + } + + AudioContentBannerType.SERIES -> { + startActivity( + Intent(requireContext(), SeriesDetailActivity::class.java).apply { + putExtra(Constants.EXTRA_SERIES_ID, it.seriesId!!) + } + ) + } + + AudioContentBannerType.LINK -> { + startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(it.link!!))) + } + } + } + + binding + .rvBanner + .layoutParams = layoutParams + + binding.rvBanner.apply { + adapter = contentBannerAdapter as BaseBannerAdapter + + setLifecycleRegistry(lifecycle) + setScrollDuration(1000) + setInterval(4 * 1000) + }.create() + + binding + .rvBanner + .setIndicatorView(binding.indicatorBanner) + .setIndicatorStyle(IndicatorStyle.ROUND_RECT) + .setIndicatorSlideMode(IndicatorSlideMode.SMOOTH) + .setIndicatorVisibility(View.GONE) + .setIndicatorSliderColor( + ContextCompat.getColor(requireContext(), R.color.color_909090), + ContextCompat.getColor(requireContext(), R.color.color_3bb9f1) + ) + .setIndicatorSliderWidth(4f.dpToPx().toInt(), 10f.dpToPx().toInt()) + .setIndicatorHeight(4f.dpToPx().toInt()) + + viewModel.contentBannerLiveData.observe(viewLifecycleOwner) { + if (contentBannerAdapter.itemCount <= 0 && it.isEmpty()) { + binding.rvBanner.visibility = View.GONE + binding.indicatorBanner.visibility = View.GONE + } else { + binding.rvBanner.visibility = View.VISIBLE + binding.indicatorBanner.visibility = View.VISIBLE + binding.rvBanner.refreshData(it) + } + } + } + + private fun setupOriginalAudioDrama() { + audioDramaAdapter = AudioContentMainTabSeriesOriginalAudioDramaAdapter { + startActivity( + Intent(requireContext(), SeriesDetailActivity::class.java).apply { + putExtra(Constants.EXTRA_SERIES_ID, it) + } + ) + } + + val recyclerView = binding.rvOriginalAudio + recyclerView.layoutManager = LinearLayoutManager( + requireContext(), + LinearLayoutManager.HORIZONTAL, + false + ) + + recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + + when (parent.getChildAdapterPosition(view)) { + 0 -> { + outRect.left = 0 + outRect.right = 6.7f.dpToPx().toInt() + } + + audioDramaAdapter.itemCount - 1 -> { + outRect.right = 0 + outRect.left = 6.7f.dpToPx().toInt() + } + + else -> { + outRect.left = 6.7f.dpToPx().toInt() + outRect.right = 6.7f.dpToPx().toInt() + } + } + } + }) + + recyclerView.adapter = audioDramaAdapter + + viewModel.originalAudioDramaLiveData.observe(viewLifecycleOwner) { + audioDramaAdapter.addItems(it) + binding.llOriginalAudioDrama.visibility = + if (audioDramaAdapter.isVisibleRecyclerView()) { + View.VISIBLE + } else { + View.GONE + } + } + + binding.ivOriginalAudioDramaAll.setOnClickListener {} + } + + private fun setupRankSeries() { + rankDailySeriesAdapter = AudioContentMainSeriesRankingAdapter { + startActivity( + Intent(requireContext(), SeriesDetailActivity::class.java).apply { + putExtra(Constants.EXTRA_SERIES_ID, it) + } + ) + } + + binding.rvRankSeries.layoutManager = GridLayoutManager( + context, + 3, + GridLayoutManager.HORIZONTAL, + false + ) + + binding.rvRankSeries.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + outRect.top = 13.3f.dpToPx().toInt() + outRect.bottom = 13.3f.dpToPx().toInt() + outRect.left = 13.3f.dpToPx().toInt() + outRect.right = 13.3f.dpToPx().toInt() + } + }) + + binding.rvRankSeries.adapter = rankDailySeriesAdapter + + viewModel.rankSeriesListLiveData.observe(viewLifecycleOwner) { + if (it.isNotEmpty()) { + binding.llRankSeries.visibility = View.VISIBLE + rankDailySeriesAdapter.addItems(it) + } else { + binding.llRankSeries.visibility = View.GONE + } + } + } + + private fun setupRecommendSeriesGenre() { + seriesGenreAdapter = AudioContentMainRecommendSeriesGenreAdapter { + } + + val recyclerView = binding.rvSeriesGenre + recyclerView.layoutManager = LinearLayoutManager( + requireContext(), + LinearLayoutManager.HORIZONTAL, + false + ) + + recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + + when (parent.getChildAdapterPosition(view)) { + 0 -> { + outRect.left = 0 + outRect.right = 4f.dpToPx().toInt() + } + + seriesGenreAdapter.itemCount - 1 -> { + outRect.left = 4f.dpToPx().toInt() + outRect.right = 0 + } + + else -> { + outRect.left = 4f.dpToPx().toInt() + outRect.right = 4f.dpToPx().toInt() + } + } + } + }) + + recyclerView.adapter = seriesGenreAdapter + + viewModel.genreListLiveData.observe(viewLifecycleOwner) { + seriesGenreAdapter.addItems(it) + } + } + + private fun setupRecommendSeriesByGenre() { + recommendSeriesByGenreAdapter = UserProfileSeriesListAdapter( + onClickItem = { + startActivity( + Intent(requireContext(), SeriesDetailActivity::class.java).apply { + putExtra(Constants.EXTRA_SERIES_ID, it) + } + ) + }, + onClickCreator = { + startActivity( + Intent(requireContext(), UserProfileActivity::class.java).apply { + putExtra(Constants.EXTRA_USER_ID, it) + } + ) + }, + isVisibleCreator = true + ) + + val recyclerView = binding.rvSeriesByGenre + recyclerView.layoutManager = LinearLayoutManager( + requireContext(), + LinearLayoutManager.HORIZONTAL, + false + ) + + recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + + when (parent.getChildAdapterPosition(view)) { + 0 -> { + outRect.left = 0 + outRect.right = 6.7f.dpToPx().toInt() + } + + recommendSeriesByGenreAdapter.itemCount - 1 -> { + outRect.right = 0 + outRect.left = 6.7f.dpToPx().toInt() + } + + else -> { + outRect.left = 6.7f.dpToPx().toInt() + outRect.right = 6.7f.dpToPx().toInt() + } + } + } + }) + + recyclerView.adapter = recommendSeriesByGenreAdapter + + viewModel.recommendSeriesListLiveData.observe(viewLifecycleOwner) { + recommendSeriesByGenreAdapter.addItems(it) + } + } + + private fun setupNewSeries() { + newSeriesAdapter = AudioContentMainNewSeriesAdapter( + onClickItem = { + startActivity( + Intent(requireContext(), SeriesDetailActivity::class.java).apply { + putExtra(Constants.EXTRA_SERIES_ID, it) + } + ) + }, + onClickCreator = { + startActivity( + Intent(requireContext(), UserProfileActivity::class.java).apply { + putExtra(Constants.EXTRA_USER_ID, it) + } + ) + } + ) + + val recyclerView = binding.rvNewSeries + recyclerView.layoutManager = LinearLayoutManager( + requireContext(), + LinearLayoutManager.HORIZONTAL, + false + ) + + recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + + when (parent.getChildAdapterPosition(view)) { + 0 -> { + outRect.left = 0 + outRect.right = 6.7f.dpToPx().toInt() + } + + recommendSeriesByGenreAdapter.itemCount - 1 -> { + outRect.right = 0 + outRect.left = 6.7f.dpToPx().toInt() + } + + else -> { + outRect.left = 6.7f.dpToPx().toInt() + outRect.right = 6.7f.dpToPx().toInt() + } + } + } + }) + + recyclerView.adapter = newSeriesAdapter + + viewModel.newSeriesListLiveData.observe(viewLifecycleOwner) { + newSeriesAdapter.addItems(it) + binding.llNewSeries.visibility = if (it.isNotEmpty()) { + View.VISIBLE + } else { + View.GONE + } + } + } + + private fun setupCompleteSeries() { + completedSeriesAdapter = UserProfileSeriesListAdapter( + onClickItem = { + startActivity( + Intent(requireContext(), SeriesDetailActivity::class.java).apply { + putExtra(Constants.EXTRA_SERIES_ID, it) + } + ) + }, + onClickCreator = { + startActivity( + Intent(requireContext(), UserProfileActivity::class.java).apply { + putExtra(Constants.EXTRA_USER_ID, it) + } + ) + }, + isVisibleCreator = true + ) + + val recyclerView = binding.rvRankSeries + recyclerView.layoutManager = LinearLayoutManager( + requireContext(), + LinearLayoutManager.HORIZONTAL, + false + ) + + recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + + when (parent.getChildAdapterPosition(view)) { + 0 -> { + outRect.left = 0 + outRect.right = 6.7f.dpToPx().toInt() + } + + completedSeriesAdapter.itemCount - 1 -> { + outRect.right = 0 + outRect.left = 6.7f.dpToPx().toInt() + } + + else -> { + outRect.left = 6.7f.dpToPx().toInt() + outRect.right = 6.7f.dpToPx().toInt() + } + } + } + }) + + recyclerView.adapter = completedSeriesAdapter + + viewModel.rankCompleteSeriesListLiveData.observe(viewLifecycleOwner) { + completedSeriesAdapter.addItems(it) + binding.llCompleteSeries.visibility = if ( + completedSeriesAdapter.itemCount <= 0 && it.isEmpty() + ) { + View.GONE + } else { + View.VISIBLE + } + } + } + + private fun setupRecommendSeriesByChannelCreator() { + recommendSeriesCreatorAdapter = ContentRankCreatorAdapter { + binding.llNoItems.visibility = View.VISIBLE + binding.rvRecommendSeriesByChannel.visibility = View.GONE + } + + val recyclerView = binding.rvRecommendSeriesChannel + recyclerView.layoutManager = LinearLayoutManager( + context, + LinearLayoutManager.HORIZONTAL, + false + ) + + recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + + when (parent.getChildAdapterPosition(view)) { + 0 -> { + outRect.left = 0 + outRect.right = 4f.dpToPx().toInt() + } + + recommendSeriesCreatorAdapter.itemCount - 1 -> { + outRect.left = 4f.dpToPx().toInt() + outRect.right = 0 + } + + else -> { + outRect.left = 4f.dpToPx().toInt() + outRect.right = 4f.dpToPx().toInt() + } + } + } + }) + + recyclerView.adapter = recommendSeriesCreatorAdapter + + viewModel.seriesRankCreatorListLiveData.observe(viewLifecycleOwner) { + recommendSeriesCreatorAdapter.addItems(it) + if (recommendSeriesCreatorAdapter.itemCount <= 0 && it.isEmpty()) { + binding.llRecommendSeriesByChannel.visibility = View.GONE + } else { + binding.llRecommendSeriesByChannel.visibility = View.VISIBLE + } + } + } + + private fun setupRecommendSeriesByChannel() { + recommendSeriesByChannelAdapter = UserProfileSeriesListAdapter( + onClickItem = { + startActivity( + Intent(requireContext(), SeriesDetailActivity::class.java).apply { + putExtra(Constants.EXTRA_SERIES_ID, it) + } + ) + }, + onClickCreator = { + startActivity( + Intent(requireContext(), UserProfileActivity::class.java).apply { + putExtra(Constants.EXTRA_USER_ID, it) + } + ) + }, + isVisibleCreator = true + ) + + val recyclerView = binding.rvRecommendSeriesByChannel + recyclerView.layoutManager = LinearLayoutManager( + requireContext(), + LinearLayoutManager.HORIZONTAL, + false + ) + + recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + + when (parent.getChildAdapterPosition(view)) { + 0 -> { + outRect.left = 0 + outRect.right = 6.7f.dpToPx().toInt() + } + + recommendSeriesByChannelAdapter.itemCount - 1 -> { + outRect.right = 0 + outRect.left = 6.7f.dpToPx().toInt() + } + + else -> { + outRect.left = 6.7f.dpToPx().toInt() + outRect.right = 6.7f.dpToPx().toInt() + } + } + } + }) + + recyclerView.adapter = recommendSeriesByChannelAdapter + + viewModel.recommendSeriesByChannelLiveData.observe(viewLifecycleOwner) { + recommendSeriesByChannelAdapter.addItems(it) + if (recommendSeriesCreatorAdapter.itemCount <= 0) { + binding.llNoItems.visibility = View.VISIBLE + binding.rvRecommendSeriesByChannel.visibility = View.GONE + } else { + binding.llNoItems.visibility = View.GONE + binding.rvRecommendSeriesByChannel.visibility = View.VISIBLE + } + } + } + + private fun setupEventBanner() { + val imageSliderLp = binding.eventBannerSlider.layoutParams + imageSliderLp.width = screenWidth + imageSliderLp.height = (screenWidth * 300) / 1000 + binding.eventBannerSlider.layoutParams = imageSliderLp + + binding.eventBannerSlider.apply { + adapter = EventBannerAdapter(requireContext()) { + if (it.detailImageUrl != null) { + val intent = Intent(requireActivity(), EventDetailActivity::class.java) + intent.putExtra(Constants.EXTRA_EVENT, it) + startActivity(intent) + } else if (!it.link.isNullOrBlank()) { + startActivity( + Intent( + Intent.ACTION_VIEW, + Uri.parse(it.link) + ) + ) + } + } as BaseBannerAdapter + setLifecycleRegistry(lifecycle) + setScrollDuration(800) + }.create() + + binding.eventBannerSlider + .setIndicatorView(binding.indicatorEventBanner) + .setIndicatorStyle(IndicatorStyle.ROUND_RECT) + .setIndicatorSlideMode(IndicatorSlideMode.SMOOTH) + .setIndicatorVisibility(View.GONE) + .setIndicatorSliderColor( + ContextCompat.getColor(requireContext(), R.color.color_909090), + ContextCompat.getColor(requireContext(), R.color.color_3bb9f1) + ) + .setIndicatorSliderWidth(4f.dpToPx().toInt(), 10f.dpToPx().toInt()) + .setIndicatorHeight(4f.dpToPx().toInt()) + + viewModel.eventLiveData.observe(viewLifecycleOwner) { + if (it.isNotEmpty()) { + binding.eventBannerSlider.visibility = View.VISIBLE + binding.indicatorEventBanner.visibility = View.VISIBLE + binding.eventBannerSlider.refreshData(it) + } else { + binding.eventBannerSlider.visibility = View.GONE + binding.indicatorEventBanner.visibility = View.GONE + } + } + } + + private fun setupCuration() { + curationAdapter = AudioContentMainSeriesCurationAdapter( + onClickItem = { + startActivity( + Intent(requireContext(), SeriesDetailActivity::class.java).apply { + putExtra(Constants.EXTRA_SERIES_ID, it) + } + ) + }, + onClickCreator = { + startActivity( + Intent(requireContext(), UserProfileActivity::class.java).apply { + putExtra(Constants.EXTRA_USER_ID, it) + } + ) + }, + ) + + binding.rvCuration.layoutManager = LinearLayoutManager( + context, + LinearLayoutManager.VERTICAL, + false + ) + + binding.rvCuration.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + + when (parent.getChildAdapterPosition(view)) { + 0 -> { + outRect.top = 30f.dpToPx().toInt() + outRect.bottom = 15f.dpToPx().toInt() + } + + curationAdapter.itemCount - 1 -> { + outRect.top = 15f.dpToPx().toInt() + outRect.bottom = 30f.dpToPx().toInt() + } + + else -> { + outRect.top = 15f.dpToPx().toInt() + outRect.bottom = 15f.dpToPx().toInt() + } + } + } + }) + + binding.rvCuration.adapter = curationAdapter + + viewModel.curationListLiveData.observe(viewLifecycleOwner) { + curationAdapter.addItems(it) + + binding.rvCuration.visibility = if (curationAdapter.itemCount <= 0 && it.isEmpty()) { + View.GONE + } else { + View.VISIBLE + } + } + } + + private fun bindData() { + viewModel.toastLiveData.observe(viewLifecycleOwner) { + it?.let { Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show() } + } + + viewModel.isLoading.observe(viewLifecycleOwner) { + if (it) { + loadingDialog.show(screenWidth) + } else { + loadingDialog.dismiss() + } + } + } } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/AudioContentMainTabSeriesRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/AudioContentMainTabSeriesRepository.kt new file mode 100644 index 0000000..b40f558 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/AudioContentMainTabSeriesRepository.kt @@ -0,0 +1,7 @@ +package kr.co.vividnext.sodalive.audio_content.main.v2.series + +import kr.co.vividnext.sodalive.audio_content.AudioContentApi + +class AudioContentMainTabSeriesRepository(private val api: AudioContentApi) { + fun getContentMainSeries(token: String) = api.getContentMainSeries(authHeader = token) +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/AudioContentMainTabSeriesViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/AudioContentMainTabSeriesViewModel.kt new file mode 100644 index 0000000..b058bb2 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/AudioContentMainTabSeriesViewModel.kt @@ -0,0 +1,120 @@ +package kr.co.vividnext.sodalive.audio_content.main.v2.series + +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.audio_content.main.ContentCreatorResponse +import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentBannerResponse +import kr.co.vividnext.sodalive.audio_content.series.GetSeriesListResponse +import kr.co.vividnext.sodalive.base.BaseViewModel +import kr.co.vividnext.sodalive.common.SharedPreferenceManager +import kr.co.vividnext.sodalive.settings.event.EventItem + +class AudioContentMainTabSeriesViewModel( + private val repository: AudioContentMainTabSeriesRepository +) : BaseViewModel() { + private val _toastLiveData = MutableLiveData() + val toastLiveData: LiveData + get() = _toastLiveData + + private var _isLoading = MutableLiveData(false) + val isLoading: LiveData + get() = _isLoading + + private var _contentBannerLiveData = MutableLiveData>() + val contentBannerLiveData: LiveData> + get() = _contentBannerLiveData + + private var _originalAudioDramaLiveData = + MutableLiveData>() + val originalAudioDramaLiveData: LiveData> + get() = _originalAudioDramaLiveData + + private var _rankSeriesListLiveData = + MutableLiveData>() + val rankSeriesListLiveData: LiveData> + get() = _rankSeriesListLiveData + + private var _genreListLiveData = MutableLiveData>() + val genreListLiveData: LiveData> + get() = _genreListLiveData + + private var _recommendSeriesListLiveData = + MutableLiveData>() + val recommendSeriesListLiveData: LiveData> + get() = _recommendSeriesListLiveData + + private var _newSeriesListLiveData = MutableLiveData>() + val newSeriesListLiveData: LiveData> + get() = _newSeriesListLiveData + + private var _rankCompleteSeriesListLiveData = + MutableLiveData>() + val rankCompleteSeriesListLiveData: LiveData> + get() = _rankCompleteSeriesListLiveData + + private var _seriesRankCreatorListLiveData = MutableLiveData>() + val seriesRankCreatorListLiveData: LiveData> + get() = _seriesRankCreatorListLiveData + + private var _recommendSeriesByChannelLiveData = + MutableLiveData>() + val recommendSeriesByChannelLiveData: LiveData> + get() = _recommendSeriesByChannelLiveData + + private val _eventLiveData = MutableLiveData>() + val eventLiveData: LiveData> + get() = _eventLiveData + + private val _curationListLiveData = MutableLiveData>() + val curationListLiveData: LiveData> + get() = _curationListLiveData + + fun fetchData() { + _isLoading.value = true + compositeDisposable.add( + repository.getContentMainSeries(token = "Bearer ${SharedPreferenceManager.token}") + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + if (it.success && it.data != null) { + val data = it.data + + Logger.e("data: $data") + + _contentBannerLiveData.value = data.contentBannerList + _originalAudioDramaLiveData.value = data.originalAudioDrama + _rankSeriesListLiveData.value = data.rankSeriesList + _genreListLiveData.value = data.genreList + _recommendSeriesListLiveData.value = data.recommendSeriesList + _newSeriesListLiveData.value = data.newSeriesList + _rankCompleteSeriesListLiveData.value = data.rankCompleteSeriesList + _seriesRankCreatorListLiveData.value = data.seriesRankCreatorList + _recommendSeriesByChannelLiveData.value = data.recommendSeriesByChannel + _eventLiveData.value = data.eventBannerList.eventList + _curationListLiveData.value = data.curationList + + } else { + if (it.message != null) { + _toastLiveData.postValue(it.message) + } else { + _toastLiveData.postValue( + "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." + ) + } + } + + _isLoading.value = false + }, + { + _isLoading.value = false + it.message?.let { message -> Logger.e(message) } + _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.") + } + ) + ) + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/GetContentMainTabSeriesResponse.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/GetContentMainTabSeriesResponse.kt new file mode 100644 index 0000000..34c7379 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/GetContentMainTabSeriesResponse.kt @@ -0,0 +1,34 @@ +package kr.co.vividnext.sodalive.audio_content.main.v2.series + +import androidx.annotation.Keep +import com.google.gson.annotations.SerializedName +import kr.co.vividnext.sodalive.audio_content.main.ContentCreatorResponse +import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentBannerResponse +import kr.co.vividnext.sodalive.audio_content.series.GetSeriesListResponse +import kr.co.vividnext.sodalive.settings.event.GetEventResponse + +@Keep +data class GetContentMainTabSeriesResponse( + @SerializedName("contentBannerList") + val contentBannerList: List, + @SerializedName("originalAudioDrama") + val originalAudioDrama: List, + @SerializedName("rankSeriesList") + val rankSeriesList: List, + @SerializedName("genreList") + val genreList: List, + @SerializedName("recommendSeriesList") + val recommendSeriesList: List, + @SerializedName("newSeriesList") + val newSeriesList: List, + @SerializedName("rankCompleteSeriesList") + val rankCompleteSeriesList: List, + @SerializedName("seriesRankCreatorList") + val seriesRankCreatorList: List, + @SerializedName("recommendSeriesByChannel") + val recommendSeriesByChannel: List, + @SerializedName("eventBannerList") + val eventBannerList: GetEventResponse, + @SerializedName("curationList") + val curationList: List +) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/GetRecommendSeriesListResponse.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/GetRecommendSeriesListResponse.kt new file mode 100644 index 0000000..8782c76 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/GetRecommendSeriesListResponse.kt @@ -0,0 +1,14 @@ +package kr.co.vividnext.sodalive.audio_content.main.v2.series + +import androidx.annotation.Keep +import com.google.gson.annotations.SerializedName + +@Keep +data class GetRecommendSeriesListResponse( + @SerializedName("seriesId") val seriesId: Long, + @SerializedName("title") val title: String, + @SerializedName("imageUrl") val imageUrl: String, + @SerializedName("creatorId") val creatorId: Long, + @SerializedName("creatorNickname") val creatorNickname: String, + @SerializedName("creatorProfileImageUrl") val creatorProfileImageUrl: String +) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/GetSeriesCurationResponse.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/GetSeriesCurationResponse.kt new file mode 100644 index 0000000..f2ca6be --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/GetSeriesCurationResponse.kt @@ -0,0 +1,11 @@ +package kr.co.vividnext.sodalive.audio_content.main.v2.series + +import androidx.annotation.Keep +import com.google.gson.annotations.SerializedName +import kr.co.vividnext.sodalive.audio_content.series.GetSeriesListResponse + +@Keep +data class GetSeriesCurationResponse( + @SerializedName("title") val title: String, + @SerializedName("items") val items: List +) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/GetSeriesGenreListResponse.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/GetSeriesGenreListResponse.kt new file mode 100644 index 0000000..e44db2c --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/GetSeriesGenreListResponse.kt @@ -0,0 +1,10 @@ +package kr.co.vividnext.sodalive.audio_content.main.v2.series + +import androidx.annotation.Keep +import com.google.gson.annotations.SerializedName + +@Keep +data class GetSeriesGenreListResponse( + @SerializedName("id") val id: Long, + @SerializedName("genre") val genre: String +) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/curation/AudioContentMainSeriesCurationAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/curation/AudioContentMainSeriesCurationAdapter.kt new file mode 100644 index 0000000..2580e10 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/curation/AudioContentMainSeriesCurationAdapter.kt @@ -0,0 +1,105 @@ +package kr.co.vividnext.sodalive.audio_content.main.v2.series.curation + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Rect +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import kr.co.vividnext.sodalive.audio_content.main.v2.series.GetSeriesCurationResponse +import kr.co.vividnext.sodalive.audio_content.series.GetSeriesListResponse +import kr.co.vividnext.sodalive.databinding.ItemAudioContentMainCurationBinding +import kr.co.vividnext.sodalive.explorer.profile.series.UserProfileSeriesListAdapter +import kr.co.vividnext.sodalive.extensions.dpToPx + +class AudioContentMainSeriesCurationAdapter( + private val onClickItem: (Long) -> Unit, + private val onClickCreator: (Long) -> Unit +) : RecyclerView.Adapter() { + + private val items = mutableListOf() + + inner class ViewHolder( + private val context: Context, + private val binding: ItemAudioContentMainCurationBinding + ) : RecyclerView.ViewHolder(binding.root) { + fun bind(item: GetSeriesCurationResponse) { + binding.tvDesc.visibility = View.GONE + binding.ivAll.visibility = View.GONE + + binding.tvTitle.text = item.title + setSeriesList(item.items) + } + + private fun setSeriesList(items: List) { + val adapter = UserProfileSeriesListAdapter( + onClickItem = onClickItem, + onClickCreator = onClickCreator, + isVisibleCreator = true + ) + + binding.rvCuration.layoutManager = LinearLayoutManager( + context, + LinearLayoutManager.HORIZONTAL, + false + ) + + if (binding.rvCuration.itemDecorationCount == 0) { + binding.rvCuration.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + + when (parent.getChildAdapterPosition(view)) { + 0 -> { + outRect.left = 0 + outRect.right = 6.7f.dpToPx().toInt() + } + + adapter.itemCount - 1 -> { + outRect.left = 6.7f.dpToPx().toInt() + outRect.right = 0 + } + + else -> { + outRect.left = 6.7f.dpToPx().toInt() + outRect.right = 6.7f.dpToPx().toInt() + } + } + } + }) + } + + binding.rvCuration.adapter = adapter + adapter.addItems(items) + } + } + + @SuppressLint("NotifyDataSetChanged") + fun addItems(items: List) { + this.items.clear() + this.items.addAll(items) + notifyDataSetChanged() + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( + parent.context, + ItemAudioContentMainCurationBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.bind(items[position]) + } + + override fun getItemCount() = items.size +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/new_series/AudioContentMainNewSeriesAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/new_series/AudioContentMainNewSeriesAdapter.kt new file mode 100644 index 0000000..c94e0b7 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/new_series/AudioContentMainNewSeriesAdapter.kt @@ -0,0 +1,77 @@ +package kr.co.vividnext.sodalive.audio_content.main.v2.series.new_series + +import android.annotation.SuppressLint +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import coil.load +import coil.transform.CircleCropTransformation +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestOptions +import kr.co.vividnext.sodalive.R +import kr.co.vividnext.sodalive.audio_content.main.v2.series.GetRecommendSeriesListResponse +import kr.co.vividnext.sodalive.databinding.ItemAudioContentMainNewSeriesBinding +import kr.co.vividnext.sodalive.extensions.dpToPx + +class AudioContentMainNewSeriesAdapter( + private val onClickItem: (Long) -> Unit, + private val onClickCreator: (Long) -> Unit +) : RecyclerView.Adapter() { + + private val items = mutableListOf() + + inner class ViewHolder( + private val context: Context, + private val binding: ItemAudioContentMainNewSeriesBinding + ) : RecyclerView.ViewHolder(binding.root) { + fun bind(item: GetRecommendSeriesListResponse) { + Glide + .with(context) + .load(item.imageUrl) + .apply( + RequestOptions().transform( + CenterCrop(), + RoundedCorners(5f.dpToPx().toInt()) + ) + ) + .into(binding.ivCover) + + binding.ivCreatorProfile.load(item.creatorProfileImageUrl) { + crossfade(true) + placeholder(R.drawable.ic_place_holder) + transformations(CircleCropTransformation()) + } + + binding.tvTitle.text = item.title + binding.tvCreatorNickname.text = item.creatorNickname + + binding.root.setOnClickListener { onClickItem(item.seriesId) } + binding.tvTitle.setOnClickListener { onClickCreator(item.creatorId) } + binding.tvCreatorNickname.setOnClickListener { onClickCreator(item.creatorId) } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( + parent.context, + ItemAudioContentMainNewSeriesBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + + override fun getItemCount() = items.size + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.bind(items[position]) + } + + @SuppressLint("NotifyDataSetChanged") + fun addItems(items: List) { + this.items.addAll(items) + notifyDataSetChanged() + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/origianl_audio_drama/AudioContentMainTabSeriesOriginalAudioDramaAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/origianl_audio_drama/AudioContentMainTabSeriesOriginalAudioDramaAdapter.kt new file mode 100644 index 0000000..9ffc031 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/origianl_audio_drama/AudioContentMainTabSeriesOriginalAudioDramaAdapter.kt @@ -0,0 +1,89 @@ +package kr.co.vividnext.sodalive.audio_content.main.v2.series.origianl_audio_drama + +import android.annotation.SuppressLint +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.bumptech.glide.Glide +import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestOptions +import kr.co.vividnext.sodalive.audio_content.series.GetSeriesListResponse +import kr.co.vividnext.sodalive.databinding.ItemSeriesOriginalAudioDramaBinding +import kr.co.vividnext.sodalive.extensions.dpToPx + +class AudioContentMainTabSeriesOriginalAudioDramaAdapter( + private val onClickItem: (Long) -> Unit +) : RecyclerView.Adapter() { + + val items = mutableListOf() + + inner class ViewHolder( + private val context: Context, + private val binding: ItemSeriesOriginalAudioDramaBinding + ) : RecyclerView.ViewHolder(binding.root) { + @SuppressLint("SetTextI18n") + fun bind(item: GetSeriesListResponse.SeriesListItem) { + Glide + .with(context) + .load(item.coverImage) + .apply( + RequestOptions().transform( + CenterCrop(), + RoundedCorners(5f.dpToPx().toInt()) + ) + ) + .into(binding.ivCover) + + binding.tvTitle.text = item.title + binding.tvSeriesContentCount.text = "총 ${item.numberOfContent}화" + binding.tvNew.visibility = if (item.isNew) { + View.VISIBLE + } else { + View.GONE + } + + binding.tvPopular.visibility = if (item.isPopular) { + View.VISIBLE + } else { + View.GONE + } + + if (item.isComplete) { + binding.tvNew.visibility = View.GONE + binding.tvComplete.visibility = View.VISIBLE + } else { + binding.tvComplete.visibility = View.GONE + } + + binding.root.setOnClickListener { onClickItem(item.seriesId) } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( + parent.context, + ItemSeriesOriginalAudioDramaBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.bind(items[position]) + } + + override fun getItemCount() = items.count() + + @SuppressLint("NotifyDataSetChanged") + fun addItems(items: List) { + this.items.addAll(items) + notifyDataSetChanged() + } + + fun isVisibleRecyclerView(): Boolean { + return items.isNotEmpty() + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/rank_series/AudioContentMainSeriesRankingAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/rank_series/AudioContentMainSeriesRankingAdapter.kt new file mode 100644 index 0000000..cbe534d --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/rank_series/AudioContentMainSeriesRankingAdapter.kt @@ -0,0 +1,57 @@ +package kr.co.vividnext.sodalive.audio_content.main.v2.series.rank_series + +import android.annotation.SuppressLint +import android.view.LayoutInflater +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.audio_content.series.GetSeriesListResponse +import kr.co.vividnext.sodalive.databinding.ItemAudioContentMainSeriesRankingBinding +import kr.co.vividnext.sodalive.extensions.dpToPx + +class AudioContentMainSeriesRankingAdapter( + private val onClickItem: (Long) -> Unit +) : RecyclerView.Adapter() { + + val items = mutableListOf() + + inner class ViewHolder( + private val binding: ItemAudioContentMainSeriesRankingBinding + ) : RecyclerView.ViewHolder(binding.root) { + @SuppressLint("SetTextI18n") + fun bind(item: GetSeriesListResponse.SeriesListItem, index: Int) { + binding.root.setOnClickListener { onClickItem(item.seriesId) } + binding.tvTitle.text = item.title + binding.tvRank.text = "${index + 1}" + binding.tvNickname.text = item.creator.nickname + + binding.ivCover.load(item.coverImage) { + crossfade(true) + placeholder(R.drawable.bg_placeholder) + transformations(RoundedCornersTransformation(5f.dpToPx())) + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( + ItemAudioContentMainSeriesRankingBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + + override fun getItemCount() = items.size + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.bind(items[position], index = position) + } + + @SuppressLint("NotifyDataSetChanged") + fun addItems(items: List) { + this.items.addAll(items) + notifyDataSetChanged() + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/recommend_by_genre/AudioContentMainRecommendSeriesGenreAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/recommend_by_genre/AudioContentMainRecommendSeriesGenreAdapter.kt new file mode 100644 index 0000000..058f6ca --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/recommend_by_genre/AudioContentMainRecommendSeriesGenreAdapter.kt @@ -0,0 +1,74 @@ +package kr.co.vividnext.sodalive.audio_content.main.v2.series.recommend_by_genre + +import android.annotation.SuppressLint +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.RecyclerView +import com.orhanobut.logger.Logger +import kr.co.vividnext.sodalive.R +import kr.co.vividnext.sodalive.audio_content.main.v2.series.GetSeriesGenreListResponse +import kr.co.vividnext.sodalive.databinding.ItemAudioContentMainNewContentThemeBinding + +class AudioContentMainRecommendSeriesGenreAdapter( + private val onClickItem: (Long) -> Unit +) : RecyclerView.Adapter() { + + private var items = mutableListOf() + private var selectedGenreId = 0L + + inner class ViewHolder( + private val context: Context, + private val binding: ItemAudioContentMainNewContentThemeBinding + ) : RecyclerView.ViewHolder(binding.root) { + @SuppressLint("NotifyDataSetChanged") + fun bind(item: GetSeriesGenreListResponse) { + if (item.id == selectedGenreId) { + binding.tvTheme.setBackgroundResource( + R.drawable.bg_round_corner_16_7_transparent_3bb9f1 + ) + binding.tvTheme.setTextColor(ContextCompat.getColor(context, R.color.color_3bb9f1)) + } else { + binding.tvTheme.setBackgroundResource( + R.drawable.bg_round_corner_16_7_transparent_777777 + ) + binding.tvTheme.setTextColor(ContextCompat.getColor(context, R.color.color_777777)) + } + + binding.tvTheme.text = item.genre + binding.root.setOnClickListener { + onClickItem(item.id) + selectedGenreId = item.id + notifyDataSetChanged() + } + } + } + + @SuppressLint("NotifyDataSetChanged") + fun addItems(items: List) { + this.selectedGenreId = if (items.isNotEmpty()) { + items[0].id + } else { + 0 + } + this.items.clear() + this.items.addAll(items) + notifyDataSetChanged() + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( + parent.context, + ItemAudioContentMainNewContentThemeBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + + override fun getItemCount() = items.size + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.bind(items[position]) + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audition/role/AuditionRoleDetailActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/audition/role/AuditionRoleDetailActivity.kt index d69b61b..8785edc 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/audition/role/AuditionRoleDetailActivity.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/audition/role/AuditionRoleDetailActivity.kt @@ -12,8 +12,6 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.core.content.ContextCompat import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import coil.load -import coil.transform.RoundedCornersTransformation import com.bumptech.glide.Glide import com.bumptech.glide.load.MultiTransformation import com.bumptech.glide.load.resource.bitmap.CenterCrop 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 6f7b3ce..065b645 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 @@ -25,6 +25,8 @@ 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.main.v2.home.AudioContentMainTabHomeRepository import kr.co.vividnext.sodalive.audio_content.main.v2.home.AudioContentMainTabHomeViewModel +import kr.co.vividnext.sodalive.audio_content.main.v2.series.AudioContentMainTabSeriesRepository +import kr.co.vividnext.sodalive.audio_content.main.v2.series.AudioContentMainTabSeriesViewModel 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.player.AudioContentGenerateUrlRepository @@ -293,6 +295,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) { viewModel { AuditionRoleDetailViewModel(get()) } viewModel { AudioContentMainCreatorRankingViewModel(get()) } viewModel { AudioContentMainTabHomeViewModel(get(), get()) } + viewModel { AudioContentMainTabSeriesViewModel(get()) } } private val repositoryModule = module { @@ -325,6 +328,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) { factory { AudioContentGenerateUrlRepository(get()) } factory { AuditionRepository(get()) } factory { AudioContentMainTabHomeRepository(get()) } + factory { AudioContentMainTabSeriesRepository(get()) } } private val moduleList = listOf( diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/now/LiveNowAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/now/LiveNowAdapter.kt index a70fc58..4a140f5 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/live/now/LiveNowAdapter.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/live/now/LiveNowAdapter.kt @@ -2,8 +2,6 @@ package kr.co.vividnext.sodalive.live.now import android.annotation.SuppressLint import android.content.Context -import android.graphics.Bitmap -import android.graphics.drawable.Drawable import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -14,8 +12,6 @@ import com.bumptech.glide.Glide import com.bumptech.glide.load.resource.bitmap.CenterCrop import com.bumptech.glide.load.resource.bitmap.RoundedCorners import com.bumptech.glide.request.RequestOptions -import com.bumptech.glide.request.target.CustomTarget -import com.bumptech.glide.request.transition.Transition import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.databinding.ItemLiveNowBinding import kr.co.vividnext.sodalive.extensions.dpToPx diff --git a/app/src/main/res/layout/fragment_audio_content_main_tab_home.xml b/app/src/main/res/layout/fragment_audio_content_main_tab_home.xml index 52e7942..c5bdcda 100644 --- a/app/src/main/res/layout/fragment_audio_content_main_tab_home.xml +++ b/app/src/main/res/layout/fragment_audio_content_main_tab_home.xml @@ -491,7 +491,7 @@ android:layout_height="wrap_content" android:layout_marginTop="13.3dp" android:clipToPadding="false" - android:paddingHorizontal="6.7dp" /> + android:paddingHorizontal="13.3dp" /> + android:layout_height="match_parent" + tools:background="@color/black"> + android:layout_marginHorizontal="13.3dp" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_audio_content_main_new_series.xml b/app/src/main/res/layout/item_audio_content_main_new_series.xml new file mode 100644 index 0000000..c795027 --- /dev/null +++ b/app/src/main/res/layout/item_audio_content_main_new_series.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_audio_content_main_series_ranking.xml b/app/src/main/res/layout/item_audio_content_main_series_ranking.xml new file mode 100644 index 0000000..3286205 --- /dev/null +++ b/app/src/main/res/layout/item_audio_content_main_series_ranking.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_series_original_audio_drama.xml b/app/src/main/res/layout/item_series_original_audio_drama.xml new file mode 100644 index 0000000..c237fa8 --- /dev/null +++ b/app/src/main/res/layout/item_series_original_audio_drama.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + +