From 0b999a874cb79e4efe3f155020d689becee54d4b Mon Sep 17 00:00:00 2001 From: klaus Date: Sat, 27 Apr 2024 00:34:42 +0900 Subject: [PATCH] =?UTF-8?q?=EC=8B=9C=EB=A6=AC=EC=A6=88=20=EC=BD=98?= =?UTF-8?q?=ED=85=90=EC=B8=A0=20=EC=A0=84=EC=B2=B4=EB=B3=B4=EA=B8=B0=20?= =?UTF-8?q?=ED=8E=98=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 + .../audio_content/series/SeriesApi.kt | 9 ++ .../audio_content/series/SeriesRepository.kt | 12 ++ .../SeriesContentAdapter.kt} | 7 +- .../content/SeriesContentAllActivity.kt | 103 ++++++++++++++++++ .../content/SeriesContentAllViewModel.kt | 73 +++++++++++++ .../series/detail/SeriesDetailHomeFragment.kt | 14 ++- .../series/detail/SeriesDetailViewModel.kt | 2 + .../java/kr/co/vividnext/sodalive/di/AppDI.kt | 4 +- .../layout/activity_series_content_all.xml | 23 ++++ .../res/layout/activity_series_detail.xml | 33 +++--- 11 files changed, 257 insertions(+), 24 deletions(-) rename app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/{detail/SeriesDetailHomeContentAdapter.kt => content/SeriesContentAdapter.kt} (91%) create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/content/SeriesContentAllActivity.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/content/SeriesContentAllViewModel.kt create mode 100644 app/src/main/res/layout/activity_series_content_all.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 626b987..a3ecd18 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -135,6 +135,7 @@ + > + + @GET("/audio-content/series/{id}/content") + fun getSeriesContentList( + @Path("id") seriesId: Long, + @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/series/SeriesRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/SeriesRepository.kt index 332678b..6b76289 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/SeriesRepository.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/SeriesRepository.kt @@ -19,4 +19,16 @@ class SeriesRepository(private val api: SeriesApi) { seriesId = seriesId, authHeader = token ) + + fun getSeriesContentList( + seriesId: Long, + page: Int, + size: Int, + token: String + ) = api.getSeriesContentList( + seriesId = seriesId, + page = page - 1, + size = size, + authHeader = token + ) } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/detail/SeriesDetailHomeContentAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/content/SeriesContentAdapter.kt similarity index 91% rename from app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/detail/SeriesDetailHomeContentAdapter.kt rename to app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/content/SeriesContentAdapter.kt index c7eb974..69d3dac 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/detail/SeriesDetailHomeContentAdapter.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/content/SeriesContentAdapter.kt @@ -1,4 +1,4 @@ -package kr.co.vividnext.sodalive.audio_content.series.detail +package kr.co.vividnext.sodalive.audio_content.series.content import android.annotation.SuppressLint import android.view.LayoutInflater @@ -8,12 +8,13 @@ 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.detail.GetSeriesContentListItem import kr.co.vividnext.sodalive.databinding.ItemSeriesContentBinding import kr.co.vividnext.sodalive.extensions.dpToPx -class SeriesDetailHomeContentAdapter( +class SeriesContentAdapter( private val onClickItem: (Long) -> Unit -) : RecyclerView.Adapter() { +) : RecyclerView.Adapter() { val items = mutableListOf() diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/content/SeriesContentAllActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/content/SeriesContentAllActivity.kt new file mode 100644 index 0000000..0db3f2f --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/content/SeriesContentAllActivity.kt @@ -0,0 +1,103 @@ +package kr.co.vividnext.sodalive.audio_content.series.content + +import android.content.Intent +import android.os.Bundle +import android.widget.Toast +import androidx.recyclerview.widget.DividerItemDecoration +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.ActivitySeriesContentAllBinding +import org.koin.android.ext.android.inject + +class SeriesContentAllActivity : BaseActivity( + ActivitySeriesContentAllBinding::inflate +) { + private val viewModel: SeriesContentAllViewModel by inject() + + private lateinit var loadingDialog: LoadingDialog + private lateinit var adapter: SeriesContentAdapter + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val seriesId = intent.getLongExtra(Constants.EXTRA_SERIES_ID, 0) + if (seriesId <= 0) { + Toast.makeText(applicationContext, "잘못된 요청입니다.", Toast.LENGTH_LONG).show() + finish() + } + + bindData() + + viewModel.seriesId = seriesId + viewModel.getSeriesContentList() + } + + override fun setupView() { + loadingDialog = LoadingDialog(this, layoutInflater) + + binding.toolbar.tvBack.text = "콘텐츠 전체보기" + binding.toolbar.tvBack.setOnClickListener { finish() } + + adapter = SeriesContentAdapter { + startActivity( + Intent(applicationContext, AudioContentDetailActivity::class.java).apply { + putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, it) + } + ) + } + + binding.rvSeriesContentAll.layoutManager = LinearLayoutManager( + applicationContext, + LinearLayoutManager.VERTICAL, + false + ) + + binding.rvSeriesContentAll.addItemDecoration( + DividerItemDecoration( + applicationContext, + DividerItemDecoration.VERTICAL + ) + ) + + binding.rvSeriesContentAll.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.getSeriesContentList() + } + } + }) + + binding.rvSeriesContentAll.adapter = adapter + } + + fun bindData() { + viewModel.toastLiveData.observe(this) { + it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() } + } + + viewModel.isLoading.observe(this) { + if (it) { + loadingDialog.show(screenWidth, "") + } else { + loadingDialog.dismiss() + } + } + + viewModel.seriesContentListLiveData.observe(this) { + adapter.addItems(it) + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/content/SeriesContentAllViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/content/SeriesContentAllViewModel.kt new file mode 100644 index 0000000..98ae18c --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/content/SeriesContentAllViewModel.kt @@ -0,0 +1,73 @@ +package kr.co.vividnext.sodalive.audio_content.series.content + +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.series.SeriesRepository +import kr.co.vividnext.sodalive.audio_content.series.detail.GetSeriesContentListItem +import kr.co.vividnext.sodalive.base.BaseViewModel +import kr.co.vividnext.sodalive.common.SharedPreferenceManager + +class SeriesContentAllViewModel(private val repository: SeriesRepository) : BaseViewModel() { + private val _toastLiveData = MutableLiveData() + val toastLiveData: LiveData + get() = _toastLiveData + + private var _isLoading = MutableLiveData(false) + val isLoading: LiveData + get() = _isLoading + + private var _seriesContentListLiveData = MutableLiveData>() + val seriesContentListLiveData: LiveData> + get() = _seriesContentListLiveData + + var seriesId = 0L + + var page = 1 + private var pageSize = 10 + private var isLast = false + + fun getSeriesContentList() { + if (!_isLoading.value!! && !isLast) { + _isLoading.value = true + + compositeDisposable.add( + repository.getSeriesContentList( + seriesId = seriesId, + page = page, + size = pageSize, + token = "Bearer ${SharedPreferenceManager.token}" + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + if (it.success && it.data != null) { + page += 1 + + if (it.data.items.isNotEmpty()) { + _seriesContentListLiveData.value = it.data.items + } else { + isLast = true + } + } else { + if (it.message != null) { + _toastLiveData.value = it.message + } else { + _toastLiveData.value = "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." + } + } + _isLoading.value = false + }, + { + _isLoading.value = false + it.message?.let { message -> Logger.e(message) } + _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.") + } + ) + ) + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/detail/SeriesDetailHomeFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/detail/SeriesDetailHomeFragment.kt index 912f846..a2d9fab 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/detail/SeriesDetailHomeFragment.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/detail/SeriesDetailHomeFragment.kt @@ -8,6 +8,8 @@ import android.view.View import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity +import kr.co.vividnext.sodalive.audio_content.series.content.SeriesContentAdapter +import kr.co.vividnext.sodalive.audio_content.series.content.SeriesContentAllActivity import kr.co.vividnext.sodalive.base.BaseFragment import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.databinding.FragmentSeriesDetailHomeBinding @@ -17,7 +19,7 @@ class SeriesDetailHomeFragment : BaseFragment( ) { private var seriesDetailResponse: GetSeriesDetailResponse? = null - private lateinit var adapter: SeriesDetailHomeContentAdapter + private lateinit var adapter: SeriesContentAdapter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -45,9 +47,15 @@ class SeriesDetailHomeFragment : BaseFragment( @SuppressLint("SetTextI18n") private fun setContent() { binding.tvTotalCount.text = "(${seriesDetailResponse!!.contentCount})" - binding.llContentAll.setOnClickListener {} + binding.llContentAll.setOnClickListener { + startActivity( + Intent(requireActivity(), SeriesContentAllActivity::class.java).apply { + putExtra(Constants.EXTRA_SERIES_ID, seriesDetailResponse!!.seriesId) + } + ) + } - adapter = SeriesDetailHomeContentAdapter { + adapter = SeriesContentAdapter { startActivity( Intent(requireActivity(), AudioContentDetailActivity::class.java).apply { putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, it) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/detail/SeriesDetailViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/detail/SeriesDetailViewModel.kt index f448452..52543da 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/detail/SeriesDetailViewModel.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/detail/SeriesDetailViewModel.kt @@ -27,6 +27,8 @@ class SeriesDetailViewModel(private val repository: SeriesRepository) : BaseView lateinit var seriesDetailResponse: GetSeriesDetailResponse fun getSeriesDetail() { + _isLoading.value = true + compositeDisposable.add( repository.getSeriesDetail( seriesId = seriesId, 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 257d645..d2b7471 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 @@ -27,6 +27,7 @@ import kr.co.vividnext.sodalive.audio_content.order.AudioContentOrderListViewMod import kr.co.vividnext.sodalive.audio_content.series.SeriesApi import kr.co.vividnext.sodalive.audio_content.series.SeriesListAllViewModel import kr.co.vividnext.sodalive.audio_content.series.SeriesRepository +import kr.co.vividnext.sodalive.audio_content.series.content.SeriesContentAllViewModel import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailViewModel import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadViewModel import kr.co.vividnext.sodalive.audio_content.upload.theme.AudioContentThemeViewModel @@ -204,13 +205,14 @@ class AppDI(private val context: Context, isDebugMode: Boolean) { viewModel { VoiceMessageViewModel(get()) } viewModel { VoiceMessageWriteViewModel(get()) } viewModel { SelectMessageRecipientViewModel(get(), get()) } - viewModel { SignOutViewModel(get()) } viewModel { NoticeViewModel(get()) } viewModel { EventViewModel(get()) } viewModel { NotificationSettingsViewModel(get()) } viewModel { SettingsViewModel(get()) } viewModel { SeriesDetailViewModel(get()) } viewModel { SeriesListAllViewModel(get()) } + viewModel { SeriesContentAllViewModel(get()) } + viewModel { SignOutViewModel(get()) } viewModel { TextMessageDetailViewModel(get()) } viewModel { LiveReservationStatusViewModel(get()) } viewModel { AudioContentMainBannerViewModel(get()) } diff --git a/app/src/main/res/layout/activity_series_content_all.xml b/app/src/main/res/layout/activity_series_content_all.xml new file mode 100644 index 0000000..0e91873 --- /dev/null +++ b/app/src/main/res/layout/activity_series_content_all.xml @@ -0,0 +1,23 @@ + + + + + + + diff --git a/app/src/main/res/layout/activity_series_detail.xml b/app/src/main/res/layout/activity_series_detail.xml index 6cceae7..63dc982 100644 --- a/app/src/main/res/layout/activity_series_detail.xml +++ b/app/src/main/res/layout/activity_series_detail.xml @@ -15,23 +15,6 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - - - - - + + + +