콘텐츠 메인 무료 탭
- 채널별 추천 무료 콘텐츠 UI 추가
This commit is contained in:
		@@ -368,4 +368,10 @@ interface AudioContentApi {
 | 
			
		||||
        @Query("size") size: Int,
 | 
			
		||||
        @Header("Authorization") authHeader: String
 | 
			
		||||
    ): Single<ApiResponse<List<GetAudioContentMainItem>>>
 | 
			
		||||
 | 
			
		||||
    @GET("/v2/audio-content/main/free/popular-content-by-creator")
 | 
			
		||||
    fun getPopularFreeContentByCreator(
 | 
			
		||||
        @Query("creatorId") creatorId: Long,
 | 
			
		||||
        @Header("Authorization") authHeader: String
 | 
			
		||||
    ): Single<ApiResponse<List<GetAudioContentRankingItem>>>
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ import android.widget.Toast
 | 
			
		||||
import androidx.annotation.OptIn
 | 
			
		||||
import androidx.core.content.ContextCompat
 | 
			
		||||
import androidx.media3.common.util.UnstableApi
 | 
			
		||||
import androidx.recyclerview.widget.GridLayoutManager
 | 
			
		||||
import androidx.recyclerview.widget.LinearLayoutManager
 | 
			
		||||
import androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
import com.zhpan.bannerview.BaseBannerAdapter
 | 
			
		||||
@@ -23,11 +24,14 @@ import kr.co.vividnext.sodalive.audio_content.main.AudioContentMainContentAdapte
 | 
			
		||||
import kr.co.vividnext.sodalive.audio_content.main.banner.AudioContentMainBannerAdapter
 | 
			
		||||
import kr.co.vividnext.sodalive.audio_content.main.new_content.AudioContentMainNewContentThemeAdapter
 | 
			
		||||
import kr.co.vividnext.sodalive.audio_content.main.v2.AudioContentMainContentCurationAdapter
 | 
			
		||||
import kr.co.vividnext.sodalive.audio_content.main.v2.ContentRankCreatorAdapter
 | 
			
		||||
import kr.co.vividnext.sodalive.audio_content.main.v2.PopularContentByCreatorAdapter
 | 
			
		||||
import kr.co.vividnext.sodalive.audio_content.main.v2.free.introduce_creator.IntroduceCreatorActivity
 | 
			
		||||
import kr.co.vividnext.sodalive.audio_content.main.v2.series.new_series.AudioContentMainNewSeriesAdapter
 | 
			
		||||
import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailActivity
 | 
			
		||||
import kr.co.vividnext.sodalive.base.BaseFragment
 | 
			
		||||
import kr.co.vividnext.sodalive.common.Constants
 | 
			
		||||
import kr.co.vividnext.sodalive.common.GridSpacingItemDecoration
 | 
			
		||||
import kr.co.vividnext.sodalive.common.LoadingDialog
 | 
			
		||||
import kr.co.vividnext.sodalive.databinding.FragmentAudioContentMainTabFreeBinding
 | 
			
		||||
import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
 | 
			
		||||
@@ -49,6 +53,8 @@ class AudioContentMainTabFreeFragment : BaseFragment<FragmentAudioContentMainTab
 | 
			
		||||
    private lateinit var newContentThemeAdapter: AudioContentMainNewContentThemeAdapter
 | 
			
		||||
    private lateinit var newContentAdapter: AudioContentMainContentAdapter
 | 
			
		||||
    private lateinit var curationAdapter: AudioContentMainContentCurationAdapter
 | 
			
		||||
    private lateinit var contentRankCreatorAdapter: ContentRankCreatorAdapter
 | 
			
		||||
    private lateinit var popularContentByCreatorAdapter: PopularContentByCreatorAdapter
 | 
			
		||||
 | 
			
		||||
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
 | 
			
		||||
        super.onViewCreated(view, savedInstanceState)
 | 
			
		||||
@@ -66,6 +72,8 @@ class AudioContentMainTabFreeFragment : BaseFragment<FragmentAudioContentMainTab
 | 
			
		||||
        setupRecommendSeries()
 | 
			
		||||
        setupNewContentTheme()
 | 
			
		||||
        setupNewContent()
 | 
			
		||||
        setupPopularContentCreator()
 | 
			
		||||
        setupPopularContentByCreator()
 | 
			
		||||
        setupCuration()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -281,7 +289,7 @@ class AudioContentMainTabFreeFragment : BaseFragment<FragmentAudioContentMainTab
 | 
			
		||||
 | 
			
		||||
        viewModel.recommendSeriesListLiveData.observe(viewLifecycleOwner) {
 | 
			
		||||
            recommendSeriesAdapter.addItems(it)
 | 
			
		||||
            recyclerView.visibility = if (it.isNotEmpty()) {
 | 
			
		||||
            binding.llRecommendSeries.visibility = if (it.isNotEmpty()) {
 | 
			
		||||
                View.VISIBLE
 | 
			
		||||
            } else {
 | 
			
		||||
                View.GONE
 | 
			
		||||
@@ -406,6 +414,102 @@ class AudioContentMainTabFreeFragment : BaseFragment<FragmentAudioContentMainTab
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun setupPopularContentCreator() {
 | 
			
		||||
        contentRankCreatorAdapter = ContentRankCreatorAdapter {
 | 
			
		||||
            binding.llNoItems.visibility = View.VISIBLE
 | 
			
		||||
            binding.rvRankingPlayCount.visibility = View.GONE
 | 
			
		||||
            viewModel.getPopularContentByCreator(it)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        binding.rvRankingCreator.layoutManager = LinearLayoutManager(
 | 
			
		||||
            context,
 | 
			
		||||
            LinearLayoutManager.HORIZONTAL,
 | 
			
		||||
            false
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        binding.rvRankingCreator.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 = 11f.dpToPx().toInt()
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    contentRankCreatorAdapter.itemCount - 1 -> {
 | 
			
		||||
                        outRect.left = 11f.dpToPx().toInt()
 | 
			
		||||
                        outRect.right = 0
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    else -> {
 | 
			
		||||
                        outRect.left = 11f.dpToPx().toInt()
 | 
			
		||||
                        outRect.right = 11f.dpToPx().toInt()
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        binding.rvRankingCreator.adapter = contentRankCreatorAdapter
 | 
			
		||||
 | 
			
		||||
        viewModel.contentCreatorListLiveData.observe(viewLifecycleOwner) {
 | 
			
		||||
            contentRankCreatorAdapter.addItems(it)
 | 
			
		||||
            if (contentRankCreatorAdapter.itemCount <= 0 && it.isEmpty()) {
 | 
			
		||||
                binding.llCreatorContentRanking.visibility = View.GONE
 | 
			
		||||
            } else {
 | 
			
		||||
                binding.llCreatorContentRanking.visibility = View.VISIBLE
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun setupPopularContentByCreator() {
 | 
			
		||||
        popularContentByCreatorAdapter = PopularContentByCreatorAdapter(
 | 
			
		||||
            itemWidth = ((screenWidth - 13.3f.dpToPx() * 3) / 2).toInt(),
 | 
			
		||||
            onClickItem = { contentId ->
 | 
			
		||||
                startActivity(
 | 
			
		||||
                    Intent(requireActivity(), AudioContentDetailActivity::class.java).apply {
 | 
			
		||||
                        putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, contentId)
 | 
			
		||||
                    }
 | 
			
		||||
                )
 | 
			
		||||
            },
 | 
			
		||||
            onClickCreator = { creatorId ->
 | 
			
		||||
                startActivity(
 | 
			
		||||
                    Intent(requireActivity(), UserProfileActivity::class.java).apply {
 | 
			
		||||
                        putExtra(Constants.EXTRA_USER_ID, creatorId)
 | 
			
		||||
                    }
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        val recyclerView = binding.rvRankingPlayCount
 | 
			
		||||
        recyclerView.layoutManager = GridLayoutManager(requireContext(), 2)
 | 
			
		||||
        recyclerView.addItemDecoration(
 | 
			
		||||
            GridSpacingItemDecoration(
 | 
			
		||||
                2,
 | 
			
		||||
                13.3f.dpToPx().toInt(),
 | 
			
		||||
                false
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        recyclerView.adapter = popularContentByCreatorAdapter
 | 
			
		||||
 | 
			
		||||
        viewModel.playCountRankContentListLiveData.observe(viewLifecycleOwner) {
 | 
			
		||||
            if (it.isNotEmpty()) {
 | 
			
		||||
                binding.llNoItems.visibility = View.GONE
 | 
			
		||||
                recyclerView.visibility = View.VISIBLE
 | 
			
		||||
                popularContentByCreatorAdapter.addItems(it)
 | 
			
		||||
            } else {
 | 
			
		||||
                binding.llNoItems.visibility = View.VISIBLE
 | 
			
		||||
                recyclerView.visibility = View.GONE
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun setupCuration() {
 | 
			
		||||
        curationAdapter = AudioContentMainContentCurationAdapter(
 | 
			
		||||
            onClickItem = {
 | 
			
		||||
 
 | 
			
		||||
@@ -21,4 +21,9 @@ class AudioContentMainTabFreeRepository(private val api: AudioContentApi) {
 | 
			
		||||
        size = size,
 | 
			
		||||
        authHeader = token
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    fun getPopularContentByCreator(
 | 
			
		||||
        creatorId: Long,
 | 
			
		||||
        token: String
 | 
			
		||||
    ) = api.getPopularFreeContentByCreator(creatorId, authHeader = token)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,8 +5,10 @@ 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.main.GetAudioContentMainItem
 | 
			
		||||
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentRankingItem
 | 
			
		||||
import kr.co.vividnext.sodalive.audio_content.main.v2.GetContentCurationResponse
 | 
			
		||||
import kr.co.vividnext.sodalive.audio_content.main.v2.series.GetRecommendSeriesListResponse
 | 
			
		||||
import kr.co.vividnext.sodalive.base.BaseViewModel
 | 
			
		||||
@@ -45,6 +47,15 @@ class AudioContentMainTabFreeViewModel(
 | 
			
		||||
    val themeListLiveData: LiveData<List<String>>
 | 
			
		||||
        get() = _themeListLiveData
 | 
			
		||||
 | 
			
		||||
    private val _contentCreatorListLiveData = MutableLiveData<List<ContentCreatorResponse>>()
 | 
			
		||||
    val contentCreatorListLiveData: LiveData<List<ContentCreatorResponse>>
 | 
			
		||||
        get() = _contentCreatorListLiveData
 | 
			
		||||
 | 
			
		||||
    private val _playCountRankContentListLiveData =
 | 
			
		||||
        MutableLiveData<List<GetAudioContentRankingItem>>()
 | 
			
		||||
    val playCountRankContentListLiveData: LiveData<List<GetAudioContentRankingItem>>
 | 
			
		||||
        get() = _playCountRankContentListLiveData
 | 
			
		||||
 | 
			
		||||
    private var _curationListLiveData = MutableLiveData<List<GetContentCurationResponse>>()
 | 
			
		||||
    val curationListLiveData: LiveData<List<GetContentCurationResponse>>
 | 
			
		||||
        get() = _curationListLiveData
 | 
			
		||||
@@ -71,6 +82,10 @@ class AudioContentMainTabFreeViewModel(
 | 
			
		||||
                            val themeList = listOf("전체").union(data.themeList).toList()
 | 
			
		||||
                            _themeListLiveData.value = themeList
 | 
			
		||||
 | 
			
		||||
                            _contentCreatorListLiveData.value = data.creatorList
 | 
			
		||||
                            _playCountRankContentListLiveData.value =
 | 
			
		||||
                                data.playCountRankContentList
 | 
			
		||||
 | 
			
		||||
                            _curationListLiveData.value = data.curationList
 | 
			
		||||
                        } else {
 | 
			
		||||
                            if (it.message != null) {
 | 
			
		||||
@@ -130,4 +145,37 @@ class AudioContentMainTabFreeViewModel(
 | 
			
		||||
                )
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getPopularContentByCreator(creatorId: Long) {
 | 
			
		||||
        _isLoading.value = true
 | 
			
		||||
        compositeDisposable.add(
 | 
			
		||||
            repository.getPopularContentByCreator(
 | 
			
		||||
                creatorId = creatorId,
 | 
			
		||||
                token = "Bearer ${SharedPreferenceManager.token}"
 | 
			
		||||
            )
 | 
			
		||||
                .subscribeOn(Schedulers.io())
 | 
			
		||||
                .observeOn(AndroidSchedulers.mainThread())
 | 
			
		||||
                .subscribe(
 | 
			
		||||
                    {
 | 
			
		||||
                        _isLoading.value = false
 | 
			
		||||
                        if (it.success && it.data != null) {
 | 
			
		||||
                            _playCountRankContentListLiveData.value = it.data!!
 | 
			
		||||
                        } else {
 | 
			
		||||
                            if (it.message != null) {
 | 
			
		||||
                                _toastLiveData.postValue(it.message)
 | 
			
		||||
                            } else {
 | 
			
		||||
                                _toastLiveData.postValue(
 | 
			
		||||
                                    "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
 | 
			
		||||
                                )
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        _isLoading.value = false
 | 
			
		||||
                        it.message?.let { message -> Logger.e(message) }
 | 
			
		||||
                        _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
 | 
			
		||||
                    }
 | 
			
		||||
                )
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,8 +2,10 @@ package kr.co.vividnext.sodalive.audio_content.main.v2.free
 | 
			
		||||
 | 
			
		||||
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.main.GetAudioContentMainItem
 | 
			
		||||
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentRankingItem
 | 
			
		||||
import kr.co.vividnext.sodalive.audio_content.main.v2.GetContentCurationResponse
 | 
			
		||||
import kr.co.vividnext.sodalive.audio_content.main.v2.series.GetRecommendSeriesListResponse
 | 
			
		||||
 | 
			
		||||
@@ -19,6 +21,10 @@ data class GetContentMainTabLiveFreeResponse(
 | 
			
		||||
    val themeList: List<String>,
 | 
			
		||||
    @SerializedName("newFreeContentList")
 | 
			
		||||
    val newFreeContentList: List<GetAudioContentMainItem>,
 | 
			
		||||
    @SerializedName("creatorList")
 | 
			
		||||
    val creatorList: List<ContentCreatorResponse>,
 | 
			
		||||
    @SerializedName("playCountRankContentList")
 | 
			
		||||
    val playCountRankContentList: List<GetAudioContentRankingItem>,
 | 
			
		||||
    @SerializedName("curationList")
 | 
			
		||||
    val curationList: List<GetContentCurationResponse>
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user