From c310f9c57e5c383fe3ad3e8a94a8ce05db6331cd Mon Sep 17 00:00:00 2001 From: klaus Date: Thu, 25 Apr 2024 22:05:58 +0900 Subject: [PATCH] =?UTF-8?q?=EC=8B=9C=EB=A6=AC=EC=A6=88=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4=EB=B3=B4=EA=B8=B0=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=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 | 18 +++ .../series/SeriesListAdapter.kt} | 17 ++- .../series/SeriesListAllActivity.kt | 105 ++++++++++++++++++ .../series/SeriesListAllViewModel.kt | 80 +++++++++++++ .../audio_content/series/SeriesRepository.kt | 17 +++ .../java/kr/co/vividnext/sodalive/di/AppDI.kt | 6 + .../explorer/profile/UserProfileActivity.kt | 15 ++- .../res/layout/activity_series_list_all.xml | 21 ++++ app/src/main/res/layout/detail_toolbar.xml | 2 + app/src/main/res/layout/item_series_list.xml | 4 +- 11 files changed, 276 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/SeriesApi.kt rename app/src/main/java/kr/co/vividnext/sodalive/{explorer/profile/series/CreatorChannelSeriesAdapter.kt => audio_content/series/SeriesListAdapter.kt} (88%) create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/SeriesListAllActivity.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/SeriesListAllViewModel.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/SeriesRepository.kt create mode 100644 app/src/main/res/layout/activity_series_list_all.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 01bf13f..7b668b2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -133,6 +133,7 @@ + > +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/series/CreatorChannelSeriesAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/SeriesListAdapter.kt similarity index 88% rename from app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/series/CreatorChannelSeriesAdapter.kt rename to app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/SeriesListAdapter.kt index 8f919af..430213e 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/series/CreatorChannelSeriesAdapter.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/SeriesListAdapter.kt @@ -1,4 +1,4 @@ -package kr.co.vividnext.sodalive.explorer.profile.series +package kr.co.vividnext.sodalive.audio_content.series import android.annotation.SuppressLint import android.view.LayoutInflater @@ -9,15 +9,14 @@ import coil.load import coil.transform.CircleCropTransformation import coil.transform.RoundedCornersTransformation import kr.co.vividnext.sodalive.R -import kr.co.vividnext.sodalive.audio_content.series.GetSeriesListResponse import kr.co.vividnext.sodalive.databinding.ItemSeriesListBinding import kr.co.vividnext.sodalive.extensions.dpToPx -class CreatorChannelSeriesAdapter( +class SeriesListAdapter( private val onClickItem: (Long) -> Unit, private val onClickCreator: (Long) -> Unit, private val isVisibleCreator: Boolean -) : RecyclerView.Adapter() { +) : RecyclerView.Adapter() { val items = mutableListOf() @@ -85,4 +84,14 @@ class CreatorChannelSeriesAdapter( } override fun getItemCount() = items.count() + + @SuppressLint("NotifyDataSetChanged") + fun addItems(items: List) { + this.items.addAll(items) + notifyDataSetChanged() + } + + fun clear() { + this.items.clear() + } } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/SeriesListAllActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/SeriesListAllActivity.kt new file mode 100644 index 0000000..400d24c --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/SeriesListAllActivity.kt @@ -0,0 +1,105 @@ +package kr.co.vividnext.sodalive.audio_content.series + +import android.os.Bundle +import android.widget.Toast +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +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.ActivitySeriesListAllBinding +import org.koin.android.ext.android.inject + +class SeriesListAllActivity : BaseActivity( + ActivitySeriesListAllBinding::inflate +) { + + private val viewModel: SeriesListAllViewModel by inject() + + private lateinit var loadingDialog: LoadingDialog + private lateinit var seriesAdapter: SeriesListAdapter + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val creatorId = intent.getLongExtra(Constants.EXTRA_USER_ID, 0) + if (creatorId <= 0) { + Toast.makeText(applicationContext, "잘못된 요청입니다.", Toast.LENGTH_LONG).show() + finish() + } + + bindData() + + viewModel.creatorId = creatorId + viewModel.getSeriesList() + } + + override fun setupView() { + loadingDialog = LoadingDialog(this, layoutInflater) + binding.toolbar.tvBack.text = "시리즈 전체보기" + binding.toolbar.tvBack.setOnClickListener { finish() } + + seriesAdapter = SeriesListAdapter( + onClickItem = {}, + onClickCreator = {}, + isVisibleCreator = false + ) + + val spanCount = 3 + val spacing = 40 + val recyclerView = binding.rvSeriesAll + recyclerView.layoutManager = GridLayoutManager(this, spanCount) + + recyclerView.addItemDecoration( + GridSpacingItemDecoration( + spanCount, + spacing, + 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.getSeriesList() + } + } + }) + + recyclerView.adapter = seriesAdapter + } + + private 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.seriesListLiveData.observe(this) { + if (viewModel.page - 1 == 1) { + seriesAdapter.clear() + binding.rvSeriesAll.scrollToPosition(0) + } + + seriesAdapter.addItems(it) + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/SeriesListAllViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/SeriesListAllViewModel.kt new file mode 100644 index 0000000..1f5473e --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/SeriesListAllViewModel.kt @@ -0,0 +1,80 @@ +package kr.co.vividnext.sodalive.audio_content.series + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.google.gson.annotations.SerializedName +import com.orhanobut.logger.Logger +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.schedulers.Schedulers +import kr.co.vividnext.sodalive.base.BaseViewModel +import kr.co.vividnext.sodalive.common.SharedPreferenceManager + +class SeriesListAllViewModel(private val repository: SeriesRepository) : BaseViewModel() { + + enum class SeriesSortType { + @SerializedName("NEWEST") NEWEST, + @SerializedName("POPULAR") POPULAR + } + + private val _toastLiveData = MutableLiveData() + val toastLiveData: LiveData + get() = _toastLiveData + + private var _isLoading = MutableLiveData(false) + val isLoading: LiveData + get() = _isLoading + + private var _seriesListLiveData = MutableLiveData>() + val seriesListLiveData: LiveData> + get() = _seriesListLiveData + + var creatorId = 0L + var isLast = false + var page = 1 + private val size = 10 + + fun getSeriesList() { + if (!_isLoading.value!! && !isLast) { + _isLoading.value = true + + compositeDisposable.add( + repository.getSeriesList( + creatorId = creatorId, + sortType = SeriesSortType.NEWEST, + 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()) { + _seriesListLiveData.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/SeriesRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/SeriesRepository.kt new file mode 100644 index 0000000..a9f12cb --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/series/SeriesRepository.kt @@ -0,0 +1,17 @@ +package kr.co.vividnext.sodalive.audio_content.series + +class SeriesRepository(private val api: SeriesApi) { + fun getSeriesList( + creatorId: Long, + sortType: SeriesListAllViewModel.SeriesSortType, + page: Int, + size: Int, + token: String + ) = api.getSeriesList( + creatorId = creatorId, + sortType = sortType, + page = page - 1, + size = size, + authHeader = token + ) +} 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 afa26ca..b64b5c6 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 @@ -24,6 +24,9 @@ import kr.co.vividnext.sodalive.audio_content.main.order.AudioContentMainOrderLi import kr.co.vividnext.sodalive.audio_content.main.ranking.AudioContentMainRankingViewModel 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.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.upload.AudioContentUploadViewModel import kr.co.vividnext.sodalive.audio_content.upload.theme.AudioContentThemeViewModel import kr.co.vividnext.sodalive.common.ApiBuilder @@ -161,6 +164,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) { single { ApiBuilder().build(get(), LiveApi::class.java) } single { ApiBuilder().build(get(), TermsApi::class.java) } single { ApiBuilder().build(get(), EventApi::class.java) } + single { ApiBuilder().build(get(), SeriesApi::class.java) } single { ApiBuilder().build(get(), ReportApi::class.java) } single { ApiBuilder().build(get(), LiveRecommendApi::class.java) } single { ApiBuilder().build(get(), ExplorerApi::class.java) } @@ -204,6 +208,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) { viewModel { EventViewModel(get()) } viewModel { NotificationSettingsViewModel(get()) } viewModel { SettingsViewModel(get()) } + viewModel { SeriesListAllViewModel(get()) } viewModel { TextMessageDetailViewModel(get()) } viewModel { LiveReservationStatusViewModel(get()) } viewModel { AudioContentMainBannerViewModel(get()) } @@ -242,6 +247,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) { private val repositoryModule = module { factory { UserRepository(get()) } factory { TermsRepository(get()) } + factory { SeriesRepository(get()) } factory { LiveRepository(get(), get(), get()) } factory { EventRepository(get()) } factory { LiveRecommendRepository(get()) } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/UserProfileActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/UserProfileActivity.kt index 834e0ac..949ec2a 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/UserProfileActivity.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/UserProfileActivity.kt @@ -43,7 +43,8 @@ import kr.co.vividnext.sodalive.explorer.profile.donation.UserProfileDonationAda import kr.co.vividnext.sodalive.explorer.profile.donation.UserProfileDonationAllViewActivity import kr.co.vividnext.sodalive.explorer.profile.fantalk.UserProfileFantalkAllViewActivity import kr.co.vividnext.sodalive.explorer.profile.follow.UserFollowerListActivity -import kr.co.vividnext.sodalive.explorer.profile.series.CreatorChannelSeriesAdapter +import kr.co.vividnext.sodalive.audio_content.series.SeriesListAdapter +import kr.co.vividnext.sodalive.audio_content.series.SeriesListAllActivity import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.loadUrl import kr.co.vividnext.sodalive.extensions.moneyFormat @@ -72,7 +73,7 @@ class UserProfileActivity : BaseActivity( private lateinit var loadingDialog: LoadingDialog private lateinit var liveAdapter: UserProfileLiveAdapter private lateinit var audioContentAdapter: AudioContentAdapter - private lateinit var seriesAdapter: CreatorChannelSeriesAdapter + private lateinit var seriesAdapter: SeriesListAdapter private lateinit var donationAdapter: UserProfileDonationAdapter private lateinit var cheersAdapter: UserProfileCheersAdapter @@ -420,7 +421,13 @@ class UserProfileActivity : BaseActivity( } private fun setupSeriesListView() { - binding.layoutCreatorChannelSeries.tvAll.setOnClickListener { } + binding.layoutCreatorChannelSeries.tvAll.setOnClickListener { + startActivity( + Intent(applicationContext, SeriesListAllActivity::class.java).apply { + putExtra(Constants.EXTRA_USER_ID, userId) + } + ) + } val recyclerView = binding.layoutCreatorChannelSeries.rvSeries recyclerView.layoutManager = LinearLayoutManager( @@ -429,7 +436,7 @@ class UserProfileActivity : BaseActivity( false ) - seriesAdapter = CreatorChannelSeriesAdapter( + seriesAdapter = SeriesListAdapter( onClickItem = {}, onClickCreator = {}, isVisibleCreator = false diff --git a/app/src/main/res/layout/activity_series_list_all.xml b/app/src/main/res/layout/activity_series_list_all.xml new file mode 100644 index 0000000..7b0e966 --- /dev/null +++ b/app/src/main/res/layout/activity_series_list_all.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/app/src/main/res/layout/detail_toolbar.xml b/app/src/main/res/layout/detail_toolbar.xml index 1005f86..13d80d3 100644 --- a/app/src/main/res/layout/detail_toolbar.xml +++ b/app/src/main/res/layout/detail_toolbar.xml @@ -14,6 +14,8 @@ android:layout_centerVertical="true" android:drawablePadding="6.7dp" android:fontFamily="@font/gmarket_sans_bold" + android:gravity="center" + android:minHeight="48dp" android:textColor="@color/color_eeeeee" android:textSize="18.3sp" app:drawableStartCompat="@drawable/ic_back" diff --git a/app/src/main/res/layout/item_series_list.xml b/app/src/main/res/layout/item_series_list.xml index e3f16d2..992fe6a 100644 --- a/app/src/main/res/layout/item_series_list.xml +++ b/app/src/main/res/layout/item_series_list.xml @@ -7,8 +7,8 @@