From e6b8e559668ca0bf1487515c16c0e8a26ca8231b Mon Sep 17 00:00:00 2001 From: klaus Date: Sun, 15 Oct 2023 04:38:07 +0900 Subject: [PATCH] =?UTF-8?q?=EC=9D=B8=EA=B8=B0=20=EC=BD=98=ED=85=90?= =?UTF-8?q?=EC=B8=A0=20=EC=A0=84=EC=B2=B4=20=EB=B3=B4=EA=B8=B0=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 1 + .../sodalive/audio_content/AudioContentApi.kt | 8 ++ .../audio_content/AudioContentRepository.kt | 10 ++ .../all/AudioContentRankingAllActivity.kt | 131 ++++++++++++++++++ .../all/AudioContentRankingAllAdapter.kt | 83 +++++++++++ .../all/AudioContentRankingAllViewModel.kt | 81 +++++++++++ .../main/AudioContentMainFragment.kt | 5 +- .../java/kr/co/vividnext/sodalive/di/AppDI.kt | 2 + .../drawable/bg_round_corner_2_6_cf5c37.xml | 8 ++ .../activity_audio_content_ranking_all.xml | 46 ++++++ .../layout/item_audio_content_ranking_all.xml | 114 +++++++++++++++ app/src/main/res/values/colors.xml | 1 + 12 files changed, 489 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/all/AudioContentRankingAllActivity.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/all/AudioContentRankingAllAdapter.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/all/AudioContentRankingAllViewModel.kt create mode 100644 app/src/main/res/drawable/bg_round_corner_2_6_cf5c37.xml create mode 100644 app/src/main/res/layout/activity_audio_content_ranking_all.xml create mode 100644 app/src/main/res/layout/item_audio_content_ranking_all.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 77904e5..5b6a208 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -117,6 +117,7 @@ + >> + + @GET("/audio-content/ranking") + fun getContentRanking( + @Query("page") page: Int, + @Query("size") size: Int, + @Header("Authorization") authHeader: String + ): Single> } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/AudioContentRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/AudioContentRepository.kt index 89df5f7..46e4a92 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/AudioContentRepository.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/AudioContentRepository.kt @@ -163,4 +163,14 @@ class AudioContentRepository( ), authHeader = token ) + + fun getContentRanking( + page: Int, + size: Int, + token: String + ) = api.getContentRanking( + page = page - 1, + size = size, + authHeader = token + ) } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/all/AudioContentRankingAllActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/all/AudioContentRankingAllActivity.kt new file mode 100644 index 0000000..c7bce6e --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/all/AudioContentRankingAllActivity.kt @@ -0,0 +1,131 @@ +package kr.co.vividnext.sodalive.audio_content.all + +import android.annotation.SuppressLint +import android.content.Intent +import android.graphics.Rect +import android.os.Bundle +import android.view.View +import android.widget.Toast +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity +import kr.co.vividnext.sodalive.base.BaseActivity +import kr.co.vividnext.sodalive.common.Constants +import kr.co.vividnext.sodalive.common.LoadingDialog +import kr.co.vividnext.sodalive.databinding.ActivityAudioContentRankingAllBinding +import kr.co.vividnext.sodalive.extensions.dpToPx +import org.koin.android.ext.android.inject + +class AudioContentRankingAllActivity : BaseActivity( + ActivityAudioContentRankingAllBinding::inflate +) { + private val viewModel: AudioContentRankingAllViewModel by inject() + + private lateinit var loadingDialog: LoadingDialog + private lateinit var adapter: AudioContentRankingAllAdapter + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + bindData() + viewModel.getAudioContentRanking() + } + + override fun setupView() { + loadingDialog = LoadingDialog(this, layoutInflater) + binding.toolbar.tvBack.setOnClickListener { finish() } + binding.toolbar.tvBack.text = "인기 콘텐츠" + + adapter = AudioContentRankingAllAdapter { + val intent = Intent(applicationContext, AudioContentDetailActivity::class.java) + .apply { + putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, it) + } + startActivity(intent) + } + + binding.rvContentRanking.layoutManager = LinearLayoutManager( + applicationContext, + LinearLayoutManager.VERTICAL, + false + ) + + binding.rvContentRanking.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + + outRect.left = 13.3f.dpToPx().toInt() + outRect.right = 13.3f.dpToPx().toInt() + + when (parent.getChildAdapterPosition(view)) { + 0 -> { + outRect.top = 0 + outRect.bottom = 10f.dpToPx().toInt() + } + + adapter.itemCount - 1 -> { + outRect.top = 10f.dpToPx().toInt() + outRect.bottom = 0 + } + + else -> { + outRect.top = 10f.dpToPx().toInt() + outRect.bottom = 10f.dpToPx().toInt() + } + } + } + }) + + binding.rvContentRanking.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + + val lastVisibleItemPosition = (recyclerView.layoutManager as LinearLayoutManager?)!! + .findLastCompletelyVisibleItemPosition() + val itemTotalCount = recyclerView.adapter!!.itemCount - 1 + + // 스크롤이 끝에 도달했는지 확인 + if (!recyclerView.canScrollVertically(1) && + lastVisibleItemPosition == itemTotalCount + ) { + viewModel.getAudioContentRanking() + } + } + }) + + binding.rvContentRanking.adapter = adapter + } + + @SuppressLint("NotifyDataSetChanged") + private fun bindData() { + viewModel.isLoading.observe(this) { + if (it) { + loadingDialog.show(screenWidth) + } else { + loadingDialog.dismiss() + } + } + + viewModel.toastLiveData.observe(this) { + it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() } + } + + viewModel.dateStringLiveData.observe(this) { + binding.tvDate.text = it + } + + viewModel.contentRankingItemsLiveData.observe(this) { + if (viewModel.page == 0) { + adapter.items.clear() + } + + adapter.items.addAll(it) + adapter.notifyDataSetChanged() + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/all/AudioContentRankingAllAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/all/AudioContentRankingAllAdapter.kt new file mode 100644 index 0000000..ea9ff40 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/all/AudioContentRankingAllAdapter.kt @@ -0,0 +1,83 @@ +package kr.co.vividnext.sodalive.audio_content.all + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.content.ContextCompat +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.main.GetAudioContentRankingItem +import kr.co.vividnext.sodalive.databinding.ItemAudioContentRankingAllBinding +import kr.co.vividnext.sodalive.extensions.dpToPx +import kr.co.vividnext.sodalive.extensions.moneyFormat + +class AudioContentRankingAllAdapter( + private val onItemClick: (Long) -> Unit +) : RecyclerView.Adapter() { + + inner class ViewHolder( + private val context: Context, + private val binding: ItemAudioContentRankingAllBinding + ) : RecyclerView.ViewHolder(binding.root) { + fun bind(item: GetAudioContentRankingItem, index: Int) { + binding.root.setOnClickListener { onItemClick(item.contentId) } + binding.ivCover.load(item.coverImageUrl) { + crossfade(true) + placeholder(R.drawable.bg_placeholder) + transformations(RoundedCornersTransformation(5.3f.dpToPx())) + } + + binding.tvTitle.text = item.title + binding.tvRank.text = index.plus(1).toString() + binding.tvTheme.text = item.themeStr + binding.tvDuration.text = item.duration + binding.tvNickname.text = item.creatorNickname + + if (item.price < 1) { + binding.tvPrice.text = "무료" + binding.tvPrice.setTextColor(ContextCompat.getColor(context, R.color.white)) + binding.tvPrice.setCompoundDrawables(null, null, null, null) + binding.tvPrice.setPadding( + 5.3f.dpToPx().toInt(), + 2.7f.dpToPx().toInt(), + 5.3f.dpToPx().toInt(), + 2.7f.dpToPx().toInt() + ) + binding.tvPrice.setBackgroundResource(R.drawable.bg_round_corner_2_6_cf5c37) + } else { + binding.tvPrice.text = item.price.moneyFormat() + binding.tvPrice.setTextColor(ContextCompat.getColor(context, R.color.color_909090)) + binding.tvPrice.setCompoundDrawablesWithIntrinsicBounds( + R.drawable.ic_can, + 0, + 0, + 0 + ) + binding.tvPrice.setPadding(0, 0, 0, 0) + binding.tvPrice.setBackgroundResource(0) + } + } + } + + val items = mutableListOf() + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ) = ViewHolder( + parent.context, + ItemAudioContentRankingAllBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + + override fun getItemCount() = items.size + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.bind(items[position], position) + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/all/AudioContentRankingAllViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/all/AudioContentRankingAllViewModel.kt new file mode 100644 index 0000000..b401fa1 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/all/AudioContentRankingAllViewModel.kt @@ -0,0 +1,81 @@ +package kr.co.vividnext.sodalive.audio_content.all + +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.GetAudioContentRankingItem +import kr.co.vividnext.sodalive.base.BaseViewModel +import kr.co.vividnext.sodalive.common.SharedPreferenceManager + +class AudioContentRankingAllViewModel( + private val repository: AudioContentRepository +) : BaseViewModel() { + private val _toastLiveData = MutableLiveData() + val toastLiveData: LiveData + get() = _toastLiveData + + private var _isLoading = MutableLiveData(false) + val isLoading: LiveData + get() = _isLoading + + private var _dateStringLiveData = MutableLiveData() + val dateStringLiveData: LiveData + get() = _dateStringLiveData + + private var _contentRankingItemsLiveData = MutableLiveData>() + val contentRankingItemsLiveData: LiveData> + get() = _contentRankingItemsLiveData + + var page = 1 + private var pageSize = 10 + private var isLast = false + + fun getAudioContentRanking() { + if (!_isLoading.value!! && !isLast) { + _isLoading.value = true + compositeDisposable.add( + repository.getContentRanking( + page = page, + size = pageSize, + token = "Bearer ${SharedPreferenceManager.token}" + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + if (it.success && it.data != null) { + _isLoading.value = false + _dateStringLiveData.value = + "${it.data.startDate}~${it.data.endDate}" + + if (it.data.items.isNotEmpty()) { + page += 1 + isLast = false + _contentRankingItemsLiveData.value = it.data.items + } else { + isLast = true + _contentRankingItemsLiveData.value = listOf() + } + } else { + _isLoading.value = false + if (it.message != null) { + _toastLiveData.postValue(it.message) + } else { + _toastLiveData.postValue( + "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." + ) + } + } + }, + { + it.message?.let { message -> Logger.e(message) } + _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.") + } + ) + ) + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/AudioContentMainFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/AudioContentMainFragment.kt index 3817dce..5914a34 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/AudioContentMainFragment.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/AudioContentMainFragment.kt @@ -20,6 +20,7 @@ import com.zhpan.indicator.enums.IndicatorStyle import kr.co.pointclick.sdk.offerwall.core.PointClickAd import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.audio_content.all.AudioContentNewAllActivity +import kr.co.vividnext.sodalive.audio_content.all.AudioContentRankingAllActivity import kr.co.vividnext.sodalive.audio_content.curation.AudioContentCurationActivity import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity import kr.co.vividnext.sodalive.audio_content.order.AudioContentOrderListActivity @@ -367,7 +368,9 @@ class AudioContentMainFragment : BaseFragment( } private fun setupContentRanking() { - binding.ivContentRankingAll.setOnClickListener {} + binding.ivContentRankingAll.setOnClickListener { + startActivity(Intent(requireContext(), AudioContentRankingAllActivity::class.java)) + } contentRankingAdapter = AudioContentMainRankingAdapter { startActivity( diff --git a/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt b/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt index 8c53f3b..1bad69f 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt @@ -8,6 +8,7 @@ import kr.co.vividnext.sodalive.audio_content.AudioContentRepository import kr.co.vividnext.sodalive.audio_content.AudioContentViewModel import kr.co.vividnext.sodalive.audio_content.PlaybackTrackingRepository import kr.co.vividnext.sodalive.audio_content.all.AudioContentNewAllViewModel +import kr.co.vividnext.sodalive.audio_content.all.AudioContentRankingAllViewModel import kr.co.vividnext.sodalive.audio_content.comment.AudioContentCommentListViewModel import kr.co.vividnext.sodalive.audio_content.comment.AudioContentCommentReplyViewModel import kr.co.vividnext.sodalive.audio_content.comment.AudioContentCommentRepository @@ -195,6 +196,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) { viewModel { UserProfileDonationAllViewModel(get(), get()) } viewModel { AudioContentCurationViewModel(get()) } viewModel { AudioContentNewAllViewModel(get()) } + viewModel { AudioContentRankingAllViewModel(get()) } } private val repositoryModule = module { diff --git a/app/src/main/res/drawable/bg_round_corner_2_6_cf5c37.xml b/app/src/main/res/drawable/bg_round_corner_2_6_cf5c37.xml new file mode 100644 index 0000000..2242a06 --- /dev/null +++ b/app/src/main/res/drawable/bg_round_corner_2_6_cf5c37.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/app/src/main/res/layout/activity_audio_content_ranking_all.xml b/app/src/main/res/layout/activity_audio_content_ranking_all.xml new file mode 100644 index 0000000..0ad77b6 --- /dev/null +++ b/app/src/main/res/layout/activity_audio_content_ranking_all.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_audio_content_ranking_all.xml b/app/src/main/res/layout/item_audio_content_ranking_all.xml new file mode 100644 index 0000000..a78c106 --- /dev/null +++ b/app/src/main/res/layout/item_audio_content_ranking_all.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index fe7b819..0b5e8b5 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -100,4 +100,5 @@ #13181B #3BB9F1 #2E6279 + #CF5C37