From 2573a50190d0aa63042417222bf83e4fb4f15295 Mon Sep 17 00:00:00 2001 From: klaus Date: Sat, 15 Feb 2025 00:43:01 +0900 Subject: [PATCH] =?UTF-8?q?=EC=83=88=EB=A1=9C=EC=9A=B4=20=EC=95=8C?= =?UTF-8?q?=EB=9E=8C=20=EC=A0=84=EC=B2=B4=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 | 3 +- .../sodalive/audio_content/AudioContentApi.kt | 8 + .../AudioContentMainNewContentThemeAdapter.kt | 3 +- .../alarm/AudioContentMainTabAlarmFragment.kt | 14 +- .../AudioContentMainTabAlarmRepository.kt | 11 ++ .../AudioContentMainTabAlarmViewModel.kt | 46 ++++- .../v2/alarm/all/AlarmContentAllActivity.kt | 158 ++++++++++++++++++ .../v2/alarm/all/AlarmContentAllViewModel.kt | 87 ++++++++++ .../java/kr/co/vividnext/sodalive/di/AppDI.kt | 2 + .../res/layout/activity_alarm_content_all.xml | 36 ++++ 10 files changed, 354 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/alarm/all/AlarmContentAllActivity.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/alarm/all/AlarmContentAllViewModel.kt create mode 100644 app/src/main/res/layout/activity_alarm_content_all.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index bd5338e..aeaf415 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -153,9 +153,10 @@ + + - 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 061e8c8..d9dd0f3 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 @@ -320,6 +320,14 @@ interface AudioContentApi { @Header("Authorization") authHeader: String ): Single> + @GET("/v2/audio-content/main/alarm/all") + fun getContentMainAlarmAll( + @Query("theme") theme: String, + @Query("page") page: Int, + @Query("size") size: Int, + @Header("Authorization") authHeader: String + ): Single>> + @GET("/v2/audio-content/main/asmr") fun getContentMainAsmr( @Header("Authorization") authHeader: String diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/new_content/AudioContentMainNewContentThemeAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/new_content/AudioContentMainNewContentThemeAdapter.kt index c0aa457..feb56c2 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/new_content/AudioContentMainNewContentThemeAdapter.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/new_content/AudioContentMainNewContentThemeAdapter.kt @@ -48,8 +48,7 @@ class AudioContentMainNewContentThemeAdapter( } @SuppressLint("NotifyDataSetChanged") - fun addItems(themeList: List, selectedTheme: String = "") { - this.selectedTheme = selectedTheme + fun addItems(themeList: List) { this.themeList.clear() this.themeList.addAll(themeList) notifyDataSetChanged() diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/alarm/AudioContentMainTabAlarmFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/alarm/AudioContentMainTabAlarmFragment.kt index 71d3304..e4ed85f 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/alarm/AudioContentMainTabAlarmFragment.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/alarm/AudioContentMainTabAlarmFragment.kt @@ -16,13 +16,13 @@ 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.AudioContentNewAllActivity 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.AudioContentMainContentAdapter 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.v2.AudioContentMainContentCurationAdapter +import kr.co.vividnext.sodalive.audio_content.main.v2.alarm.all.AlarmContentAllActivity import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailActivity import kr.co.vividnext.sodalive.base.BaseFragment import kr.co.vividnext.sodalive.common.Constants @@ -150,6 +150,7 @@ class AudioContentMainTabAlarmFragment : BaseFragment Logger.e(message) } + _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.") + } + ) + ) + } } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/alarm/all/AlarmContentAllActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/alarm/all/AlarmContentAllActivity.kt new file mode 100644 index 0000000..cba5a00 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/alarm/all/AlarmContentAllActivity.kt @@ -0,0 +1,158 @@ +package kr.co.vividnext.sodalive.audio_content.main.v2.alarm.all + +import android.content.Intent +import android.graphics.Rect +import android.os.Bundle +import android.view.View +import android.widget.Toast +import androidx.annotation.OptIn +import androidx.media3.common.util.UnstableApi +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import kr.co.vividnext.sodalive.audio_content.all.AudioContentNewAllAdapter +import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity +import kr.co.vividnext.sodalive.audio_content.main.new_content.AudioContentMainNewContentThemeAdapter +import kr.co.vividnext.sodalive.base.BaseActivity +import kr.co.vividnext.sodalive.common.Constants +import kr.co.vividnext.sodalive.common.GridSpacingItemDecoration +import kr.co.vividnext.sodalive.common.LoadingDialog +import kr.co.vividnext.sodalive.databinding.ActivityAlarmContentAllBinding +import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity +import kr.co.vividnext.sodalive.extensions.dpToPx +import org.koin.android.ext.android.inject + +@OptIn(UnstableApi::class) +class AlarmContentAllActivity : BaseActivity( + ActivityAlarmContentAllBinding::inflate +) { + private val viewModel: AlarmContentAllViewModel by inject() + + private lateinit var loadingDialog: LoadingDialog + + private lateinit var newContentThemeAdapter: AudioContentMainNewContentThemeAdapter + private lateinit var newContentAdapter: AudioContentNewAllAdapter + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + bindData() + viewModel.getContentMainAlarmAll() + + newContentThemeAdapter.addItems(listOf("전체", "모닝콜", "슬립콜", "알람")) + } + + override fun setupView() { + loadingDialog = LoadingDialog(this, layoutInflater) + binding.toolbar.tvBack.text = "새로운 알람" + binding.toolbar.tvBack.setOnClickListener { finish() } + + setupNewContentTheme() + setupNewContent() + } + + private fun setupNewContentTheme() { + newContentThemeAdapter = AudioContentMainNewContentThemeAdapter { + newContentAdapter.clear() + viewModel.selectTheme(it) + } + + binding.rvNewContentTheme.layoutManager = LinearLayoutManager( + this, + LinearLayoutManager.HORIZONTAL, + false + ) + + binding.rvNewContentTheme.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() + } + + newContentThemeAdapter.itemCount - 1 -> { + outRect.left = 4f.dpToPx().toInt() + outRect.right = 0 + } + + else -> { + outRect.left = 4f.dpToPx().toInt() + outRect.right = 4f.dpToPx().toInt() + } + } + } + }) + + binding.rvNewContentTheme.adapter = newContentThemeAdapter + } + + private fun setupNewContent() { + val spanCount = 3 + val spacing = 40 + newContentAdapter = AudioContentNewAllAdapter( + itemWidth = (screenWidth - (spacing * (spanCount + 1))) / 3, + onClickItem = { + startActivity( + Intent(this, AudioContentDetailActivity::class.java).apply { + putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, it) + } + ) + }, + onClickCreator = { + startActivity( + Intent(this, UserProfileActivity::class.java).apply { + putExtra(Constants.EXTRA_USER_ID, it) + } + ) + } + ) + + binding.rvContent.layoutManager = GridLayoutManager(this, spanCount) + binding.rvContent.addItemDecoration(GridSpacingItemDecoration(spanCount, spacing, true)) + + binding.rvContent.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.getContentMainAlarmAll() + } + } + }) + + binding.rvContent.adapter = newContentAdapter + } + + 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.newContentListLiveData.observe(this) { + newContentAdapter.addItems(it) + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/alarm/all/AlarmContentAllViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/alarm/all/AlarmContentAllViewModel.kt new file mode 100644 index 0000000..c743dcc --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/v2/alarm/all/AlarmContentAllViewModel.kt @@ -0,0 +1,87 @@ +package kr.co.vividnext.sodalive.audio_content.main.v2.alarm.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.main.GetAudioContentMainItem +import kr.co.vividnext.sodalive.audio_content.main.v2.alarm.AudioContentMainTabAlarmRepository +import kr.co.vividnext.sodalive.base.BaseViewModel +import kr.co.vividnext.sodalive.common.SharedPreferenceManager + +class AlarmContentAllViewModel( + private val repository: AudioContentMainTabAlarmRepository +) : BaseViewModel() { + private val _toastLiveData = MutableLiveData() + val toastLiveData: LiveData + get() = _toastLiveData + + private var _isLoading = MutableLiveData(false) + val isLoading: LiveData + get() = _isLoading + + private var _newContentListLiveData = MutableLiveData>() + val newContentListLiveData: LiveData> + get() = _newContentListLiveData + + private var isLast = false + private var page = 1 + private val size = 10 + private var selectedTheme = "" + + fun getContentMainAlarmAll() { + if (!_isLoading.value!! && !isLast) { + _isLoading.value = true + + compositeDisposable.add( + repository.getContentMainAlarmAll( + theme = if (selectedTheme == "전체") { + "" + } else { + selectedTheme + }, + page = page, + size = size, + token = "Bearer ${SharedPreferenceManager.token}" + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + if (it.success && it.data != null) { + if (it.data.isNotEmpty()) { + page += 1 + _newContentListLiveData.value = it.data!! + } else { + isLast = true + } + } 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 selectTheme(theme: String) { + isLast = false + page = 1 + selectedTheme = theme + getContentMainAlarmAll() + } +} 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 88b053c..d097972 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 @@ -25,6 +25,7 @@ import kr.co.vividnext.sodalive.audio_content.main.ranking.AudioContentMainRanki import kr.co.vividnext.sodalive.audio_content.main.recommend_series.AudioContentMainRecommendSeriesViewModel import kr.co.vividnext.sodalive.audio_content.main.v2.alarm.AudioContentMainTabAlarmRepository import kr.co.vividnext.sodalive.audio_content.main.v2.alarm.AudioContentMainTabAlarmViewModel +import kr.co.vividnext.sodalive.audio_content.main.v2.alarm.all.AlarmContentAllViewModel import kr.co.vividnext.sodalive.audio_content.main.v2.asmr.AudioContentMainTabAsmrRepository import kr.co.vividnext.sodalive.audio_content.main.v2.asmr.AudioContentMainTabAsmrViewModel import kr.co.vividnext.sodalive.audio_content.main.v2.content.AudioContentMainTabContentRepository @@ -318,6 +319,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) { viewModel { OriginalAudioDramaContentAllViewModel(get()) } viewModel { IntroduceCreatorViewModel(get()) } viewModel { CompletedSeriesViewModel(get()) } + viewModel { AlarmContentAllViewModel(get()) } } private val repositoryModule = module { diff --git a/app/src/main/res/layout/activity_alarm_content_all.xml b/app/src/main/res/layout/activity_alarm_content_all.xml new file mode 100644 index 0000000..18e0822 --- /dev/null +++ b/app/src/main/res/layout/activity_alarm_content_all.xml @@ -0,0 +1,36 @@ + + + + + + + + + + +