콘텐츠 메인

- 시리즈 탭 UI 구성
This commit is contained in:
klaus 2025-02-13 13:27:45 +09:00
parent 42e4c4649b
commit f75134c7e7
23 changed files with 1823 additions and 17 deletions

View File

@ -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<ApiResponse<GetPopularContentByCreatorResponse>>
@GET("/v2/audio-content/main/series")
fun getContentMainSeries(
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetContentMainTabSeriesResponse>>
}

View File

@ -337,10 +337,7 @@ class AudioContentMainTabHomeFragment : BaseFragment<FragmentAudioContentMainTab
}
binding.rlCategorySeries.setOnClickListener {
// startAudioContentMainActivity(AudioContentMainTab.SERIES)
requireContext().startActivity(
Intent(requireContext(), AudioContentMainActivity::class.java)
)
startAudioContentMainActivity(AudioContentMainTab.SERIES)
}
binding.rlCategoryContent.setOnClickListener {
@ -711,7 +708,7 @@ class AudioContentMainTabHomeFragment : BaseFragment<FragmentAudioContentMainTab
viewModel.contentRankCreatorListLiveData.observe(viewLifecycleOwner) {
contentRankCreatorAdapter.addItems(it)
if (rankCreatorAdapter.itemCount <= 0 && it.isEmpty()) {
if (contentRankCreatorAdapter.itemCount <= 0 && it.isEmpty()) {
binding.llCreatorContentRanking.visibility = View.GONE
} else {
binding.llCreatorContentRanking.visibility = View.VISIBLE

View File

@ -80,8 +80,6 @@ class AudioContentMainTabHomeViewModel(
if (it.success && it.data != null) {
val data = it.data
Logger.e("data: $data")
if (data.latestNotice != null) {
_noticeLiveData.value = data.latestNotice!!
}

View File

@ -1,9 +1,753 @@
package kr.co.vividnext.sodalive.audio_content.main.v2.series
import android.content.Intent
import android.graphics.Rect
import android.net.Uri
import android.os.Bundle
import android.view.View
import android.widget.LinearLayout
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.zhpan.bannerview.BaseBannerAdapter
import com.zhpan.indicator.enums.IndicatorSlideMode
import com.zhpan.indicator.enums.IndicatorStyle
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.main.AudioContentBannerType
import kr.co.vividnext.sodalive.audio_content.main.banner.AudioContentMainBannerAdapter
import kr.co.vividnext.sodalive.audio_content.main.v2.ContentRankCreatorAdapter
import kr.co.vividnext.sodalive.audio_content.main.v2.series.curation.AudioContentMainSeriesCurationAdapter
import kr.co.vividnext.sodalive.audio_content.main.v2.series.new_series.AudioContentMainNewSeriesAdapter
import kr.co.vividnext.sodalive.audio_content.main.v2.series.origianl_audio_drama.AudioContentMainTabSeriesOriginalAudioDramaAdapter
import kr.co.vividnext.sodalive.audio_content.main.v2.series.rank_series.AudioContentMainSeriesRankingAdapter
import kr.co.vividnext.sodalive.audio_content.main.v2.series.recommend_by_genre.AudioContentMainRecommendSeriesGenreAdapter
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.LoadingDialog
import kr.co.vividnext.sodalive.databinding.FragmentAudioContentMainTabSeriesBinding
import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
import kr.co.vividnext.sodalive.explorer.profile.series.UserProfileSeriesListAdapter
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.live.event_banner.EventBannerAdapter
import kr.co.vividnext.sodalive.settings.event.EventDetailActivity
import org.koin.android.ext.android.inject
import kotlin.math.roundToInt
class AudioContentMainTabSeriesFragment : BaseFragment<FragmentAudioContentMainTabSeriesBinding>(
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<Any>
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<Any>
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()
}
}
}
}

View File

@ -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)
}

View File

@ -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<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
private var _contentBannerLiveData = MutableLiveData<List<GetAudioContentBannerResponse>>()
val contentBannerLiveData: LiveData<List<GetAudioContentBannerResponse>>
get() = _contentBannerLiveData
private var _originalAudioDramaLiveData =
MutableLiveData<List<GetSeriesListResponse.SeriesListItem>>()
val originalAudioDramaLiveData: LiveData<List<GetSeriesListResponse.SeriesListItem>>
get() = _originalAudioDramaLiveData
private var _rankSeriesListLiveData =
MutableLiveData<List<GetSeriesListResponse.SeriesListItem>>()
val rankSeriesListLiveData: LiveData<List<GetSeriesListResponse.SeriesListItem>>
get() = _rankSeriesListLiveData
private var _genreListLiveData = MutableLiveData<List<GetSeriesGenreListResponse>>()
val genreListLiveData: LiveData<List<GetSeriesGenreListResponse>>
get() = _genreListLiveData
private var _recommendSeriesListLiveData =
MutableLiveData<List<GetSeriesListResponse.SeriesListItem>>()
val recommendSeriesListLiveData: LiveData<List<GetSeriesListResponse.SeriesListItem>>
get() = _recommendSeriesListLiveData
private var _newSeriesListLiveData = MutableLiveData<List<GetRecommendSeriesListResponse>>()
val newSeriesListLiveData: LiveData<List<GetRecommendSeriesListResponse>>
get() = _newSeriesListLiveData
private var _rankCompleteSeriesListLiveData =
MutableLiveData<List<GetSeriesListResponse.SeriesListItem>>()
val rankCompleteSeriesListLiveData: LiveData<List<GetSeriesListResponse.SeriesListItem>>
get() = _rankCompleteSeriesListLiveData
private var _seriesRankCreatorListLiveData = MutableLiveData<List<ContentCreatorResponse>>()
val seriesRankCreatorListLiveData: LiveData<List<ContentCreatorResponse>>
get() = _seriesRankCreatorListLiveData
private var _recommendSeriesByChannelLiveData =
MutableLiveData<List<GetSeriesListResponse.SeriesListItem>>()
val recommendSeriesByChannelLiveData: LiveData<List<GetSeriesListResponse.SeriesListItem>>
get() = _recommendSeriesByChannelLiveData
private val _eventLiveData = MutableLiveData<List<EventItem>>()
val eventLiveData: LiveData<List<EventItem>>
get() = _eventLiveData
private val _curationListLiveData = MutableLiveData<List<GetSeriesCurationResponse>>()
val curationListLiveData: LiveData<List<GetSeriesCurationResponse>>
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("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}

View File

@ -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<GetAudioContentBannerResponse>,
@SerializedName("originalAudioDrama")
val originalAudioDrama: List<GetSeriesListResponse.SeriesListItem>,
@SerializedName("rankSeriesList")
val rankSeriesList: List<GetSeriesListResponse.SeriesListItem>,
@SerializedName("genreList")
val genreList: List<GetSeriesGenreListResponse>,
@SerializedName("recommendSeriesList")
val recommendSeriesList: List<GetSeriesListResponse.SeriesListItem>,
@SerializedName("newSeriesList")
val newSeriesList: List<GetRecommendSeriesListResponse>,
@SerializedName("rankCompleteSeriesList")
val rankCompleteSeriesList: List<GetSeriesListResponse.SeriesListItem>,
@SerializedName("seriesRankCreatorList")
val seriesRankCreatorList: List<ContentCreatorResponse>,
@SerializedName("recommendSeriesByChannel")
val recommendSeriesByChannel: List<GetSeriesListResponse.SeriesListItem>,
@SerializedName("eventBannerList")
val eventBannerList: GetEventResponse,
@SerializedName("curationList")
val curationList: List<GetSeriesCurationResponse>
)

View File

@ -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
)

View File

@ -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<GetSeriesListResponse.SeriesListItem>
)

View File

@ -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
)

View File

@ -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<AudioContentMainSeriesCurationAdapter.ViewHolder>() {
private val items = mutableListOf<GetSeriesCurationResponse>()
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<GetSeriesListResponse.SeriesListItem>) {
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<GetSeriesCurationResponse>) {
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
}

View File

@ -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<AudioContentMainNewSeriesAdapter.ViewHolder>() {
private val items = mutableListOf<GetRecommendSeriesListResponse>()
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<GetRecommendSeriesListResponse>) {
this.items.addAll(items)
notifyDataSetChanged()
}
}

View File

@ -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<AudioContentMainTabSeriesOriginalAudioDramaAdapter.ViewHolder>() {
val items = mutableListOf<GetSeriesListResponse.SeriesListItem>()
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<GetSeriesListResponse.SeriesListItem>) {
this.items.addAll(items)
notifyDataSetChanged()
}
fun isVisibleRecyclerView(): Boolean {
return items.isNotEmpty()
}
}

View File

@ -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<AudioContentMainSeriesRankingAdapter.ViewHolder>() {
val items = mutableListOf<GetSeriesListResponse.SeriesListItem>()
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<GetSeriesListResponse.SeriesListItem>) {
this.items.addAll(items)
notifyDataSetChanged()
}
}

View File

@ -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<AudioContentMainRecommendSeriesGenreAdapter.ViewHolder>() {
private var items = mutableListOf<GetSeriesGenreListResponse>()
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<GetSeriesGenreListResponse>) {
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])
}
}

View File

@ -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

View File

@ -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(

View File

@ -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

View File

@ -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" />
</LinearLayout>
<com.zhpan.bannerview.BannerViewPager

View File

@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
tools:background="@color/black">
<LinearLayout
android:layout_width="match_parent"
@ -12,8 +14,7 @@
android:id="@+id/rv_banner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="30dp" />
android:layout_marginHorizontal="13.3dp" />
<com.zhpan.indicator.IndicatorView
android:id="@+id/indicator_banner"
@ -21,5 +22,252 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="6.7dp" />
<LinearLayout
android:id="@+id/ll_original_audio_drama"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:orientation="vertical"
android:visibility="gone">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:fontFamily="@font/gmarket_sans_bold"
android:text="오리지널 오디오 드라마"
android:textColor="@color/color_eeeeee"
android:textSize="18.3sp" />
<ImageView
android:id="@+id/iv_original_audio_drama_all"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:contentDescription="@null"
android:paddingHorizontal="13.3dp"
android:src="@drawable/ic_forward" />
</RelativeLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_original_audio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"
android:clipToPadding="false"
android:paddingHorizontal="13.3dp" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_rank_series"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:fontFamily="@font/gmarket_sans_bold"
android:text="일간 랭킹"
android:textColor="@color/color_eeeeee"
android:textSize="18.3sp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_rank_series"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"
android:clipToPadding="false"
android:paddingHorizontal="13.3dp" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_series_by_genre"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:fontFamily="@font/gmarket_sans_bold"
android:text="장르별 추천 시리즈"
android:textColor="@color/color_eeeeee"
android:textSize="18.3sp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_series_genre"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"
android:clipToPadding="false"
android:paddingHorizontal="13.3dp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_series_by_genre"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"
android:clipToPadding="false"
android:paddingHorizontal="13.3dp" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_new_series"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:fontFamily="@font/gmarket_sans_bold"
android:text="새로운 시리즈"
android:textColor="@color/color_eeeeee"
android:textSize="18.3sp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_new_series"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"
android:clipToPadding="false"
android:paddingHorizontal="13.3dp" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_complete_series"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:fontFamily="@font/gmarket_sans_bold"
android:text="완결 시리즈"
android:textColor="@color/color_eeeeee"
android:textSize="18.3sp" />
<ImageView
android:id="@+id/iv_complete_series_all"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:contentDescription="@null"
android:paddingHorizontal="13.3dp"
android:src="@drawable/ic_forward" />
</RelativeLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_complete_series"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"
android:clipToPadding="false"
android:paddingHorizontal="13.3dp" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_recommend_series_by_channel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:fontFamily="@font/gmarket_sans_bold"
android:text="채널별 추천 시리즈"
android:textColor="@color/color_eeeeee"
android:textSize="18.3sp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_recommend_series_channel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:clipToPadding="false"
android:paddingHorizontal="13.3dp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_recommend_series_by_channel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:clipToPadding="false"
android:paddingHorizontal="13.3dp"
android:visibility="gone" />
<LinearLayout
android:id="@+id/ll_no_items"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="28.3dp"
android:background="@drawable/bg_round_corner_4_7_13181b"
android:gravity="center"
android:orientation="vertical"
android:paddingVertical="16.7dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_no_item" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginVertical="10dp"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:lineSpacingExtra="8dp"
android:text="마이페이지에서 본인인증을 해주세요"
android:textColor="@color/color_bbbbbb"
android:textSize="13sp"
tools:ignore="SmallSp" />
</LinearLayout>
</LinearLayout>
<com.zhpan.bannerview.BannerViewPager
android:id="@+id/event_banner_slider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:visibility="gone" />
<com.zhpan.indicator.IndicatorView
android:id="@+id/indicator_event_banner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="6.7dp"
android:visibility="gone" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_curation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:background="@color/black">
<ImageView
android:id="@+id/iv_cover"
android:layout_width="267dp"
android:layout_height="141.3dp"
android:contentDescription="@null"
app:layout_constraintDimensionRatio="800:424"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="9dp"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@color/color_eeeeee"
android:textSize="13.3sp"
app:layout_constraintStart_toStartOf="@+id/iv_cover"
app:layout_constraintTop_toBottomOf="@+id/iv_cover"
tools:text="시리즈 제목" />
<ImageView
android:id="@+id/iv_creator_profile"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginTop="9dp"
android:contentDescription="@null"
app:layout_constraintStart_toStartOf="@+id/tv_title"
app:layout_constraintTop_toBottomOf="@+id/tv_title"
tools:src="@drawable/img_thumb_default" />
<TextView
android:id="@+id/tv_creator_nickname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="5.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@color/color_777777"
android:textSize="10sp"
app:layout_constraintBottom_toBottomOf="@+id/iv_creator_profile"
app:layout_constraintStart_toEndOf="@+id/iv_creator_profile"
app:layout_constraintTop_toTopOf="@+id/iv_creator_profile"
tools:ignore="SmallSp"
tools:text="민하나" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:gravity="center_vertical"
android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_cover"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintDimensionRatio="60:85"
app:layout_constraintTop_toTopOf="parent"
android:contentDescription="@null" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/tv_rank"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="12dp"
android:fontFamily="@font/gmarket_sans_bold"
android:textColor="@color/color_3bb9f1"
android:textSize="16.7sp"
tools:text="1" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:maxLines="2"
android:textColor="@color/color_d2d2d2"
android:textSize="13.3sp"
tools:text="라일락 꽃 향기 맡으며" />
<TextView
android:id="@+id/tv_nickname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@color/color_777777"
android:textSize="11sp"
tools:text="J Fla" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_cover"
android:layout_width="150dp"
android:layout_height="212dp"
android:contentDescription="@null"
android:scaleType="centerCrop"
app:layout_constraintDimensionRatio="450:636"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_launcher_background" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="3.3dp"
android:layout_marginTop="3.3dp"
android:orientation="horizontal"
app:layout_constraintStart_toStartOf="@+id/iv_cover"
app:layout_constraintTop_toTopOf="@+id/iv_cover">
<TextView
android:id="@+id/tv_new"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_round_corner_13_3_3bb9f1"
android:fontFamily="@font/gmarket_sans_medium"
android:paddingHorizontal="5.3dp"
android:paddingVertical="3.7dp"
android:text="신작"
android:textColor="@color/white"
android:textSize="10.3sp"
android:visibility="gone"
tools:ignore="SmallSp" />
<TextView
android:id="@+id/tv_complete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_round_corner_13_3_002abd"
android:fontFamily="@font/gmarket_sans_medium"
android:paddingHorizontal="5.3dp"
android:paddingVertical="3.7dp"
android:text="완결"
android:textColor="@color/white"
android:textSize="10.3sp"
android:visibility="gone"
tools:ignore="SmallSp" />
<TextView
android:id="@+id/tv_popular"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_round_corner_13_3_ec6033"
android:fontFamily="@font/gmarket_sans_medium"
android:paddingHorizontal="5.3dp"
android:paddingVertical="3.7dp"
android:text="인기"
android:textColor="@color/white"
android:textSize="10.3sp"
android:visibility="gone"
tools:ignore="SmallSp" />
</LinearLayout>
<TextView
android:id="@+id/tv_series_content_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3.3dp"
android:layout_marginEnd="3.3dp"
android:background="@drawable/bg_round_corner_13_3_b3333333"
android:fontFamily="@font/gmarket_sans_medium"
android:paddingHorizontal="5.3dp"
android:paddingVertical="2.7dp"
android:textColor="@color/white"
android:textSize="10.3sp"
app:layout_constraintEnd_toEndOf="@+id/iv_cover"
app:layout_constraintTop_toTopOf="@+id/iv_cover"
tools:ignore="SmallSp"
tools:text="총 24화" />
<TextView
android:id="@+id/tv_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"
android:ellipsize="end"
android:fontFamily="@font/gmarket_sans_medium"
android:maxLines="2"
android:textColor="@color/color_eeeeee"
android:textSize="12sp"
app:layout_constraintEnd_toEndOf="@+id/iv_cover"
app:layout_constraintStart_toStartOf="@+id/iv_cover"
app:layout_constraintTop_toBottomOf="@+id/iv_cover"
tools:text="제목, 관심사,프로필+방장, 참여인원(어딘가..)" />
</androidx.constraintlayout.widget.ConstraintLayout>