콘텐츠 메인 업데이트

- 홈 UI 업데이트
This commit is contained in:
klaus 2025-02-10 03:08:10 +09:00
parent c7b238f975
commit 5469d288ba
21 changed files with 1956 additions and 15 deletions

View File

@ -15,10 +15,10 @@ import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentBannerResponse
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentCurationResponse
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.GetNewContentUploadCreator
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.order.GetAudioContentOrderListResponse
import kr.co.vividnext.sodalive.audio_content.order.OrderRequest
import kr.co.vividnext.sodalive.audio_content.order.OrderType
import kr.co.vividnext.sodalive.audio_content.player.GenerateUrlResponse
import kr.co.vividnext.sodalive.audio_content.upload.theme.GetAudioContentThemeResponse
import kr.co.vividnext.sodalive.common.ApiResponse
@ -211,11 +211,6 @@ interface AudioContentApi {
@Header("Authorization") authHeader: String
): Single<ApiResponse<List<GetAudioContentCurationResponse>>>
@GET("/audio-content/main/new-content-upload-creator")
fun getNewContentUploadCreatorList(
@Header("Authorization") authHeader: String
): Single<ApiResponse<List<GetNewContentUploadCreator>>>
@GET("/audio-content/main/banner-list")
fun getMainBannerList(
@Header("Authorization") authHeader: String
@ -243,4 +238,15 @@ interface AudioContentApi {
@Path("id") contentId: Long,
@Header("Authorization") authHeader: String
): Single<ApiResponse<GenerateUrlResponse>>
@GET("/v2/audio-content/main/home")
fun getContentMainHome(
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetContentMainTabHomeResponse>>
@GET("/v2/audio-content/main/home/popular-content-by-creator")
fun getPopularContentByCreator(
@Query("creatorId") creatorId: Long,
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetPopularContentByCreatorResponse>>
}

View File

@ -198,10 +198,6 @@ class AudioContentRepository(
authHeader = token
)
fun getNewContentUploadCreatorList(
token: String
) = api.getNewContentUploadCreatorList(authHeader = token)
fun getMainBannerList(token: String) = api.getMainBannerList(authHeader = token)
fun getMainOrderList(token: String) = api.getMainOrderList(authHeader = token)
fun pinContent(

View File

@ -5,7 +5,7 @@ import com.google.gson.annotations.SerializedName
import kr.co.vividnext.sodalive.settings.event.EventItem
@Keep
data class GetNewContentUploadCreator(
data class ContentCreatorResponse(
@SerializedName("creatorId") val creatorId: Long,
@SerializedName("creatorNickname") val creatorNickname: String,
@SerializedName("creatorProfileImageUrl") val creatorProfileImageUrl: String
@ -39,7 +39,8 @@ data class GetAudioContentRankingItem(
@SerializedName("price") val price: Int,
@SerializedName("duration") val duration: String,
@SerializedName("creatorId") val creatorId: Long,
@SerializedName("creatorNickname") val creatorNickname: String
@SerializedName("creatorNickname") val creatorNickname: String,
@SerializedName("creatorProfileImageUrl") val creatorProfileImageUrl: String
)
@Keep

View File

@ -0,0 +1,87 @@
package kr.co.vividnext.sodalive.audio_content.main.v2
import android.annotation.SuppressLint
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import coil.load
import coil.transform.CircleCropTransformation
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.main.ContentCreatorResponse
import kr.co.vividnext.sodalive.databinding.ItemContentRankCreatorBinding
class ContentRankCreatorAdapter(
private val onClickItem: (Long) -> Unit,
) : RecyclerView.Adapter<ContentRankCreatorAdapter.ViewHolder>() {
private var selectedCreatorId: Long = 0
private val items = mutableListOf<ContentCreatorResponse>()
inner class ViewHolder(
private val context: Context,
private val binding: ItemContentRankCreatorBinding
) : RecyclerView.ViewHolder(binding.root) {
@SuppressLint("NotifyDataSetChanged")
fun bind(item: ContentCreatorResponse) {
binding.root.setOnClickListener {
selectedCreatorId = item.creatorId
onClickItem(item.creatorId)
notifyDataSetChanged()
}
binding.tvNickname.text = item.creatorNickname
binding.ivProfile.load(item.creatorProfileImageUrl) {
transformations(CircleCropTransformation())
placeholder(R.drawable.ic_place_holder)
crossfade(true)
}
if (item.creatorId == selectedCreatorId) {
binding.ivBg.setImageResource(R.drawable.bg_circle_3bb9f1)
binding.ivBg.visibility = View.VISIBLE
binding.tvNickname.setTextColor(
ContextCompat.getColor(
context,
R.color.color_3bb9f1
)
)
} else {
binding.ivBg.setImageResource(0)
binding.ivBg.visibility = View.GONE
binding.tvNickname.setTextColor(
ContextCompat.getColor(
context,
R.color.color_bbbbbb
)
)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
parent.context,
ItemContentRankCreatorBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(items[position])
}
override fun getItemCount() = items.size
@SuppressLint("NotifyDataSetChanged")
fun addItems(items: List<ContentCreatorResponse>) {
this.items.addAll(items)
if (this.items.isNotEmpty()) {
this.selectedCreatorId = this.items[0].creatorId
}
notifyDataSetChanged()
}
}

View File

@ -0,0 +1,13 @@
package kr.co.vividnext.sodalive.audio_content.main.v2
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentRankingItem
@Keep
data class GetPopularContentByCreatorResponse(
@SerializedName("salesRankContentList")
val salesRankContentList: List<GetAudioContentRankingItem>,
@SerializedName("salesCountRankContentList")
val salesCountRankContentList: List<GetAudioContentRankingItem>
)

View File

@ -0,0 +1,690 @@
package kr.co.vividnext.sodalive.audio_content.main.v2.home
import android.content.Intent
import android.graphics.Color
import android.graphics.Rect
import android.net.Uri
import android.os.Bundle
import android.text.SpannableString
import android.text.Spanned
import android.text.style.ForegroundColorSpan
import android.view.View
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
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 coil.load
import coil.transform.CircleCropTransformation
import coil.transform.RoundedCornersTransformation
import com.orhanobut.logger.Logger
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.all.AudioContentRankingAllActivity
import kr.co.vividnext.sodalive.audio_content.box.AudioContentBoxActivity
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.audio_content.main.AudioContentBannerType
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentRankingItem
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.ranking.AudioContentMainRankingAdapter
import kr.co.vividnext.sodalive.audio_content.main.v2.ContentRankCreatorAdapter
import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailActivity
import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadActivity
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.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.FragmentAudioContentMainTabHomeBinding
import kr.co.vividnext.sodalive.explorer.ExplorerSectionAdapter
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.main.MainActivity
import kr.co.vividnext.sodalive.mypage.alarm.AlarmListActivity
import kr.co.vividnext.sodalive.settings.event.EventDetailActivity
import kr.co.vividnext.sodalive.settings.notice.NoticeDetailActivity
import kr.co.vividnext.sodalive.settings.notification.MemberRole
import org.koin.android.ext.android.inject
import kotlin.math.roundToInt
@UnstableApi
class AudioContentMainTabHomeFragment : BaseFragment<FragmentAudioContentMainTabHomeBinding>(
FragmentAudioContentMainTabHomeBinding::inflate
) {
private val viewModel: AudioContentMainTabHomeViewModel by inject()
private lateinit var loadingDialog: LoadingDialog
private lateinit var rankCreatorAdapter: ExplorerSectionAdapter
private lateinit var rankSeriesAdapter: UserProfileSeriesListAdapter
private lateinit var contentBannerAdapter: AudioContentMainBannerAdapter
private lateinit var rankContentAdapter: AudioContentMainRankingAdapter
private lateinit var rankContentSortAdapter: AudioContentMainNewContentThemeAdapter
private lateinit var contentRankCreatorAdapter: ContentRankCreatorAdapter
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupView()
bindData()
viewModel.fetchData()
}
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()
}
}
viewModel.salesRankContentListLiveData.observe(viewLifecycleOwner) {
if (it.isNotEmpty()) {
binding.llNoItems.visibility = View.GONE
binding.llSalesTop2.visibility = View.VISIBLE
setSalesRankContent(
item = it[0],
titleTextView = binding.tvSalesTitle1,
coverImageView = binding.ivSales1,
creatorTextView = binding.tvSalesCreator1,
creatorImageView = binding.ivSalesCreator1
)
if (it.size > 1) {
binding.llSales2.visibility = View.VISIBLE
setSalesRankContent(
item = it[1],
titleTextView = binding.tvSalesTitle2,
coverImageView = binding.ivSales2,
creatorTextView = binding.tvSalesCreator2,
creatorImageView = binding.ivSalesCreator2
)
}
}
}
viewModel.salesCountRankContentListLiveData.observe(viewLifecycleOwner) {
if (it.isNotEmpty()) {
binding.llNoItems.visibility = View.GONE
binding.llSalesCountTop2.visibility = View.VISIBLE
setSalesRankContent(
item = it[0],
titleTextView = binding.tvSalesCountTitle1,
coverImageView = binding.ivSalesCount1,
creatorTextView = binding.tvSalesCountCreator1,
creatorImageView = binding.ivSalesCountCreator1
)
if (it.size > 1) {
binding.llSalesCount2.visibility = View.VISIBLE
setSalesRankContent(
item = it[1],
titleTextView = binding.tvSalesCountTitle2,
coverImageView = binding.ivSalesCount2,
creatorTextView = binding.tvSalesCountCreator2,
creatorImageView = binding.ivSalesCountCreator2
)
}
}
}
}
private fun setSalesRankContent(
item: GetAudioContentRankingItem,
titleTextView: TextView,
creatorTextView: TextView,
coverImageView: ImageView,
creatorImageView: ImageView
) {
coverImageView.load(item.coverImageUrl) {
crossfade(true)
placeholder(R.drawable.bg_placeholder)
transformations(RoundedCornersTransformation(5.3f.dpToPx()))
}
titleTextView.text = item.title
creatorTextView.text = item.creatorNickname
creatorImageView.load(item.creatorProfileImageUrl) {
transformations(CircleCropTransformation())
placeholder(R.drawable.ic_place_holder)
crossfade(true)
}
coverImageView.setOnClickListener {
startActivity(
Intent(requireActivity(), AudioContentDetailActivity::class.java).apply {
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, item.contentId)
}
)
}
creatorImageView.setOnClickListener {
startActivity(
Intent(requireActivity(), UserProfileActivity::class.java).apply {
putExtra(Constants.EXTRA_USER_ID, item.creatorId)
}
)
}
}
private fun setupView() {
loadingDialog = LoadingDialog(requireActivity(), layoutInflater)
if (SharedPreferenceManager.role == MemberRole.CREATOR.name) {
binding.llUploadContent.visibility = View.VISIBLE
binding.llUploadContent.setOnClickListener {
startActivity(
Intent(
requireActivity(),
AudioContentUploadActivity::class.java
)
)
}
} else {
binding.llUploadContent.visibility = View.GONE
}
binding.ivContentKeep.setOnClickListener {
startActivity(
Intent(
requireContext(),
AudioContentBoxActivity::class.java
)
)
}
binding.ivAlarm.setOnClickListener {
startActivity(
Intent(
requireActivity(),
AlarmListActivity::class.java
)
)
}
binding.flSearchChannel.setOnClickListener {
(requireActivity() as MainActivity).showSearchBar()
}
setupNotice()
setupContentBanner()
setupCategory()
setupRankCreator()
setupRankSeries()
setupRankContentSortType()
setupRankContent()
setupEventBanner()
setupPopularContentByCreator()
}
private fun setupNotice() {
viewModel.noticeLiveData.observe(viewLifecycleOwner) { notice ->
binding.tvNoticeTitle.text = notice.title
binding.tvDetail.setOnClickListener {
startActivity(
Intent(requireContext(), NoticeDetailActivity::class.java).apply {
putExtra(Constants.EXTRA_NOTICE, notice)
}
)
}
}
}
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 setupCategory() {
binding.rlCategoryAudioBook.setOnClickListener {
showToast("준비중 입니다.")
}
binding.rlCategoryAudioToon.setOnClickListener {
showToast("준비중 입니다.")
}
}
private fun setupRankCreator() {
rankCreatorAdapter = ExplorerSectionAdapter(
onClickItem = {
startActivity(
Intent(requireContext(), UserProfileActivity::class.java).apply {
putExtra(Constants.EXTRA_USER_ID, it)
}
)
},
isVisibleRanking = true
)
binding.rvCreatorRank.layoutManager = LinearLayoutManager(
context,
LinearLayoutManager.HORIZONTAL,
false
)
binding.rvCreatorRank.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()
}
rankCreatorAdapter.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.rvCreatorRank.adapter = rankCreatorAdapter
viewModel.rankCreatorLiveData.observe(viewLifecycleOwner) {
binding.tvDesc.text = it.desc
binding.tvCreatorRankTitle.text = if (
!it.coloredTitle.isNullOrBlank() &&
!it.color.isNullOrBlank()
) {
val spStr = SpannableString(it.title)
try {
spStr.setSpan(
ForegroundColorSpan(
Color.parseColor("#${it.color}")
),
it.title.indexOf(it.coloredTitle),
it.title.indexOf(it.coloredTitle) + it.coloredTitle.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
spStr
} catch (e: IllegalArgumentException) {
it.title
}
} else {
it.title
}
rankCreatorAdapter.addItems(it.creators)
if (rankCreatorAdapter.itemCount <= 0 && it.creators.isEmpty()) {
binding.llCreatorRank.visibility = View.GONE
binding.rvCreatorRank.visibility = View.GONE
} else {
binding.llCreatorRank.visibility = View.VISIBLE
binding.rvCreatorRank.visibility = View.VISIBLE
}
}
}
private fun setupRankSeries() {
rankSeriesAdapter = 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()
}
rankSeriesAdapter.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 = rankSeriesAdapter
viewModel.rankSeriesLiveData.observe(viewLifecycleOwner) {
rankSeriesAdapter.addItems(it)
binding.llRankSeries.visibility = if (
rankSeriesAdapter.itemCount <= 0 && it.isEmpty()
) {
View.GONE
} else {
View.VISIBLE
}
}
}
private fun setupRankContentSortType() {
rankContentSortAdapter = AudioContentMainNewContentThemeAdapter {
viewModel.getContentRanking(sort = it)
}
binding.rvRankContentSort.layoutManager = LinearLayoutManager(
context,
LinearLayoutManager.HORIZONTAL,
false
)
binding.rvRankContentSort.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()
}
rankContentSortAdapter.itemCount - 1 -> {
outRect.left = 4f.dpToPx().toInt()
outRect.right = 0
}
else -> {
outRect.left = 4f.dpToPx().toInt()
outRect.right = 4f.dpToPx().toInt()
}
}
}
})
binding.rvRankContentSort.adapter = rankContentSortAdapter
viewModel.rankContentSortListLiveData.observe(viewLifecycleOwner) {
binding.llRankContent.visibility = View.VISIBLE
rankContentSortAdapter.addItems(it)
}
}
private fun setupRankContent() {
binding.ivRankContentAll.setOnClickListener {
startActivity(Intent(requireContext(), AudioContentRankingAllActivity::class.java))
}
rankContentAdapter = AudioContentMainRankingAdapter(
width = (screenWidth * 0.66).toInt()
) {
startActivity(
Intent(requireContext(), AudioContentDetailActivity::class.java).apply {
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, it)
}
)
}
binding.rvRankContent.layoutManager = GridLayoutManager(
context,
3,
GridLayoutManager.HORIZONTAL,
false
)
binding.rvRankContent.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.rvRankContent.adapter = rankContentAdapter
viewModel.rankContentLiveData.observe(viewLifecycleOwner) {
binding.llUploadContent.visibility = View.VISIBLE
rankContentAdapter.addItems(it)
}
}
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.eventBannerSlider.refreshData(it)
} else {
binding.eventBannerSlider.visibility = View.GONE
}
}
}
private fun setupPopularContentByCreator() {
contentRankCreatorAdapter = ContentRankCreatorAdapter {
loadingPopularContentByCreator()
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 = 4f.dpToPx().toInt()
}
contentRankCreatorAdapter.itemCount - 1 -> {
outRect.left = 4f.dpToPx().toInt()
outRect.right = 0
}
else -> {
outRect.left = 4f.dpToPx().toInt()
outRect.right = 4f.dpToPx().toInt()
}
}
}
})
binding.rvRankingCreator.adapter = contentRankCreatorAdapter
viewModel.contentRankCreatorListLiveData.observe(viewLifecycleOwner) {
contentRankCreatorAdapter.addItems(it)
if (rankCreatorAdapter.itemCount <= 0 && it.isEmpty()) {
binding.llCreatorContentRanking.visibility = View.GONE
} else {
binding.llCreatorContentRanking.visibility = View.VISIBLE
}
}
}
private fun loadingPopularContentByCreator() {
binding.llSales2.visibility = View.GONE
binding.llSalesTop2.visibility = View.GONE
binding.llSalesCount2.visibility = View.GONE
binding.llSalesCountTop2.visibility = View.GONE
binding.llNoItems.visibility = View.VISIBLE
}
}

View File

@ -0,0 +1,11 @@
package kr.co.vividnext.sodalive.audio_content.main.v2.home
import kr.co.vividnext.sodalive.audio_content.AudioContentApi
class AudioContentMainTabHomeRepository(private val api: AudioContentApi) {
fun getContentMainHome(token: String) = api.getContentMainHome(authHeader = token)
fun getPopularContentByCreator(
creatorId: Long,
token: String
) = api.getPopularContentByCreator(creatorId, authHeader = token)
}

View File

@ -0,0 +1,189 @@
package kr.co.vividnext.sodalive.audio_content.main.v2.home
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.AudioContentRepository
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.GetAudioContentRankingItem
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.explorer.GetExplorerSectionResponse
import kr.co.vividnext.sodalive.settings.event.EventItem
import kr.co.vividnext.sodalive.settings.notice.NoticeItem
class AudioContentMainTabHomeViewModel(
private val repository: AudioContentMainTabHomeRepository,
private val contentRepository: AudioContentRepository
) : 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 _noticeLiveData = MutableLiveData<NoticeItem>()
val noticeLiveData: LiveData<NoticeItem>
get() = _noticeLiveData
private var _contentBannerLiveData = MutableLiveData<List<GetAudioContentBannerResponse>>()
val contentBannerLiveData: LiveData<List<GetAudioContentBannerResponse>>
get() = _contentBannerLiveData
private val _rankCreatorLiveData = MutableLiveData<GetExplorerSectionResponse>()
val rankCreatorLiveData: LiveData<GetExplorerSectionResponse>
get() = _rankCreatorLiveData
private var _rankSeriesLiveData = MutableLiveData<List<GetSeriesListResponse.SeriesListItem>>()
val rankSeriesLiveData: LiveData<List<GetSeriesListResponse.SeriesListItem>>
get() = _rankSeriesLiveData
private var _rankContentSortListLiveData = MutableLiveData<List<String>>()
val rankContentSortListLiveData: LiveData<List<String>>
get() = _rankContentSortListLiveData
private var _rankContentLiveData = MutableLiveData<List<GetAudioContentRankingItem>>()
val rankContentLiveData: LiveData<List<GetAudioContentRankingItem>>
get() = _rankContentLiveData
private val _eventLiveData = MutableLiveData<List<EventItem>>()
val eventLiveData: LiveData<List<EventItem>>
get() = _eventLiveData
private val _contentRankCreatorListLiveData = MutableLiveData<List<ContentCreatorResponse>>()
val contentRankCreatorListLiveData: LiveData<List<ContentCreatorResponse>>
get() = _contentRankCreatorListLiveData
private val _salesRankContentListLiveData = MutableLiveData<List<GetAudioContentRankingItem>>()
val salesRankContentListLiveData: LiveData<List<GetAudioContentRankingItem>>
get() = _salesRankContentListLiveData
private val _salesCountRankContentListLiveData =
MutableLiveData<List<GetAudioContentRankingItem>>()
val salesCountRankContentListLiveData: LiveData<List<GetAudioContentRankingItem>>
get() = _salesCountRankContentListLiveData
fun fetchData() {
_isLoading.value = true
compositeDisposable.add(
repository.getContentMainHome(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")
if (data.latestNotice != null) {
_noticeLiveData.value = data.latestNotice!!
}
_contentBannerLiveData.value = data.bannerList
_rankCreatorLiveData.value = data.rankCreatorList
_rankSeriesLiveData.value = data.rankSeriesList
_rankContentLiveData.value = data.rankContentList
_rankContentSortListLiveData.value = data.rankSortTypeList
_eventLiveData.value = data.eventBannerList.eventList
_contentRankCreatorListLiveData.value = data.contentRankCreatorList
_salesRankContentListLiveData.value = data.salesRankContentList
_salesCountRankContentListLiveData.value =
data.salesCountRankContentList
} 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("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun getContentRanking(sort: String = "매출") {
_isLoading.value = true
compositeDisposable.add(
contentRepository.getContentRanking(
page = 1,
size = 12,
sortType = sort,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success && it.data != null) {
_rankContentLiveData.value = it.data.items
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
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) {
val data = it.data
_salesRankContentListLiveData.value = data.salesRankContentList
_salesCountRankContentListLiveData.value =
data.salesCountRankContentList
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}

View File

@ -0,0 +1,24 @@
package kr.co.vividnext.sodalive.audio_content.main.v2.home
import androidx.annotation.Keep
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.GetAudioContentRankingItem
import kr.co.vividnext.sodalive.audio_content.series.GetSeriesListResponse
import kr.co.vividnext.sodalive.explorer.GetExplorerSectionResponse
import kr.co.vividnext.sodalive.settings.event.GetEventResponse
import kr.co.vividnext.sodalive.settings.notice.NoticeItem
@Keep
data class GetContentMainTabHomeResponse(
val latestNotice: NoticeItem?,
val bannerList: List<GetAudioContentBannerResponse>,
val rankCreatorList: GetExplorerSectionResponse,
val rankSeriesList: List<GetSeriesListResponse.SeriesListItem>,
val rankSortTypeList: List<String>,
val rankContentList: List<GetAudioContentRankingItem>,
val eventBannerList: GetEventResponse,
val contentRankCreatorList: List<ContentCreatorResponse>,
val salesRankContentList: List<GetAudioContentRankingItem>,
val salesCountRankContentList: List<GetAudioContentRankingItem>
)

View File

@ -23,6 +23,8 @@ import kr.co.vividnext.sodalive.audio_content.main.order.AudioContentMainOrderLi
import kr.co.vividnext.sodalive.audio_content.main.ranking.AudioContentMainCreatorRankingViewModel
import kr.co.vividnext.sodalive.audio_content.main.ranking.AudioContentMainRankingViewModel
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.modify.AudioContentModifyViewModel
import kr.co.vividnext.sodalive.audio_content.order.AudioContentOrderListViewModel
import kr.co.vividnext.sodalive.audio_content.player.AudioContentGenerateUrlRepository
@ -290,6 +292,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
viewModel { AuditionDetailViewModel(get()) }
viewModel { AuditionRoleDetailViewModel(get()) }
viewModel { AudioContentMainCreatorRankingViewModel(get()) }
viewModel { AudioContentMainTabHomeViewModel(get(), get()) }
}
private val repositoryModule = module {
@ -321,6 +324,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
factory { AudioContentPlaylistRepository(get()) }
factory { AudioContentGenerateUrlRepository(get()) }
factory { AuditionRepository(get()) }
factory { AudioContentMainTabHomeRepository(get()) }
}
private val moduleList = listOf(

View File

@ -1,6 +1,7 @@
package kr.co.vividnext.sodalive.main
import android.Manifest
import android.annotation.SuppressLint
import android.app.Service
import android.content.BroadcastReceiver
import android.content.ComponentName
@ -39,7 +40,7 @@ import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.AudioContentPlayService
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.audio_content.main.AudioContentMainFragment
import kr.co.vividnext.sodalive.audio_content.main.v2.home.AudioContentMainTabHomeFragment
import kr.co.vividnext.sodalive.audio_content.player.AudioContentPlayerFragment
import kr.co.vividnext.sodalive.audio_content.player.AudioContentPlayerService
import kr.co.vividnext.sodalive.audition.AuditionFragment
@ -232,6 +233,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
playerFragment.show(supportFragmentManager, playerFragment.tag)
}
@SuppressLint("UnspecifiedRegisterReceiverFlag")
override fun onResume() {
super.onResume()
val intentFilter = IntentFilter(Constants.ACTION_MAIN_AUDIO_CONTENT_RECEIVER)
@ -445,7 +447,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
if (fragment == null) {
fragment = when (currentTab) {
MainViewModel.CurrentTab.LIVE -> liveFragment
MainViewModel.CurrentTab.CONTENT -> AudioContentMainFragment()
MainViewModel.CurrentTab.CONTENT -> AudioContentMainTabHomeFragment()
MainViewModel.CurrentTab.AUDITION -> AuditionFragment()
MainViewModel.CurrentTab.MESSAGE -> MessageFragment()
MainViewModel.CurrentTab.MY -> MyPageFragment()
@ -528,6 +530,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
}
}
@SuppressLint("NotifyDataSetChanged")
private fun setupSearchChannelView() {
searchChannelAdapter = SelectMessageRecipientAdapter {
hideKeyboard()

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1,872 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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="match_parent"
android:layout_height="match_parent"
tools:background="@color/black">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingVertical="13.3dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp">
<TextView
android:id="@+id/tv_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_toStartOf="@+id/iv_alarm"
android:fontFamily="@font/gmarket_sans_bold"
android:text="콘텐츠 마켓"
android:textColor="@color/color_3bb9f1"
android:textSize="21.3sp" />
<ImageView
android:id="@+id/iv_alarm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_toStartOf="@+id/iv_content_keep"
android:contentDescription="@null"
android:src="@drawable/ic_alarm_clock" />
<ImageView
android:id="@+id/iv_content_keep"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:contentDescription="@null"
android:src="@drawable/ic_content_keep" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="15dp"
android:background="@drawable/bg_round_corner_5_3_222222"
android:paddingHorizontal="13.3dp"
android:paddingVertical="10dp">
<TextView
android:id="@+id/tv_notice_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@color/white"
android:textSize="13.3sp"
tools:text="[업데이트] 1.23.0 버전 업데이트" />
<TextView
android:id="@+id/tv_detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:fontFamily="@font/gmarket_sans_medium"
android:text="자세히 >"
android:textColor="@color/white"
android:textSize="13.3sp" />
</RelativeLayout>
<com.zhpan.bannerview.BannerViewPager
android:id="@+id/rv_banner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="30dp" />
<com.zhpan.indicator.IndicatorView
android:id="@+id/indicator_banner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="6.7dp" />
<RelativeLayout
android:id="@+id/fl_search_channel"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="30dp"
android:background="@drawable/bg_round_corner_6_7_222222_bbbbbb"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_gravity="center_vertical"
android:layout_marginStart="21.3dp"
android:contentDescription="@null"
android:src="@drawable/ic_title_search_black" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@null"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center_vertical"
android:paddingHorizontal="54.67dp"
android:text="채널명을 입력해보세요"
android:textColor="@color/color_555555"
android:textSize="13.3sp" />
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="30dp"
android:background="@drawable/bg_round_corner_5_3_222222"
android:orientation="vertical"
android:paddingVertical="13.3dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:orientation="horizontal">
<RelativeLayout
android:id="@+id/rl_category_series"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<ImageView
android:id="@+id/iv_category_series"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:contentDescription="@null"
android:src="@drawable/ic_category_series" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/iv_category_series"
android:layout_centerHorizontal="true"
android:layout_marginTop="5.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="시리즈"
android:textColor="@color/color_777777"
android:textSize="9sp"
tools:ignore="SmallSp" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/rl_category_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<ImageView
android:id="@+id/iv_category_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:contentDescription="@null"
android:src="@drawable/ic_category_content" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/iv_category_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="5.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="단편"
android:textColor="@color/color_777777"
android:textSize="9sp"
tools:ignore="SmallSp" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/rl_category_audio_book"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<ImageView
android:id="@+id/iv_category_audio_book"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:contentDescription="@null"
android:src="@drawable/ic_category_audio_book" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/iv_category_audio_book"
android:layout_centerHorizontal="true"
android:layout_marginTop="5.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="오디오북"
android:textColor="@color/color_777777"
android:textSize="9sp"
tools:ignore="SmallSp" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/rl_category_alarm"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<ImageView
android:id="@+id/iv_category_alarm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:contentDescription="@null"
android:src="@drawable/ic_category_alarm" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/iv_category_alarm"
android:layout_centerHorizontal="true"
android:layout_marginTop="5.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="모닝콜"
android:textColor="@color/color_777777"
android:textSize="9sp"
tools:ignore="SmallSp" />
</RelativeLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"
android:baselineAligned="false"
android:orientation="horizontal">
<RelativeLayout
android:id="@+id/rl_category_asmr"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<ImageView
android:id="@+id/iv_category_asmr"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:contentDescription="@null"
android:src="@drawable/ic_category_asmr" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/iv_category_asmr"
android:layout_centerHorizontal="true"
android:layout_marginTop="5.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="ASMR"
android:textColor="@color/color_777777"
android:textSize="9sp"
tools:ignore="SmallSp" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/rl_category_replay"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<ImageView
android:id="@+id/iv_category_replay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:contentDescription="@null"
android:src="@drawable/ic_category_replay" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/iv_category_replay"
android:layout_centerHorizontal="true"
android:layout_marginTop="5.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="다시듣기"
android:textColor="@color/color_777777"
android:textSize="9sp"
tools:ignore="SmallSp" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/rl_category_audio_toon"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<ImageView
android:id="@+id/iv_category_audio_toon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:contentDescription="@null"
android:src="@drawable/ic_category_audio_toon" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/iv_category_audio_toon"
android:layout_centerHorizontal="true"
android:layout_marginTop="5.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="오디오툰"
android:textColor="@color/color_777777"
android:textSize="9sp"
tools:ignore="SmallSp" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/rl_category_free"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
<ImageView
android:id="@+id/iv_category_free"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:contentDescription="@null"
android:src="@drawable/ic_category_free" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/iv_category_free"
android:layout_centerHorizontal="true"
android:layout_marginTop="5.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="무료"
android:textColor="@color/color_777777"
android:textSize="9sp"
tools:ignore="SmallSp" />
</RelativeLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_creator_rank"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/color_222222"
android:gravity="center"
android:orientation="vertical"
android:paddingVertical="8dp">
<TextView
android:id="@+id/tv_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:textColor="@color/color_eeeeee"
android:textSize="14.7sp"
tools:text="2023년 10월 16일~10월 22일" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:fontFamily="@font/gmarket_sans_light"
android:text="※ 인기 순위는 매주 업데이트됩니다."
android:textColor="@color/color_bbbbbb"
android:textSize="13.3sp" />
</LinearLayout>
<TextView
android:id="@+id/tv_creator_rank_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:fontFamily="@font/gmarket_sans_bold"
android:paddingHorizontal="13.3dp"
android:text="인기 크리에이터"
android:textColor="@color/color_eeeeee"
android:textSize="18.3sp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_creator_rank"
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"
android:visibility="visible">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:paddingHorizontal="13.3dp"
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_rank_content"
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_rank_content_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_rank_content_sort"
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_rank_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"
android:clipToPadding="false"
android:paddingHorizontal="6.7dp" />
</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" />
<LinearLayout
android:id="@+id/ll_creator_content_ranking"
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_ranking_creator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:clipToPadding="false"
android:paddingHorizontal="13.3dp" />
<LinearLayout
android:id="@+id/ll_sales_top2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:baselineAligned="false"
android:orientation="horizontal"
android:paddingHorizontal="13.3dp"
android:visibility="gone">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center">
<ImageView
android:id="@+id/iv_sales_1"
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="@null"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/tv_sales_title_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@color/color_d2d2d2"
android:textSize="13.3sp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center_vertical"
android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/iv_sales_creator_1"
android:layout_width="21dp"
android:layout_height="21dp"
android:contentDescription="@null" />
<TextView
android:id="@+id/tv_sales_creator_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@color/color_777777"
android:textSize="10sp"
tools:ignore="SmallSp" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_sales_2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:layout_weight="1"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center">
<ImageView
android:id="@+id/iv_sales_2"
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="@null"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/tv_sales_title_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@color/color_d2d2d2"
android:textSize="13.3sp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center_vertical"
android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/iv_sales_creator_2"
android:layout_width="21dp"
android:layout_height="21dp"
android:contentDescription="@null" />
<TextView
android:id="@+id/tv_sales_creator_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@color/color_777777"
android:textSize="10sp"
tools:ignore="SmallSp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_sales_count_top2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:baselineAligned="false"
android:orientation="horizontal"
android:paddingHorizontal="13.3dp"
android:visibility="gone">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
tools:ignore="TooManyViews">
<ImageView
android:id="@+id/iv_sales_count_1"
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="@null"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/tv_sales_count_title_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@color/color_d2d2d2"
android:textSize="13.3sp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center_vertical"
android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/iv_sales_count_creator_1"
android:layout_width="21dp"
android:layout_height="21dp"
android:contentDescription="@null" />
<TextView
android:id="@+id/tv_sales_count_creator_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@color/color_777777"
android:textSize="10sp"
tools:ignore="SmallSp" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_sales_count_2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:layout_weight="1"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center">
<ImageView
android:id="@+id/iv_sales_count_2"
android:layout_width="0dp"
android:layout_height="0dp"
android:contentDescription="@null"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/tv_sales_count_title_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@color/color_d2d2d2"
android:textSize="13.3sp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center_vertical"
android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/iv_sales_count_creator_2"
android:layout_width="21dp"
android:layout_height="21dp"
android:contentDescription="@null" />
<TextView
android:id="@+id/tv_sales_count_creator_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@color/color_777777"
android:textSize="10sp"
tools:ignore="SmallSp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<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>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="30dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="
- 회사명 : 주식회사 소다라이브\n\n
- 대표자 : 이재형\n\n
- 주소 : 경기도 성남시 분당구 황새울로335번길 10, 5층 563A호\n\n
- 사업자등록번호 : 870-81-03220\n\n
- 통신판매업신고 : 제2024-성남분당B-1012호\n\n
- 고객센터 : 02.2055.1477(이용시간 10:00~19:00)\n\n
- 대표 이메일 : sodalive.official@gmail.com"
android:textColor="@color/color_777777"
android:textSize="11sp" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<LinearLayout
android:id="@+id/ll_upload_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_marginEnd="16.7dp"
android:layout_marginBottom="16.7dp"
android:background="@drawable/bg_round_corner_44_3bb9f1"
android:orientation="horizontal"
android:padding="13.3dp">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="5dp"
android:contentDescription="@null"
android:src="@drawable/ic_thumb_play" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:text="콘텐츠 업로드"
android:textColor="@color/white"
android:textSize="13.3sp" />
</LinearLayout>
</RelativeLayout>

View File

@ -0,0 +1,45 @@
<?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="93.3dp"
android:layout_height="wrap_content">
<RelativeLayout
android:id="@+id/rl_profile"
android:layout_width="66.6dp"
android:layout_height="66.6dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/iv_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@null" />
<ImageView
android:id="@+id/iv_profile"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_centerInParent="true"
android:contentDescription="@null" />
</RelativeLayout>
<TextView
android:id="@+id/tv_nickname"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"
android:ellipsize="end"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:maxLines="1"
android:textColor="@color/color_bbbbbb"
android:textSize="11.3sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/rl_profile"
tools:text="상남자12039" />
</androidx.constraintlayout.widget.ConstraintLayout>