콘텐츠 메인 - 시리즈 탭

- 오리지널 오디오 드라마 전체보기 페이지 추가
This commit is contained in:
klaus 2025-02-14 05:22:08 +09:00
parent cc10bce487
commit e1028ada43
9 changed files with 332 additions and 101 deletions

View File

@ -86,7 +86,6 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".main.MainActivity" /> <activity android:name=".main.MainActivity" />
<activity android:name=".audio_content.main.v2.AudioContentMainActivity" />
<activity android:name=".user.login.LoginActivity" /> <activity android:name=".user.login.LoginActivity" />
<activity android:name=".user.signup.SignUpActivity" /> <activity android:name=".user.signup.SignUpActivity" />
<activity android:name=".settings.terms.TermsActivity" /> <activity android:name=".settings.terms.TermsActivity" />
@ -153,6 +152,9 @@
<activity android:name=".audition.detail.AuditionDetailActivity" /> <activity android:name=".audition.detail.AuditionDetailActivity" />
<activity android:name=".audition.role.AuditionRoleDetailActivity" /> <activity android:name=".audition.role.AuditionRoleDetailActivity" />
<activity android:name=".audio_content.main.v2.AudioContentMainActivity" />
<activity android:name=".audio_content.main.v2.series.origianl_audio_drama.OriginalAudioDramaContentAllActivity" />
<activity android:name=".mypage.alarm.AlarmListActivity" /> <activity android:name=".mypage.alarm.AlarmListActivity" />
<activity android:name=".mypage.alarm.AddAlarmActivity" /> <activity android:name=".mypage.alarm.AddAlarmActivity" />
<activity android:name=".mypage.alarm.select_audio_content.AlarmSelectAudioContentActivity" /> <activity android:name=".mypage.alarm.select_audio_content.AlarmSelectAudioContentActivity" />

View File

@ -27,6 +27,7 @@ import kr.co.vividnext.sodalive.audio_content.main.v2.series.GetContentMainTabSe
import kr.co.vividnext.sodalive.audio_content.order.GetAudioContentOrderListResponse import kr.co.vividnext.sodalive.audio_content.order.GetAudioContentOrderListResponse
import kr.co.vividnext.sodalive.audio_content.order.OrderRequest import kr.co.vividnext.sodalive.audio_content.order.OrderRequest
import kr.co.vividnext.sodalive.audio_content.player.GenerateUrlResponse import kr.co.vividnext.sodalive.audio_content.player.GenerateUrlResponse
import kr.co.vividnext.sodalive.audio_content.series.GetSeriesListResponse
import kr.co.vividnext.sodalive.audio_content.upload.theme.GetAudioContentThemeResponse import kr.co.vividnext.sodalive.audio_content.upload.theme.GetAudioContentThemeResponse
import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.explorer.profile.GetAudioContentListResponse import kr.co.vividnext.sodalive.explorer.profile.GetAudioContentListResponse
@ -262,6 +263,13 @@ interface AudioContentApi {
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String
): Single<ApiResponse<GetContentMainTabSeriesResponse>> ): Single<ApiResponse<GetContentMainTabSeriesResponse>>
@GET("/v2/audio-content/main/series/original")
fun getOriginalAudioDramaList(
@Query("page") page: Int,
@Query("size") size: Int,
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetSeriesListResponse>>
@GET("/v2/audio-content/main/content") @GET("/v2/audio-content/main/content")
fun getContentMainContent( fun getContentMainContent(
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String

View File

@ -0,0 +1,109 @@
package kr.co.vividnext.sodalive.audio_content.main.v2.series.origianl_audio_drama
import android.content.Intent
import android.os.Bundle
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.audio_content.series.SeriesListAdapter
import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailActivity
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.DifferentSpacingItemDecoration
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.ActivityOriginalAudioDramaContentAllBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import org.koin.android.ext.android.inject
import kotlin.math.roundToInt
class OriginalAudioDramaContentAllActivity :
BaseActivity<ActivityOriginalAudioDramaContentAllBinding>(
ActivityOriginalAudioDramaContentAllBinding::inflate
) {
private val viewModel: OriginalAudioDramaContentAllViewModel by inject()
private lateinit var loadingDialog: LoadingDialog
private lateinit var adapter: SeriesListAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
bindData()
viewModel.getOriginalAudioDramaList()
}
override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater)
binding.toolbar.tvBack.text = "오리지널 오디오 드라마"
binding.toolbar.tvBack.setOnClickListener { finish() }
setupOriginalAudioDramaListView()
}
private fun setupOriginalAudioDramaListView() {
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.getOriginalAudioDramaList()
}
}
})
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.originalAudioDramaLiveData.observe(this) {
adapter.addItems(it)
}
}
}

View File

@ -0,0 +1,15 @@
package kr.co.vividnext.sodalive.audio_content.main.v2.series.origianl_audio_drama
import kr.co.vividnext.sodalive.audio_content.AudioContentApi
class OriginalAudioDramaContentAllRepository(private val api: AudioContentApi) {
fun getOriginalAudioDramaList(
page: Int,
size: Int,
token: String
) = api.getOriginalAudioDramaList(
page = page - 1,
size = size,
authHeader = token
)
}

View File

@ -0,0 +1,74 @@
package kr.co.vividnext.sodalive.audio_content.main.v2.series.origianl_audio_drama
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.GetSeriesListResponse
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
class OriginalAudioDramaContentAllViewModel(
private val repository: OriginalAudioDramaContentAllRepository
) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
private var _originalAudioDramaLiveData =
MutableLiveData<List<GetSeriesListResponse.SeriesListItem>>()
val originalAudioDramaLiveData: LiveData<List<GetSeriesListResponse.SeriesListItem>>
get() = _originalAudioDramaLiveData
var isLast = false
var page = 1
private val size = 15
fun getOriginalAudioDramaList() {
if (!_isLoading.value!! && !isLast) {
_isLoading.value = true
compositeDisposable.add(
repository.getOriginalAudioDramaList(
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()) {
_originalAudioDramaLiveData.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("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}
}

View File

@ -4,7 +4,7 @@ import android.annotation.SuppressLint
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout import android.widget.LinearLayout
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.load import coil.load
import coil.transform.CircleCropTransformation import coil.transform.CircleCropTransformation
@ -27,10 +27,10 @@ class SeriesListAdapter(
) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root) {
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
fun bind(item: GetSeriesListResponse.SeriesListItem) { fun bind(item: GetSeriesListResponse.SeriesListItem) {
val lp = binding.ivCover.layoutParams as ConstraintLayout.LayoutParams val lp = binding.clCover.layoutParams as LinearLayout.LayoutParams
lp.width = itemWidth lp.width = itemWidth
lp.height = itemWidth * 432 / 306 lp.height = itemWidth * 432 / 306
binding.ivCover.layoutParams = lp binding.clCover.layoutParams = lp
binding.ivCover.load(item.coverImage) { binding.ivCover.load(item.coverImage) {
crossfade(true) crossfade(true)

View File

@ -37,6 +37,8 @@ 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.replay.AudioContentMainTabReplayViewModel
import kr.co.vividnext.sodalive.audio_content.main.v2.series.AudioContentMainTabSeriesRepository 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.AudioContentMainTabSeriesViewModel
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 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.order.AudioContentOrderListViewModel
import kr.co.vividnext.sodalive.audio_content.player.AudioContentGenerateUrlRepository import kr.co.vividnext.sodalive.audio_content.player.AudioContentGenerateUrlRepository
@ -311,6 +313,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
viewModel { AudioContentMainTabAsmrViewModel(get()) } viewModel { AudioContentMainTabAsmrViewModel(get()) }
viewModel { AudioContentMainTabReplayViewModel(get()) } viewModel { AudioContentMainTabReplayViewModel(get()) }
viewModel { AudioContentMainTabFreeViewModel(get()) } viewModel { AudioContentMainTabFreeViewModel(get()) }
viewModel { OriginalAudioDramaContentAllViewModel(get()) }
} }
private val repositoryModule = module { private val repositoryModule = module {
@ -349,6 +352,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
factory { AudioContentMainTabAsmrRepository(get()) } factory { AudioContentMainTabAsmrRepository(get()) }
factory { AudioContentMainTabReplayRepository(get()) } factory { AudioContentMainTabReplayRepository(get()) }
factory { AudioContentMainTabFreeRepository(get()) } factory { AudioContentMainTabFreeRepository(get()) }
factory { OriginalAudioDramaContentAllRepository(get()) }
} }
private val moduleList = listOf( private val moduleList = listOf(

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="@+id/toolbar"
layout="@layout/detail_toolbar" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_series"
android:layout_width="0dp"
android:layout_height="0dp"
android:clipToPadding="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,25 +1,34 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_cover"
android:layout_width="wrap_content"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<ImageView <ImageView
android:id="@+id/iv_cover" android:id="@+id/iv_cover"
android:layout_width="102dp" android:layout_width="0dp"
android:layout_height="144dp" android:layout_height="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:adjustViewBounds="true" android:adjustViewBounds="true"
android:contentDescription="@null" android:contentDescription="@null"
android:scaleType="centerCrop" android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="306:432"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_launcher_background" /> tools:src="@drawable/ic_launcher_background" />
<LinearLayout <LinearLayout
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="3.3dp"
android:layout_marginTop="3.3dp" android:layout_marginTop="3.3dp"
android:orientation="horizontal" android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="@+id/iv_cover" app:layout_constraintEnd_toEndOf="@+id/iv_cover"
@ -30,7 +39,6 @@
android:id="@+id/tv_new" android:id="@+id/tv_new"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="3.3dp"
android:background="@drawable/bg_round_corner_13_3_3bb9f1" android:background="@drawable/bg_round_corner_13_3_3bb9f1"
android:fontFamily="@font/gmarket_sans_medium" android:fontFamily="@font/gmarket_sans_medium"
android:paddingHorizontal="5.3dp" android:paddingHorizontal="5.3dp"
@ -45,7 +53,6 @@
android:id="@+id/tv_complete" android:id="@+id/tv_complete"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="3.3dp"
android:background="@drawable/bg_round_corner_13_3_002abd" android:background="@drawable/bg_round_corner_13_3_002abd"
android:fontFamily="@font/gmarket_sans_medium" android:fontFamily="@font/gmarket_sans_medium"
android:paddingHorizontal="5.3dp" android:paddingHorizontal="5.3dp"
@ -60,7 +67,6 @@
android:id="@+id/tv_popular" android:id="@+id/tv_popular"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="3.3dp"
android:background="@drawable/bg_round_corner_13_3_ec6033" android:background="@drawable/bg_round_corner_13_3_ec6033"
android:fontFamily="@font/gmarket_sans_medium" android:fontFamily="@font/gmarket_sans_medium"
android:paddingHorizontal="5.3dp" android:paddingHorizontal="5.3dp"
@ -88,30 +94,26 @@
app:layout_constraintEnd_toEndOf="@+id/iv_cover" app:layout_constraintEnd_toEndOf="@+id/iv_cover"
tools:ignore="SmallSp" tools:ignore="SmallSp"
tools:text="총 24화" /> tools:text="총 24화" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView <TextView
android:id="@+id/tv_title" android:id="@+id/tv_title"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:ellipsize="end"
android:fontFamily="@font/gmarket_sans_medium" android:fontFamily="@font/gmarket_sans_medium"
android:maxLines="2" android:maxLines="2"
android:textColor="@color/color_eeeeee" android:textColor="@color/color_eeeeee"
android:textSize="12sp" android:textSize="12sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/iv_cover"
tools:text="제목, 관심사,프로필+방장, 참여인원(어딘가..)" /> tools:text="제목, 관심사,프로필+방장, 참여인원(어딘가..)" />
<LinearLayout <LinearLayout
android:id="@+id/ll_creator" android:id="@+id/ll_creator"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:orientation="horizontal" android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_title"
tools:ignore="UseCompoundDrawables"> tools:ignore="UseCompoundDrawables">
<ImageView <ImageView
@ -137,14 +139,11 @@
<TextView <TextView
android:id="@+id/tv_published_days_of_week" android:id="@+id/tv_published_days_of_week"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:fontFamily="@font/gmarket_sans_medium" android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@color/color_777777" android:textColor="@color/color_777777"
android:textSize="11sp" android:textSize="11sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ll_creator"
tools:text="매주 수, 토요일" /> tools:text="매주 수, 토요일" />
</androidx.constraintlayout.widget.ConstraintLayout> </LinearLayout>