Compare commits

...

7 Commits

Author SHA1 Message Date
klaus 51b57a0b1d 콘텐츠 전체보기
- 카테고리 추가
2024-02-07 07:28:14 +09:00
klaus eafb922ae9 설정페이지
- 회사정보 추가
2024-02-05 22:06:44 +09:00
klaus 6cfbdd7acd 콘텐츠 상세
- 미리듣기가 없을 떄 문구 추가
2024-01-29 16:23:38 +09:00
klaus f8fd06706b 크리에이터 콘텐츠 리스트
- 고정 콘텐츠 핀 추가
2024-01-29 00:48:45 +09:00
klaus c23ef771be 콘텐츠 상세
- 콘텐츠 고정/해제 기능 추가
2024-01-29 00:33:50 +09:00
klaus 0bbb1e070c 콘텐츠 상세
- 미리듣기 없는 콘텐츠는 재생 버튼이 보이지 않도록 수정
2024-01-26 13:31:51 +09:00
klaus fba11ae4b9 콘텐츠 업로드
- 미리듣기 여부 선택 버튼 추가
2024-01-26 02:43:55 +09:00
29 changed files with 866 additions and 134 deletions

View File

@ -40,8 +40,8 @@ android {
applicationId "kr.co.vividnext.sodalive"
minSdk 23
targetSdk 33
versionCode 19
versionName "1.4.1"
versionCode 20
versionName "1.5.0"
}
buildTypes {

View File

@ -14,6 +14,8 @@ import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.category.AudioContentCategoryAdapter
import kr.co.vividnext.sodalive.audio_content.category.GetCategoryListResponse
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadActivity
import kr.co.vividnext.sodalive.base.BaseActivity
@ -32,6 +34,7 @@ class AudioContentActivity : BaseActivity<ActivityAudioContentBinding>(
private lateinit var loadingDialog: LoadingDialog
private lateinit var audioContentAdapter: AudioContentAdapter
private lateinit var categoryAdapter: AudioContentCategoryAdapter
private var userId: Long = 0
private lateinit var activityResultLauncher: ActivityResultLauncher<Intent>
@ -55,6 +58,7 @@ class AudioContentActivity : BaseActivity<ActivityAudioContentBinding>(
}
bindData()
viewModel.getCategoryList(userId = userId)
viewModel.getAudioContentList(userId = userId) { finish() }
}
@ -71,6 +75,60 @@ class AudioContentActivity : BaseActivity<ActivityAudioContentBinding>(
activityResultLauncher.launch(intent)
}
categoryAdapter = AudioContentCategoryAdapter {
viewModel.selectCategory(it, userId = userId)
}
binding.rvCategory.layoutManager = LinearLayoutManager(
applicationContext,
LinearLayoutManager.HORIZONTAL,
false
)
binding.rvCategory.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)) {
categoryAdapter.itemCount - 1 -> {
outRect.right = 0
}
else -> {
outRect.right = 13.3f.dpToPx().toInt()
}
}
}
})
binding.rvCategory.adapter = categoryAdapter
binding.rvAudioContent.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)) {
audioContentAdapter.itemCount - 1 -> {
outRect.bottom = 0
}
else -> {
outRect.bottom = 13.3f.dpToPx().toInt()
}
}
}
})
binding.rvAudioContent.layoutManager = LinearLayoutManager(
applicationContext,
LinearLayoutManager.VERTICAL,
@ -189,6 +247,17 @@ class AudioContentActivity : BaseActivity<ActivityAudioContentBinding>(
)
viewModel.getAudioContentList(userId = userId) { finish() }
}
viewModel.categoryListLiveData.observe(this) {
if (it.isNotEmpty()) {
binding.rvCategory.visibility = View.VISIBLE
val items = it as MutableList<GetCategoryListResponse>
items.add(0, GetCategoryListResponse(0, "전체"))
categoryAdapter.addItems(items = items)
} else {
binding.rvCategory.visibility = View.GONE
}
}
}
private fun deselectSort() {

View File

@ -22,6 +22,12 @@ class AudioContentAdapter(
private val binding: ItemAudioContentBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: GetAudioContentListItem) {
binding.ivPin.visibility = if (item.isPin) {
View.VISIBLE
} else {
View.GONE
}
binding.ivCover.load(item.coverImageUrl) {
crossfade(true)
placeholder(R.drawable.bg_placeholder)

View File

@ -37,6 +37,7 @@ interface AudioContentApi {
@GET("/audio-content")
fun getAudioContentList(
@Query("creator-id") id: Long,
@Query("category-id") categoryId: Long,
@Query("page") page: Int,
@Query("size") size: Int,
@Query("sort-type") sort: AudioContentViewModel.Sort,
@ -201,4 +202,16 @@ interface AudioContentApi {
fun getMainOrderList(
@Header("Authorization") authHeader: String
): Single<ApiResponse<List<GetAudioContentMainItem>>>
@POST("/audio-content/pin-to-the-top/{id}")
fun pinContent(
@Path("id") audioContentId: Long,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
@PUT("/audio-content/unpin-at-the-top/{id}")
fun unpinContent(
@Path("id") audioContentId: Long,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
}

View File

@ -1,5 +1,6 @@
package kr.co.vividnext.sodalive.audio_content
import kr.co.vividnext.sodalive.audio_content.category.CategoryApi
import kr.co.vividnext.sodalive.audio_content.detail.PutAudioContentLikeRequest
import kr.co.vividnext.sodalive.audio_content.donation.AudioContentDonationRequest
import kr.co.vividnext.sodalive.audio_content.order.OrderRequest
@ -12,7 +13,8 @@ import java.util.TimeZone
class AudioContentRepository(
private val api: AudioContentApi,
private val userApi: UserApi
private val userApi: UserApi,
private val categoryApi: CategoryApi
) {
fun getAudioContentListByCurationId(
curationId: Long,
@ -30,12 +32,14 @@ class AudioContentRepository(
fun getAudioContentList(
id: Long,
categoryId: Long,
page: Int,
size: Int,
sort: AudioContentViewModel.Sort,
token: String
) = api.getAudioContentList(
id = id,
categoryId = categoryId,
page = page - 1,
size = size,
sort = sort,
@ -188,4 +192,18 @@ class AudioContentRepository(
fun getMainBannerList(token: String) = api.getMainBannerList(authHeader = token)
fun getMainOrderList(token: String) = api.getMainOrderList(authHeader = token)
fun pinContent(
audioContentId: Long,
token: String
) = api.pinContent(audioContentId, authHeader = token)
fun unpinContent(
audioContentId: Long,
token: String
) = api.unpinContent(audioContentId, authHeader = token)
fun getCategoryList(
creatorId: Long,
token: String
) = categoryApi.getCategoryList(creatorId, authHeader = token)
}

View File

@ -6,6 +6,7 @@ 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.audio_content.category.GetCategoryListResponse
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.explorer.profile.GetAudioContentListResponse
@ -24,6 +25,10 @@ class AudioContentViewModel(private val repository: AudioContentRepository) : Ba
val audioContentListLiveData: LiveData<GetAudioContentListResponse>
get() = _audioContentListLiveData
private var _categoryListLiveData = MutableLiveData<List<GetCategoryListResponse>>()
val categoryListLiveData: LiveData<List<GetCategoryListResponse>>
get() = _categoryListLiveData
private val _sort = MutableLiveData(Sort.NEWEST)
val sort: LiveData<Sort>
get() = _sort
@ -42,6 +47,7 @@ class AudioContentViewModel(private val repository: AudioContentRepository) : Ba
var isLast = false
var page = 1
private val size = 10
private var selectedCategoryId = 0L
fun getAudioContentList(userId: Long, onFailure: (() -> Unit)? = null) {
if (!_isLoading.value!! && !isLast) {
@ -49,6 +55,7 @@ class AudioContentViewModel(private val repository: AudioContentRepository) : Ba
compositeDisposable.add(
repository.getAudioContentList(
id = userId,
categoryId = selectedCategoryId,
page = page,
size = size,
token = "Bearer ${SharedPreferenceManager.token}",
@ -59,12 +66,12 @@ class AudioContentViewModel(private val repository: AudioContentRepository) : Ba
.subscribe(
{
if (it.success && it.data != null) {
if (it.data.items.isNotEmpty()) {
page += 1
_audioContentListLiveData.postValue(it.data!!)
} else {
if (it.data.items.isEmpty()) {
isLast = true
}
page += 1
_audioContentListLiveData.postValue(it.data!!)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
@ -99,4 +106,44 @@ class AudioContentViewModel(private val repository: AudioContentRepository) : Ba
isLast = false
_sort.postValue(sort)
}
fun selectCategory(categoryId: Long, userId: Long) {
isLast = false
page = 1
selectedCategoryId = categoryId
getAudioContentList(userId = userId)
}
fun getCategoryList(userId: Long) {
compositeDisposable.add(
repository.getCategoryList(
creatorId = userId,
token = "Bearer ${SharedPreferenceManager.token}",
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
_categoryListLiveData.value = it.data!!
} 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("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}

View File

@ -0,0 +1,79 @@
package kr.co.vividnext.sodalive.audio_content.category
import android.annotation.SuppressLint
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.databinding.ItemContentCategoryBinding
class AudioContentCategoryAdapter(
private val onClick: (Long) -> Unit
) : RecyclerView.Adapter<AudioContentCategoryAdapter.ViewHolder>() {
private val items = mutableListOf<GetCategoryListResponse>()
private var selectedCategory = ""
inner class ViewHolder(
private val context: Context,
private val binding: ItemContentCategoryBinding
) : RecyclerView.ViewHolder(binding.root) {
@SuppressLint("NotifyDataSetChanged")
fun bind(item: GetCategoryListResponse) {
if (
item.category == selectedCategory ||
(selectedCategory == "" && item.category == "전체")
) {
binding.tvCategory.setBackgroundResource(
R.drawable.bg_round_corner_16_7_transparent_3bb9f1
)
binding.tvCategory.setTextColor(
ContextCompat.getColor(context, R.color.color_3bb9f1)
)
} else {
binding.tvCategory.setBackgroundResource(
R.drawable.bg_round_corner_16_7_transparent_777777
)
binding.tvCategory.setTextColor(
ContextCompat.getColor(context, R.color.color_777777)
)
}
binding.tvCategory.text = item.category
binding.root.setOnClickListener {
onClick(item.categoryId)
selectedCategory = item.category
notifyDataSetChanged()
}
}
}
@SuppressLint("NotifyDataSetChanged")
fun addItems(items: List<GetCategoryListResponse>) {
this.selectedCategory = ""
this.items.clear()
this.items.addAll(items)
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
parent.context,
ItemContentCategoryBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun getItemCount() = items.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(items[position])
}
fun clear() {
this.items.clear()
}
}

View File

@ -0,0 +1,15 @@
package kr.co.vividnext.sodalive.audio_content.category
import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.Query
interface CategoryApi {
@GET("/category")
fun getCategoryList(
@Query("creatorId") creatorId: Long,
@Header("Authorization") authHeader: String
): Single<ApiResponse<List<GetCategoryListResponse>>>
}

View File

@ -0,0 +1,8 @@
package kr.co.vividnext.sodalive.audio_content.category
import com.google.gson.annotations.SerializedName
data class GetCategoryListResponse(
@SerializedName("categoryId") val categoryId: Long,
@SerializedName("category") val category: String
)

View File

@ -13,7 +13,6 @@ import android.view.View
import android.widget.RelativeLayout
import android.widget.SeekBar
import android.widget.Toast
import androidx.appcompat.widget.PopupMenu
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
@ -30,6 +29,7 @@ import kr.co.vividnext.sodalive.audio_content.order.AudioContentOrderConfirmDial
import kr.co.vividnext.sodalive.audio_content.order.AudioContentOrderFragment
import kr.co.vividnext.sodalive.audio_content.order.OrderType
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.base.SodaDialog
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
@ -116,10 +116,7 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
binding.tvBack.setOnClickListener { finish() }
binding.ivClosePreviewAlert.setOnClickListener { viewModel.toggleShowPreviewAlert() }
binding.ivMenu.setOnClickListener {
showOptionMenu(
this,
binding.ivMenu,
)
showOptionMenu()
}
creatorOtherContentAdapter = OtherContentAdapter {
@ -260,7 +257,7 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
}
}
dialog.show(screenWidth)
dialog.show(screenWidth - 26.7f.dpToPx().toInt())
}
}
@ -270,51 +267,46 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
}
}
private fun showOptionMenu(context: Context, v: View) {
val popup = PopupMenu(context, v)
val inflater = popup.menuInflater
if (
viewModel.audioContentLiveData.value!!.creator.creatorId ==
SharedPreferenceManager.userId
) {
inflater.inflate(R.menu.audio_content_detail_creator_menu, popup.menu)
popup.setOnMenuItemClickListener {
when (it.itemId) {
R.id.menu_modify -> {
refresh = true
setResult(RESULT_OK)
startActivity(
Intent(applicationContext, AudioContentModifyActivity::class.java)
.apply {
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, audioContentId)
}
)
}
R.id.menu_delete -> {
showDeleteDialog()
private fun showOptionMenu() {
val dialog = AudioContentDetailMenuBottomSheetDialog(
isPin = viewModel.audioContentLiveData.value!!.isPin,
isCreator = viewModel.audioContentLiveData.value!!
.creator.creatorId == SharedPreferenceManager.userId,
onClickPin = {
if (viewModel.audioContentLiveData.value!!.isPin) {
viewModel.unPinContent(audioContentId)
} else {
if (viewModel.audioContentLiveData.value!!.isAvailablePin) {
viewModel.pinContent(audioContentId)
} else {
SodaDialog(this@AudioContentDetailActivity,
layoutInflater,
"고정 한도 도달",
"이 콘텐츠를 고정하시겠어요? " +
"채널에 콘텐츠를 최대 3개까지 고정할 수 있습니다." +
"이 콘텐츠를 고정하면 가장 오래된 콘텐츠가 대체됩니다.",
"확인",
{ viewModel.pinContent(audioContentId) },
"취소",
{}
).show(screenWidth)
}
}
true
}
} else {
inflater.inflate(R.menu.audio_content_detail_user_menu, popup.menu)
popup.setOnMenuItemClickListener {
when (it.itemId) {
R.id.menu_report -> {
showReportDialog()
}
}
true
}
}
popup.show()
},
onClickModify = {
refresh = true
setResult(RESULT_OK)
startActivity(
Intent(applicationContext, AudioContentModifyActivity::class.java)
.apply {
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, audioContentId)
}
)
},
onClickDelete = { showDeleteDialog() },
onClickReport = { showReportDialog() }
)
dialog.show(supportFragmentManager, dialog.tag)
}
private fun showDeleteDialog() {
@ -389,18 +381,6 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
setupCommentArea(it)
setupCreatorOtherContentListArea(it.creatorOtherContentList)
setupSameThemeOtherContentList(it.sameThemeOtherContentList)
isAlertPreview = it.creator.creatorId != SharedPreferenceManager.userId &&
!it.existOrdered &&
it.price > 0
binding.ivPlayOrPause.setImageResource(
if (isAlertPreview) {
R.drawable.btn_audio_content_preview_play
} else {
R.drawable.btn_audio_content_play
}
)
}
viewModel.isContentPlayLoopLiveData.observe(this) {
@ -547,9 +527,18 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
.apply(RequestOptions().override((screenWidth - 13.3f.dpToPx()).toInt()))
.into(binding.ivCover)
binding.ivPlayOrPause.visibility = View.GONE
binding.tvPreviewNo.visibility = View.GONE
binding.tvTotalDuration.text = " / ${response.duration}"
isAlertPreview = response.creator.creatorId != SharedPreferenceManager.userId &&
!response.existOrdered &&
response.price > 0
if (
response.releaseDate == null ||
response.creator.creatorId == SharedPreferenceManager.userId
response.releaseDate == null &&
!isAlertPreview ||
(isAlertPreview && response.isActivePreview)
) {
binding.ivPlayOrPause.visibility = View.VISIBLE
binding.ivPlayOrPause.setOnClickListener {
@ -575,11 +564,17 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
}
)
}
} else {
binding.ivPlayOrPause.visibility = View.GONE
}
binding.tvTotalDuration.text = " / ${response.duration}"
binding.ivPlayOrPause.setImageResource(
if (!isAlertPreview) {
R.drawable.btn_audio_content_play
} else {
R.drawable.btn_audio_content_preview_play
}
)
} else {
binding.tvPreviewNo.visibility = View.VISIBLE
}
}
@SuppressLint("SetTextI18n")

View File

@ -0,0 +1,66 @@
package kr.co.vividnext.sodalive.audio_content.detail
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.databinding.DialogAudioContentDetailMenuBinding
class AudioContentDetailMenuBottomSheetDialog(
private val isPin: Boolean,
private val isCreator: Boolean,
private val onClickPin: () -> Unit,
private val onClickModify: () -> Unit,
private val onClickDelete: () -> Unit,
private val onClickReport: () -> Unit
) : BottomSheetDialogFragment() {
private lateinit var dialog: DialogAudioContentDetailMenuBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
dialog = DialogAudioContentDetailMenuBinding.inflate(inflater, container, false)
return dialog.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (isCreator) {
dialog.tvReport.visibility = View.GONE
dialog.llMenuCreator.visibility = View.VISIBLE
if (isPin) {
dialog.ivPin.setImageResource(R.drawable.ic_pin_cancel)
dialog.tvPin.text = "내 채널에 고정 취소"
} else {
dialog.ivPin.setImageResource(R.drawable.ic_pin)
dialog.tvPin.text = "내 채널에 고정"
}
dialog.llPin.setOnClickListener {
dismiss()
onClickPin()
}
dialog.llModify.setOnClickListener {
dismiss()
onClickModify()
}
dialog.llDelete.setOnClickListener {
dismiss()
onClickDelete()
}
} else {
dialog.llMenuCreator.visibility = View.GONE
dialog.tvReport.visibility = View.VISIBLE
dialog.tvReport.setOnClickListener {
dismiss()
onClickReport()
}
}
}
}

View File

@ -481,4 +481,74 @@ class AudioContentDetailViewModel(
)
)
}
fun pinContent(audioContentId: Long) {
isLoading.value = true
compositeDisposable.add(
repository.pinContent(
audioContentId,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
isLoading.value = false
if (it.success) {
_toastLiveData.postValue("고정되었습니다.")
getAudioContentDetail(audioContentId)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun unPinContent(audioContentId: Long) {
isLoading.value = true
compositeDisposable.add(
repository.unpinContent(
audioContentId,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
isLoading.value = false
if (it.success) {
_toastLiveData.postValue("해제되었습니다.")
getAudioContentDetail(audioContentId)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}

View File

@ -15,6 +15,7 @@ data class GetAudioContentDetailResponse(
@SerializedName("price") val price: Int,
@SerializedName("duration") val duration: String,
@SerializedName("releaseDate") val releaseDate: String?,
@SerializedName("isActivePreview") val isActivePreview: Boolean,
@SerializedName("isAdult") val isAdult: Boolean,
@SerializedName("isMosaic") val isMosaic: Boolean,
@SerializedName("isOnlyRental") val isOnlyRental: Boolean,
@ -30,6 +31,8 @@ data class GetAudioContentDetailResponse(
@SerializedName("likeCount") val likeCount: Int,
@SerializedName("commentList") val commentList: List<GetAudioContentCommentListItem>,
@SerializedName("commentCount") val commentCount: Int,
@SerializedName("isPin") val isPin: Boolean,
@SerializedName("isAvailablePin") val isAvailablePin: Boolean,
@SerializedName("creator") val creator: AudioContentCreator
)

View File

@ -197,6 +197,8 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
binding.llPricePaid.setOnClickListener { viewModel.setPriceFree(false) }
binding.llPriceFree.setOnClickListener { viewModel.setPriceFree(true) }
binding.llPreviewYes.setOnClickListener { viewModel.setGeneratePreview(true) }
binding.llPreviewNo.setOnClickListener { viewModel.setGeneratePreview(false) }
binding.llRentalAndKeep.setOnClickListener { viewModel.setIsOnlyRental(false) }
binding.llOnlyRental.setOnClickListener { viewModel.setIsOnlyRental(true) }
binding.llCommentNo.setOnClickListener { viewModel.setAvailableComment(false) }
@ -401,6 +403,14 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
}
}
viewModel.isGeneratePreviewLiveData.observe(this) {
if (it) {
checkGeneratePreview()
} else {
checkNotGeneratePreview()
}
}
viewModel.isOnlyRentalLiveData.observe(this) {
if (it) {
checkOnlyRental()
@ -593,6 +603,7 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
R.drawable.bg_round_corner_6_7_13181b
)
binding.llConfigPreview.visibility = View.GONE
binding.llConfigPreviewTime.visibility = View.GONE
}
@ -620,9 +631,55 @@ class AudioContentUploadActivity : BaseActivity<ActivityAudioContentUploadBindin
binding.llPriceFree.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b
)
binding.llConfigPreview.visibility = View.VISIBLE
}
private fun checkGeneratePreview() {
binding.ivPreviewYes.visibility = View.VISIBLE
binding.tvPreviewYes.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_eeeeee
)
)
binding.llPreviewYes.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
binding.ivPreviewNo.visibility = View.GONE
binding.tvPreviewNo.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_3bb9f1
)
)
binding.llPreviewNo.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b
)
binding.llConfigPreviewTime.visibility = View.VISIBLE
}
private fun checkNotGeneratePreview() {
binding.ivPreviewNo.visibility = View.VISIBLE
binding.tvPreviewNo.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_eeeeee
)
)
binding.llPreviewNo.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
binding.ivPreviewYes.visibility = View.GONE
binding.tvPreviewYes.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_3bb9f1
)
)
binding.llPreviewYes.setBackgroundResource(
R.drawable.bg_round_corner_6_7_13181b
)
binding.llConfigPreviewTime.visibility = View.GONE
}
private fun checkRentalAndKeep() {
binding.tvPriceTitle.text = "소장 가격"
binding.ivRentalAndKeep.visibility = View.VISIBLE

View File

@ -50,6 +50,10 @@ class AudioContentUploadViewModel(
val isPriceFreeLiveData: LiveData<Boolean>
get() = _isPriceFreeLiveData
private val _isGeneratePreviewLiveData = MutableLiveData(true)
val isGeneratePreviewLiveData: LiveData<Boolean>
get() = _isGeneratePreviewLiveData
private val _isActiveReservationLiveData = MutableLiveData(false)
val isActiveReservationLiveData: LiveData<Boolean>
get() = _isActiveReservationLiveData
@ -89,9 +93,14 @@ class AudioContentUploadViewModel(
if (isPriceFree) {
_isOnlyRentalLiveData.postValue(false)
_isGeneratePreviewLiveData.postValue(true)
}
}
fun setGeneratePreview(isGeneratePreview: Boolean) {
_isGeneratePreviewLiveData.value = isGeneratePreview
}
fun setIsOnlyRental(isOnlyRental: Boolean) {
_isOnlyRentalLiveData.postValue(isOnlyRental)
}
@ -104,6 +113,8 @@ class AudioContentUploadViewModel(
if (!_isLoading.value!! && validateData()) {
_isLoading.postValue(true)
val isGeneratePreview = _isGeneratePreviewLiveData.value!!
val request = CreateAudioContentRequest(
title = title,
detail = detail,
@ -118,9 +129,18 @@ class AudioContentUploadViewModel(
themeId = theme!!.id,
isAdult = _isAdultLiveData.value!!,
isOnlyRental = _isOnlyRentalLiveData.value!!,
isGeneratePreview = isGeneratePreview,
isCommentAvailable = _isAvailableCommentLiveData.value!!,
previewStartTime = previewStartTime,
previewEndTime = previewEndTime
previewStartTime = if (isGeneratePreview) {
previewStartTime
} else {
null
},
previewEndTime = if (isGeneratePreview) {
previewEndTime
} else {
null
}
)
val requestJson = Gson().toJson(request)

View File

@ -12,6 +12,7 @@ data class CreateAudioContentRequest(
@SerializedName("themeId") val themeId: Long,
@SerializedName("isAdult") val isAdult: Boolean,
@SerializedName("isOnlyRental") val isOnlyRental: Boolean,
@SerializedName("isGeneratePreview") val isGeneratePreview: Boolean,
@SerializedName("isCommentAvailable") val isCommentAvailable: Boolean,
@SerializedName("previewStartTime") val previewStartTime: String? = null,
@SerializedName("previewEndTime") val previewEndTime: String? = null,

View File

@ -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.category.CategoryApi
import kr.co.vividnext.sodalive.audio_content.comment.AudioContentCommentListViewModel
import kr.co.vividnext.sodalive.audio_content.comment.AudioContentCommentReplyViewModel
import kr.co.vividnext.sodalive.audio_content.comment.AudioContentCommentRepository
@ -162,6 +163,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
single { ApiBuilder().build(get(), MemberTagApi::class.java) }
single { ApiBuilder().build(get(), RouletteApi::class.java) }
single { ApiBuilder().build(get(), CreatorCommunityApi::class.java) }
single { ApiBuilder().build(get(), CategoryApi::class.java) }
}
private val viewModelModule = module {
@ -240,7 +242,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
factory { ExplorerRepository(get()) }
factory { MessageRepository(get()) }
factory { NoticeRepository(get()) }
factory { AudioContentRepository(get(), get()) }
factory { AudioContentRepository(get(), get(), get()) }
factory { AudioContentCommentRepository(get()) }
factory { PlaybackTrackingRepository(get()) }
factory { FollowingCreatorRepository(get(), get()) }

View File

@ -76,6 +76,7 @@ data class GetAudioContentListItem(
@SerializedName("duration") val duration: String?,
@SerializedName("likeCount") val likeCount: Int,
@SerializedName("commentCount") val commentCount: Int,
@SerializedName("isPin") val isPin: Boolean,
@SerializedName("isAdult") val isAdult: Boolean,
@SerializedName("isScheduledToOpen") val isScheduledToOpen: Boolean
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 787 B

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/color_333333" />
<corners android:radius="46.7dp" />
<stroke
android:width="3dp"
android:color="@color/color_333333" />
</shape>

View File

@ -9,6 +9,15 @@
android:id="@+id/toolbar"
layout="@layout/detail_toolbar" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_category"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"
android:clipToPadding="false"
android:padding="13.3dp"
android:visibility="gone" />
<TextView
android:id="@+id/tv_new_content"
android:layout_width="match_parent"

View File

@ -118,6 +118,18 @@
android:visibility="gone"
tools:src="@drawable/btn_audio_content_play" />
<TextView
android:id="@+id/tv_preview_no"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_round_corner_46_7_333333"
android:padding="13.3dp"
android:visibility="gone"
android:layout_centerInParent="true"
android:text="해당 콘텐츠는 크리에이터의 요청으로\n미리듣기를 제공하지 않습니다"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<SeekBar
android:id="@+id/sb_progress"
android:layout_width="match_parent"

View File

@ -535,7 +535,7 @@
</LinearLayout>
<LinearLayout
android:id="@+id/ll_config_preview_time"
android:id="@+id/ll_config_preview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
@ -547,19 +547,10 @@
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:lineSpacingExtra="5sp"
android:text="미리듣기 시간 설정"
android:text="미리듣기"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="미리듣기 시간을 직접 설정하지 않으면 콘텐츠 앞부분 30초가 자동으로 설정됩니다. 미리듣기의 시간제한은 없습니다."
android:textColor="@color/color_777777"
android:textSize="13.3sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -567,74 +558,164 @@
android:baselineAligned="false">
<LinearLayout
android:id="@+id/ll_preview_yes"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
android:background="@drawable/bg_round_corner_6_7_13181b"
android:gravity="center"
android:paddingVertical="14.3dp">
<TextView
<ImageView
android:id="@+id/iv_preview_yes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:text="시작 시간"
android:textColor="@color/color_eeeeee"
android:textSize="13.3sp" />
android:layout_marginEnd="6.7dp"
android:contentDescription="@null"
android:src="@drawable/ic_select_check"
android:visibility="gone" />
<EditText
android:id="@+id/et_preview_start_time"
android:layout_width="match_parent"
<TextView
android:id="@+id/tv_preview_yes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5.3dp"
android:background="@drawable/bg_round_corner_6_7_222222"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:hint="00:00:00"
android:importantForAutofill="no"
android:inputType="textWebEditText"
android:paddingHorizontal="13.3dp"
android:paddingVertical="17dp"
android:textColor="@color/color_eeeeee"
android:textColorHint="@color/color_777777"
android:textCursorDrawable="@drawable/edit_text_cursor"
android:textSize="14.7sp"
android:theme="@style/EditTextStyle"
tools:ignore="LabelFor" />
android:text="생성"
android:textColor="@color/color_3bb9f1"
android:textSize="14.7sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_preview_no"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:layout_weight="1"
android:orientation="vertical">
android:background="@drawable/bg_round_corner_6_7_13181b"
android:gravity="center"
android:paddingVertical="14.3dp">
<TextView
<ImageView
android:id="@+id/iv_preview_no"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:text="종료 시간"
android:textColor="@color/color_eeeeee"
android:textSize="13.3sp" />
android:layout_marginEnd="6.7dp"
android:contentDescription="@null"
android:src="@drawable/ic_select_check"
android:visibility="gone" />
<EditText
android:id="@+id/et_preview_end_time"
android:layout_width="match_parent"
<TextView
android:id="@+id/tv_preview_no"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5.3dp"
android:background="@drawable/bg_round_corner_6_7_222222"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:hint="00:30:00"
android:importantForAutofill="no"
android:inputType="textWebEditText"
android:paddingHorizontal="13.3dp"
android:paddingVertical="17dp"
android:textColor="@color/color_eeeeee"
android:textColorHint="@color/color_777777"
android:textCursorDrawable="@drawable/edit_text_cursor"
android:textSize="14.7sp"
android:theme="@style/EditTextStyle"
tools:ignore="LabelFor" />
android:text="생성 안 함"
android:textColor="@color/color_3bb9f1"
android:textSize="14.7sp" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_config_preview_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:lineSpacingExtra="5sp"
android:text="미리듣기 시간 설정"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="미리듣기 시간을 직접 설정하지 않으면 콘텐츠 앞부분 30초가 자동으로 설정됩니다. 미리듣기의 시간제한은 없습니다."
android:textColor="@color/color_777777"
android:textSize="13.3sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"
android:baselineAligned="false">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:text="시작 시간"
android:textColor="@color/color_eeeeee"
android:textSize="13.3sp" />
<EditText
android:id="@+id/et_preview_start_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5.3dp"
android:background="@drawable/bg_round_corner_6_7_222222"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:hint="00:00:00"
android:importantForAutofill="no"
android:inputType="textWebEditText"
android:paddingHorizontal="13.3dp"
android:paddingVertical="17dp"
android:textColor="@color/color_eeeeee"
android:textColorHint="@color/color_777777"
android:textCursorDrawable="@drawable/edit_text_cursor"
android:textSize="14.7sp"
android:theme="@style/EditTextStyle"
tools:ignore="LabelFor" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:text="종료 시간"
android:textColor="@color/color_eeeeee"
android:textSize="13.3sp" />
<EditText
android:id="@+id/et_preview_end_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5.3dp"
android:background="@drawable/bg_round_corner_6_7_222222"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:hint="00:30:00"
android:importantForAutofill="no"
android:inputType="textWebEditText"
android:paddingHorizontal="13.3dp"
android:paddingVertical="17dp"
android:textColor="@color/color_eeeeee"
android:textColorHint="@color/color_777777"
android:textCursorDrawable="@drawable/edit_text_cursor"
android:textSize="14.7sp"
android:theme="@style/EditTextStyle"
tools:ignore="LabelFor" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -257,6 +257,22 @@
</RelativeLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="16.7dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="
- 회사명 : 주식회사 비비드넥스트\n
- 대표자 : 한지영\n
- 주소 : 서울 강남구 테헤란로 410, 11층 A08호(대치동, 금강타워)\n
- 사업자등록번호 : 508-86-01545\n
- 통신판매업신고 : 제2022-서울강남-00559호\n
- 고객센터 : 02.2055.1477"
android:textColor="@color/color_777777"
android:textSize="11sp" />
<TextView
android:id="@+id/tv_log_out"
android:layout_width="match_parent"

View File

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingVertical="25dp"
tools:background="@color/black">
<LinearLayout
android:id="@+id/ll_menu_creator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="21dp">
<LinearLayout
android:id="@+id/ll_pin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingVertical="8dp"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/iv_pin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_pin" />
<TextView
android:id="@+id/tv_pin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:text="내 채널에 고정"
android:textColor="@color/color_e2e2e2"
android:textSize="14.7sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_modify"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="21dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingVertical="8dp"
tools:ignore="UseCompoundDrawables">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_make_message" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:text="수정"
android:textColor="@color/color_e2e2e2"
android:textSize="14.7sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_delete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="21dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingVertical="8dp"
tools:ignore="UseCompoundDrawables">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_trash_can" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:text="삭제"
android:textColor="@color/color_e2e2e2"
android:textSize="14.7sp" />
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/tv_report"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:paddingHorizontal="21.3dp"
android:paddingVertical="8dp"
android:text="신고하기"
android:textColor="@color/color_e2e2e2"
android:textSize="14.7sp" />
</LinearLayout>

View File

@ -68,6 +68,15 @@
android:textSize="8sp"
tools:ignore="SmallSp"
tools:text="00:30:20" />
<ImageView
android:id="@+id/iv_pin"
android:layout_width="13.3dp"
android:layout_height="13.3dp"
android:layout_marginStart="8dp"
android:contentDescription="@null"
android:src="@drawable/ic_pin"
android:visibility="gone" />
</LinearLayout>
<TextView

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_category"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_round_corner_16_7_transparent_777777"
android:paddingHorizontal="13.3dp"
android:paddingVertical="9dp"
android:textColor="@color/color_777777"
android:textSize="14.7sp"
tools:text="남공여수" />
</FrameLayout>