diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b95e2f3..7354621 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -128,6 +128,7 @@
+
>>
+ @GET("/audio-content/theme/{id}/content")
+ fun getAudioContentByTheme(
+ @Path("id") id: Long,
+ @Query("page") page: Int,
+ @Query("size") size: Int,
+ @Query("sort-type") sort: AudioContentViewModel.Sort,
+ @Header("Authorization") authHeader: String
+ ): Single>
+
@POST("/audio-content")
@Multipart
fun uploadAudioContent(
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/AudioContentRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/AudioContentRepository.kt
index 278e5da..e8c7e22 100644
--- a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/AudioContentRepository.kt
+++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/AudioContentRepository.kt
@@ -206,4 +206,18 @@ class AudioContentRepository(
creatorId: Long,
token: String
) = categoryApi.getCategoryList(creatorId, authHeader = token)
+
+ fun getAudioContentByTheme(
+ themeId: Long,
+ page: Int,
+ size: Int,
+ sort: AudioContentViewModel.Sort = AudioContentViewModel.Sort.NEWEST,
+ token: String
+ ) = api.getAudioContentByTheme(
+ id = themeId,
+ page = page - 1,
+ size = size,
+ sort = sort,
+ authHeader = token
+ )
}
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/all/by_theme/AudioContentAllByThemeActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/all/by_theme/AudioContentAllByThemeActivity.kt
new file mode 100644
index 0000000..4b284a0
--- /dev/null
+++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/all/by_theme/AudioContentAllByThemeActivity.kt
@@ -0,0 +1,191 @@
+package kr.co.vividnext.sodalive.audio_content.all.by_theme
+
+import android.content.Intent
+import android.graphics.Rect
+import android.os.Bundle
+import android.view.View
+import android.widget.TextView
+import android.widget.Toast
+import androidx.core.content.ContextCompat
+import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import kr.co.vividnext.sodalive.R
+import kr.co.vividnext.sodalive.audio_content.AudioContentViewModel
+import kr.co.vividnext.sodalive.audio_content.all.AudioContentNewAllAdapter
+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.ActivityAudioContentAllByThemeBinding
+import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
+import kr.co.vividnext.sodalive.extensions.dpToPx
+import org.koin.android.ext.android.inject
+
+class AudioContentAllByThemeActivity : BaseActivity(
+ ActivityAudioContentAllByThemeBinding::inflate
+) {
+ private val viewModel: AudioContentAllByThemeViewModel by inject()
+
+ private lateinit var loadingDialog: LoadingDialog
+ private lateinit var adapter: AudioContentNewAllAdapter
+
+ private var themeId: Long = 0
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ themeId = intent.getLongExtra(Constants.EXTRA_THEME_ID, 0)
+
+ if (themeId <= 0) {
+ Toast.makeText(
+ applicationContext,
+ "잘못된 요청입니다.\n다시 시도해 주세요.",
+ Toast.LENGTH_LONG
+ ).show()
+
+ finish()
+ }
+
+ super.onCreate(savedInstanceState)
+
+ bindData()
+ viewModel.getContentList(themeId = themeId)
+ }
+
+ override fun setupView() {
+ loadingDialog = LoadingDialog(this, layoutInflater)
+ binding.toolbar.tvBack.setOnClickListener { finish() }
+
+ adapter = AudioContentNewAllAdapter(
+ itemWidth = (screenWidth - 54f.dpToPx().toInt()) / 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.rvContentAll.layoutManager = GridLayoutManager(this, 3)
+
+ binding.rvContentAll.addItemDecoration(object : RecyclerView.ItemDecoration() {
+ override fun getItemOffsets(
+ outRect: Rect,
+ view: View,
+ parent: RecyclerView,
+ state: RecyclerView.State
+ ) {
+ super.getItemOffsets(outRect, view, parent, state)
+
+ outRect.top = 6.7f.dpToPx().toInt()
+ outRect.bottom = 6.7f.dpToPx().toInt()
+ outRect.left = 6.7f.dpToPx().toInt()
+ outRect.right = 6.7f.dpToPx().toInt()
+ }
+ })
+
+ binding.rvContentAll.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.getContentList(themeId = themeId)
+ }
+ }
+ })
+
+ binding.rvContentAll.adapter = adapter
+
+ binding.tvSortNewest.setOnClickListener {
+ viewModel.changeSort(AudioContentViewModel.Sort.NEWEST)
+ }
+
+ binding.tvSortPriceLow.setOnClickListener {
+ viewModel.changeSort(AudioContentViewModel.Sort.PRICE_LOW)
+ }
+
+ binding.tvSortPriceHigh.setOnClickListener {
+ viewModel.changeSort(AudioContentViewModel.Sort.PRICE_HIGH)
+ }
+ }
+
+ 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.contentListLiveData.observe(this) {
+ if (viewModel.page - 1 == 1) {
+ adapter.clear()
+ binding.rvContentAll.scrollToPosition(0)
+ }
+
+ binding.tvTotalCount.text = "${it.totalCount}"
+ binding.toolbar.tvBack.text = it.theme
+ adapter.addItems(it.items)
+ }
+
+ viewModel.sort.observe(this) {
+ deselectSort()
+ selectSort(
+ when (it) {
+ AudioContentViewModel.Sort.PRICE_HIGH -> {
+ binding.tvSortPriceHigh
+ }
+
+ AudioContentViewModel.Sort.PRICE_LOW -> {
+ binding.tvSortPriceLow
+ }
+
+ else -> {
+ binding.tvSortNewest
+ }
+ }
+ )
+
+ viewModel.getContentList(themeId = themeId)
+ }
+ }
+
+ private fun deselectSort() {
+ val color = ContextCompat.getColor(
+ applicationContext,
+ R.color.color_88e2e2e2
+ )
+
+ binding.tvSortNewest.setTextColor(color)
+ binding.tvSortPriceLow.setTextColor(color)
+ binding.tvSortPriceHigh.setTextColor(color)
+ }
+
+ private fun selectSort(view: TextView) {
+ view.setTextColor(
+ ContextCompat.getColor(
+ applicationContext,
+ R.color.color_e2e2e2
+ )
+ )
+ }
+}
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/all/by_theme/AudioContentAllByThemeViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/all/by_theme/AudioContentAllByThemeViewModel.kt
new file mode 100644
index 0000000..4879607
--- /dev/null
+++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/all/by_theme/AudioContentAllByThemeViewModel.kt
@@ -0,0 +1,86 @@
+package kr.co.vividnext.sodalive.audio_content.all.by_theme
+
+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.AudioContentRepository
+import kr.co.vividnext.sodalive.audio_content.AudioContentViewModel
+import kr.co.vividnext.sodalive.base.BaseViewModel
+import kr.co.vividnext.sodalive.common.SharedPreferenceManager
+
+class AudioContentAllByThemeViewModel(
+ private val repository: AudioContentRepository
+) : BaseViewModel() {
+ private val _toastLiveData = MutableLiveData()
+ val toastLiveData: LiveData
+ get() = _toastLiveData
+
+ private var _isLoading = MutableLiveData(false)
+ val isLoading: LiveData
+ get() = _isLoading
+
+ private var _contentListLiveData = MutableLiveData()
+ val contentListLiveData: LiveData
+ get() = _contentListLiveData
+
+ private val _sort = MutableLiveData(AudioContentViewModel.Sort.NEWEST)
+ val sort: LiveData
+ get() = _sort
+
+ private var isLast = false
+ var page = 1
+ private val size = 15
+
+ fun getContentList(themeId: Long) {
+ if (!_isLoading.value!! && !isLast) {
+ _isLoading.value = true
+
+ compositeDisposable.add(
+ repository.getAudioContentByTheme(
+ themeId = themeId,
+ page = page,
+ size = size,
+ sort = _sort.value!!,
+ token = "Bearer ${SharedPreferenceManager.token}"
+ )
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ {
+ if (it.success && it.data != null) {
+ if (it.data.items.isNotEmpty()) {
+ page += 1
+ _contentListLiveData.postValue(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 changeSort(sort: AudioContentViewModel.Sort) {
+ page = 1
+ isLast = false
+ _sort.postValue(sort)
+ }
+}
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/all/by_theme/GetContentByThemeResponse.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/all/by_theme/GetContentByThemeResponse.kt
new file mode 100644
index 0000000..bec1a38
--- /dev/null
+++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/all/by_theme/GetContentByThemeResponse.kt
@@ -0,0 +1,10 @@
+package kr.co.vividnext.sodalive.audio_content.all.by_theme
+
+import com.google.gson.annotations.SerializedName
+import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainItem
+
+data class GetContentByThemeResponse(
+ @SerializedName("theme") val theme: String,
+ @SerializedName("totalCount") val totalCount: Int,
+ @SerializedName("items") val items: List
+)
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/AudioContentMainFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/AudioContentMainFragment.kt
index 4b7c9f0..6d4d7a2 100644
--- a/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/AudioContentMainFragment.kt
+++ b/app/src/main/java/kr/co/vividnext/sodalive/audio_content/main/AudioContentMainFragment.kt
@@ -20,6 +20,7 @@ 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.all.AudioContentRankingAllActivity
+import kr.co.vividnext.sodalive.audio_content.all.by_theme.AudioContentAllByThemeActivity
import kr.co.vividnext.sodalive.audio_content.curation.AudioContentCurationActivity
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.audio_content.main.banner.AudioContentMainBannerAdapter
@@ -116,6 +117,22 @@ class AudioContentMainFragment : BaseFragment(
contentRankingViewModel.getContentRankingSortType()
newContentCreatorViewModel.getNewContentUploadCreatorList()
}
+
+ binding.llShortPlay.setOnClickListener {
+ startActivity(
+ Intent(requireContext(), AudioContentAllByThemeActivity::class.java).apply {
+ putExtra(Constants.EXTRA_THEME_ID, 11L)
+ }
+ )
+ }
+
+ binding.llReviewLive.setOnClickListener {
+ startActivity(
+ Intent(requireContext(), AudioContentAllByThemeActivity::class.java).apply {
+ putExtra(Constants.EXTRA_THEME_ID, 7L)
+ }
+ )
+ }
}
private fun setupNewContentCreator() {
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/common/Constants.kt b/app/src/main/java/kr/co/vividnext/sodalive/common/Constants.kt
index 1eab262..752831f 100644
--- a/app/src/main/java/kr/co/vividnext/sodalive/common/Constants.kt
+++ b/app/src/main/java/kr/co/vividnext/sodalive/common/Constants.kt
@@ -23,6 +23,7 @@ object Constants {
const val EXTRA_NOTICE = "extra_notice"
const val EXTRA_ROOM_ID = "extra_room_id"
const val EXTRA_USER_ID = "extra_user_id"
+ const val EXTRA_THEME_ID = "extra_theme_id"
const val EXTRA_NICKNAME = "extra_nickname"
const val EXTRA_MESSAGE_ID = "extra_message_id"
const val EXTRA_ROOM_DETAIL = "extra_room_detail"
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 e72f6f1..c34761d 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
@@ -9,6 +9,7 @@ import kr.co.vividnext.sodalive.audio_content.AudioContentViewModel
import kr.co.vividnext.sodalive.audio_content.PlaybackTrackingRepository
import kr.co.vividnext.sodalive.audio_content.all.AudioContentNewAllViewModel
import kr.co.vividnext.sodalive.audio_content.all.AudioContentRankingAllViewModel
+import kr.co.vividnext.sodalive.audio_content.all.by_theme.AudioContentAllByThemeViewModel
import kr.co.vividnext.sodalive.audio_content.category.CategoryApi
import kr.co.vividnext.sodalive.audio_content.comment.AudioContentCommentListViewModel
import kr.co.vividnext.sodalive.audio_content.comment.AudioContentCommentReplyViewModel
@@ -220,6 +221,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
viewModel { UserProfileDonationAllViewModel(get(), get()) }
viewModel { AudioContentCurationViewModel(get()) }
viewModel { AudioContentNewAllViewModel(get()) }
+ viewModel { AudioContentAllByThemeViewModel(get()) }
viewModel { AudioContentRankingAllViewModel(get()) }
viewModel { RouletteSettingsViewModel(get()) }
viewModel { CreatorCommunityAllViewModel(get(), get()) }
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomViewModel.kt
index 59e33c3..09f0aa9 100644
--- a/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomViewModel.kt
+++ b/app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomViewModel.kt
@@ -192,7 +192,6 @@ class LiveRoomViewModel(
{
if (it.success && it.data != null) {
roomInfoResponse = it.data
- Logger.e("data: ${it.data}")
_roomInfoLiveData.postValue(roomInfoResponse)
if (_coverImageUrlLiveData.value!! != roomInfoResponse.coverImageUrl) {
diff --git a/app/src/main/res/drawable-xxhdpi/ic_short_play.png b/app/src/main/res/drawable-xxhdpi/ic_short_play.png
new file mode 100644
index 0000000..51b75d6
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_short_play.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_thumb_play_blue.png b/app/src/main/res/drawable-xxhdpi/ic_thumb_play_blue.png
new file mode 100644
index 0000000..2d392a6
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_thumb_play_blue.png differ
diff --git a/app/src/main/res/drawable/bg_round_corner_2_6_ecf9ff.xml b/app/src/main/res/drawable/bg_round_corner_2_6_ecf9ff.xml
new file mode 100644
index 0000000..92ab409
--- /dev/null
+++ b/app/src/main/res/drawable/bg_round_corner_2_6_ecf9ff.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/bg_round_corner_2_6_ffecf7.xml b/app/src/main/res/drawable/bg_round_corner_2_6_ffecf7.xml
new file mode 100644
index 0000000..c239b8b
--- /dev/null
+++ b/app/src/main/res/drawable/bg_round_corner_2_6_ffecf7.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_audio_content_all_by_theme.xml b/app/src/main/res/layout/activity_audio_content_all_by_theme.xml
new file mode 100644
index 0000000..21033d7
--- /dev/null
+++ b/app/src/main/res/layout/activity_audio_content_all_by_theme.xml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_audio_content_main.xml b/app/src/main/res/layout/fragment_audio_content_main.xml
index e302de2..178f404 100644
--- a/app/src/main/res/layout/fragment_audio_content_main.xml
+++ b/app/src/main/res/layout/fragment_audio_content_main.xml
@@ -1,5 +1,7 @@
@@ -51,7 +53,78 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="6.7dp"
- android:layout_marginBottom="40dp" />
+ android:layout_marginBottom="26.7dp" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#553bb9f1
#EA3A25
#CC004462
+ #DD158D
+ #0057FF
+ #FFECF7
+ #ECF9FF