diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c3ff5e7..bd5338e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -154,6 +154,7 @@ + diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/AudioContentApi.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/AudioContentApi.kt index 6989bef..061e8c8 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/AudioContentApi.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/AudioContentApi.kt @@ -283,6 +283,13 @@ interface AudioContentApi { @Header("Authorization") authHeader: String ): Single>> + @GET("/v2/audio-content/main/series/completed-monthly-rank") + fun getCompletedSeries( + @Query("page") page: Int, + @Query("size") size: Int, + @Header("Authorization") authHeader: String + ): Single> + @GET("/v2/audio-content/main/content") fun getContentMainContent( @Header("Authorization") authHeader: String diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/AudioContentMainTabSeriesFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/AudioContentMainTabSeriesFragment.kt index 4283399..657988a 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/AudioContentMainTabSeriesFragment.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/AudioContentMainTabSeriesFragment.kt @@ -18,6 +18,7 @@ import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.audio_content.main.AudioContentBannerType import kr.co.vividnext.sodalive.audio_content.main.banner.AudioContentMainBannerAdapter import kr.co.vividnext.sodalive.audio_content.main.v2.ContentRankCreatorAdapter +import kr.co.vividnext.sodalive.audio_content.main.v2.series.completed.CompletedSeriesActivity import kr.co.vividnext.sodalive.audio_content.main.v2.series.curation.AudioContentMainSeriesCurationAdapter import kr.co.vividnext.sodalive.audio_content.main.v2.series.new_series.AudioContentMainNewSeriesAdapter import kr.co.vividnext.sodalive.audio_content.main.v2.series.origianl_audio_drama.AudioContentMainTabSeriesOriginalAudioDramaAdapter @@ -456,6 +457,15 @@ class AudioContentMainTabSeriesFragment : BaseFragment( + ActivityCompletedSeriesBinding::inflate +) { + + private val viewModel: CompletedSeriesViewModel by inject() + + private lateinit var loadingDialog: LoadingDialog + private lateinit var adapter: SeriesListAdapter + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + bindData() + + viewModel.getCompletedSeries() + } + + override fun setupView() { + loadingDialog = LoadingDialog(this, layoutInflater) + binding.toolbar.tvBack.text = "완결 시리즈" + binding.toolbar.tvBack.setOnClickListener { finish() } + + setupCompletedSeriesListView() + } + + private fun setupCompletedSeriesListView() { + val spacing = 13.3f.dpToPx().roundToInt() + + adapter = SeriesListAdapter( + itemWidth = ((screenWidth - spacing * 3) / 2f).roundToInt(), + onClickItem = { + startActivity( + Intent(applicationContext, SeriesDetailActivity::class.java).apply { + putExtra(Constants.EXTRA_SERIES_ID, it) + } + ) + }, + onClickCreator = {}, + isVisibleCreator = false + ) + + val spanCount = 2 + val recyclerView = binding.rvSeries + recyclerView.layoutManager = GridLayoutManager(this, spanCount) + + recyclerView.addItemDecoration( + DifferentSpacingItemDecoration( + spanCount = spanCount, + horizontalSpacing = spacing, + verticalSpacing = spacing, + includeEdge = true + ) + ) + + recyclerView.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.getCompletedSeries() + } + } + }) + + recyclerView.adapter = adapter + } + + private fun bindData() { + viewModel.toastLiveData.observe(this) { + it?.let { showToast(it) } + } + + viewModel.isLoading.observe(this) { + if (it) { + loadingDialog.show(screenWidth) + } else { + loadingDialog.dismiss() + } + } + + viewModel.completedSeriesLiveData.observe(this) { + adapter.addItems(it) + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/completed/CompletedSeriesViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/completed/CompletedSeriesViewModel.kt new file mode 100644 index 0000000..f59fd80 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/series/completed/CompletedSeriesViewModel.kt @@ -0,0 +1,75 @@ +package kr.co.vividnext.sodalive.audio_content.main.v2.series.completed + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.orhanobut.logger.Logger +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.schedulers.Schedulers +import kr.co.vividnext.sodalive.audio_content.main.v2.series.AudioContentMainTabSeriesRepository +import kr.co.vividnext.sodalive.audio_content.series.GetSeriesListResponse +import kr.co.vividnext.sodalive.base.BaseViewModel +import kr.co.vividnext.sodalive.common.SharedPreferenceManager + +class CompletedSeriesViewModel( + private val repository: AudioContentMainTabSeriesRepository +) : BaseViewModel() { + private val _toastLiveData = MutableLiveData() + val toastLiveData: LiveData + get() = _toastLiveData + + private var _isLoading = MutableLiveData(false) + val isLoading: LiveData + get() = _isLoading + + private var _completedSeriesLiveData = + MutableLiveData>() + val completedSeriesLiveData: LiveData> + get() = _completedSeriesLiveData + + var isLast = false + var page = 1 + private val size = 20 + + fun getCompletedSeries() { + if (!_isLoading.value!! && !isLast) { + _isLoading.value = true + + compositeDisposable.add( + repository.getCompletedSeries( + page = page, + size = size, + token = "Bearer ${SharedPreferenceManager.token}" + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + if (it.success && it.data != null) { + page += 1 + + if (it.data.items.isNotEmpty()) { + _completedSeriesLiveData.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/di/AppDI.kt b/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt index 112e705..88b053c 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 @@ -38,6 +38,7 @@ import kr.co.vividnext.sodalive.audio_content.main.v2.replay.AudioContentMainTab import kr.co.vividnext.sodalive.audio_content.main.v2.replay.AudioContentMainTabReplayViewModel import kr.co.vividnext.sodalive.audio_content.main.v2.series.AudioContentMainTabSeriesRepository import kr.co.vividnext.sodalive.audio_content.main.v2.series.AudioContentMainTabSeriesViewModel +import kr.co.vividnext.sodalive.audio_content.main.v2.series.completed.CompletedSeriesViewModel import kr.co.vividnext.sodalive.audio_content.main.v2.series.origianl_audio_drama.OriginalAudioDramaContentAllRepository import kr.co.vividnext.sodalive.audio_content.main.v2.series.origianl_audio_drama.OriginalAudioDramaContentAllViewModel import kr.co.vividnext.sodalive.audio_content.modify.AudioContentModifyViewModel @@ -316,6 +317,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) { viewModel { AudioContentMainTabFreeViewModel(get()) } viewModel { OriginalAudioDramaContentAllViewModel(get()) } viewModel { IntroduceCreatorViewModel(get()) } + viewModel { CompletedSeriesViewModel(get()) } } private val repositoryModule = module { diff --git a/app/src/main/res/layout/activity_completed_series.xml b/app/src/main/res/layout/activity_completed_series.xml new file mode 100644 index 0000000..b0f2ea1 --- /dev/null +++ b/app/src/main/res/layout/activity_completed_series.xml @@ -0,0 +1,20 @@ + + + + + + +