Compare commits

..

No commits in common. "e52075a692f14a3009954604ca0d19e3cfed14b9" and "1ec0d8540aa59fdd0a272399f8ef3b930d08bcde" have entirely different histories.

123 changed files with 680 additions and 3076 deletions

View File

@ -40,8 +40,8 @@ android {
applicationId "kr.co.vividnext.sodalive" applicationId "kr.co.vividnext.sodalive"
minSdk 23 minSdk 23
targetSdk 33 targetSdk 33
versionCode 46 versionCode 22
versionName "1.8.21" versionName "1.6.0"
} }
buildTypes { buildTypes {
@ -149,7 +149,4 @@ dependencies {
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
implementation "com.michalsvec:single-row-calednar:1.0.0" implementation "com.michalsvec:single-row-calednar:1.0.0"
// google in-app-purchase
implementation "com.android.billingclient:billing-ktx:6.2.0"
} }

View File

@ -2,7 +2,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
@ -85,9 +84,7 @@
<activity android:name=".settings.terms.TermsActivity" /> <activity android:name=".settings.terms.TermsActivity" />
<activity android:name=".user.find_password.FindPasswordActivity" /> <activity android:name=".user.find_password.FindPasswordActivity" />
<activity android:name=".mypage.can.status.CanStatusActivity" /> <activity android:name=".mypage.can.status.CanStatusActivity" />
<activity <activity android:name=".mypage.can.charge.CanChargeActivity" />
android:name=".mypage.can.charge.CanChargeActivity"
android:configChanges="orientation|screenSize|keyboardHidden" />
<activity android:name=".mypage.can.payment.CanPaymentActivity" /> <activity android:name=".mypage.can.payment.CanPaymentActivity" />
<activity android:name=".mypage.can.coupon.CanCouponActivity" /> <activity android:name=".mypage.can.coupon.CanCouponActivity" />
<activity android:name=".live.room.create.LiveRoomCreateActivity" /> <activity android:name=".live.room.create.LiveRoomCreateActivity" />

View File

@ -12,7 +12,6 @@ import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.audio_content.main.new_content.AudioContentMainNewContentThemeAdapter import kr.co.vividnext.sodalive.audio_content.main.new_content.AudioContentMainNewContentThemeAdapter
import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants 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.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.ActivityAudioContentNewAllBinding import kr.co.vividnext.sodalive.databinding.ActivityAudioContentNewAllBinding
import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
@ -90,10 +89,8 @@ class AudioContentNewAllActivity : BaseActivity<ActivityAudioContentNewAllBindin
} }
private fun setupNewContent() { private fun setupNewContent() {
val spanCount = 3
val spacing = 40
newContentAdapter = AudioContentNewAllAdapter( newContentAdapter = AudioContentNewAllAdapter(
itemWidth = (screenWidth - (spacing * (spanCount + 1))) / 3, itemWidth = (screenWidth - 42f.dpToPx().toInt()) / 3,
onClickItem = { onClickItem = {
startActivity( startActivity(
Intent(this, AudioContentDetailActivity::class.java).apply { Intent(this, AudioContentDetailActivity::class.java).apply {
@ -110,8 +107,23 @@ class AudioContentNewAllActivity : BaseActivity<ActivityAudioContentNewAllBindin
} }
) )
binding.rvContent.layoutManager = GridLayoutManager(this, spanCount) binding.rvContent.layoutManager = GridLayoutManager(this, 3)
binding.rvContent.addItemDecoration(GridSpacingItemDecoration(spanCount, spacing, true))
binding.rvContent.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
outRect.top = 8f.dpToPx().toInt()
outRect.bottom = 8f.dpToPx().toInt()
outRect.left = 4f.dpToPx().toInt()
outRect.right = 4f.dpToPx().toInt()
}
})
binding.rvContent.addOnScrollListener(object : RecyclerView.OnScrollListener() { binding.rvContent.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {

View File

@ -1,9 +1,6 @@
package kr.co.vividnext.sodalive.audio_content.all package kr.co.vividnext.sodalive.audio_content.all
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -11,12 +8,8 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.load import coil.load
import coil.transform.CircleCropTransformation import coil.transform.CircleCropTransformation
import com.bumptech.glide.Glide import coil.transform.RoundedCornersTransformation
import com.bumptech.glide.load.resource.bitmap.CenterCrop import com.orhanobut.logger.Logger
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainItem import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainItem
import kr.co.vividnext.sodalive.databinding.ItemAudioContentNewAllBinding import kr.co.vividnext.sodalive.databinding.ItemAudioContentNewAllBinding
@ -30,27 +23,24 @@ class AudioContentNewAllAdapter(
) : RecyclerView.Adapter<AudioContentNewAllAdapter.ViewHolder>() { ) : RecyclerView.Adapter<AudioContentNewAllAdapter.ViewHolder>() {
inner class ViewHolder( inner class ViewHolder(
private val context: Context,
private val binding: ItemAudioContentNewAllBinding, private val binding: ItemAudioContentNewAllBinding,
private val onClickItem: (Long) -> Unit, private val onClickItem: (Long) -> Unit,
private val onClickCreator: (Long) -> Unit private val onClickCreator: (Long) -> Unit
) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: GetAudioContentMainItem) { fun bind(item: GetAudioContentMainItem) {
Glide Logger.e("item: $item")
.with(context) binding.ivAudioContentCoverImage.load(item.coverImageUrl) {
.load(item.coverImageUrl) crossfade(true)
.apply( placeholder(R.drawable.ic_place_holder)
RequestOptions().transform( transformations(RoundedCornersTransformation(2.7f.dpToPx()))
CenterCrop(),
RoundedCorners(8)
)
)
.into(binding.ivAudioContentCoverImage)
val layoutParams = binding.ivAudioContentCoverImage.layoutParams as ConstraintLayout.LayoutParams val layoutParams = binding.ivAudioContentCoverImage
layoutParams.width = itemWidth .layoutParams as ConstraintLayout.LayoutParams
layoutParams.height = itemWidth
binding.ivAudioContentCoverImage.layoutParams = layoutParams layoutParams.width = itemWidth
layoutParams.height = itemWidth
binding.ivAudioContentCoverImage.layoutParams = layoutParams
}
binding.ivAudioContentCreator.load(item.creatorProfileImageUrl) { binding.ivAudioContentCreator.load(item.creatorProfileImageUrl) {
crossfade(true) crossfade(true)
@ -82,7 +72,6 @@ class AudioContentNewAllAdapter(
parent: ViewGroup, parent: ViewGroup,
viewType: Int viewType: Int
) = ViewHolder( ) = ViewHolder(
parent.context,
ItemAudioContentNewAllBinding.inflate( ItemAudioContentNewAllBinding.inflate(
LayoutInflater.from(parent.context), LayoutInflater.from(parent.context),
parent, parent,

View File

@ -16,7 +16,6 @@ import kr.co.vividnext.sodalive.audio_content.all.AudioContentNewAllAdapter
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants 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.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.ActivityAudioContentAllByThemeBinding import kr.co.vividnext.sodalive.databinding.ActivityAudioContentAllByThemeBinding
import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
@ -56,10 +55,8 @@ class AudioContentAllByThemeActivity : BaseActivity<ActivityAudioContentAllByThe
loadingDialog = LoadingDialog(this, layoutInflater) loadingDialog = LoadingDialog(this, layoutInflater)
binding.toolbar.tvBack.setOnClickListener { finish() } binding.toolbar.tvBack.setOnClickListener { finish() }
val spanCount = 3
val spacing = 40
adapter = AudioContentNewAllAdapter( adapter = AudioContentNewAllAdapter(
itemWidth = (screenWidth - (spacing * (spanCount + 1))) / 3, itemWidth = (screenWidth - 42f.dpToPx().toInt()) / 3,
onClickItem = { onClickItem = {
startActivity( startActivity(
Intent(this, AudioContentDetailActivity::class.java).apply { Intent(this, AudioContentDetailActivity::class.java).apply {
@ -76,8 +73,23 @@ class AudioContentAllByThemeActivity : BaseActivity<ActivityAudioContentAllByThe
} }
) )
binding.rvContentAll.layoutManager = GridLayoutManager(this, spanCount) binding.rvContentAll.layoutManager = GridLayoutManager(this, 3)
binding.rvContentAll.addItemDecoration(GridSpacingItemDecoration(spanCount, spacing, true))
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 = 8f.dpToPx().toInt()
outRect.bottom = 8f.dpToPx().toInt()
outRect.left = 4f.dpToPx().toInt()
outRect.right = 4f.dpToPx().toInt()
}
})
binding.rvContentAll.addOnScrollListener(object : RecyclerView.OnScrollListener() { binding.rvContentAll.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {

View File

@ -17,7 +17,6 @@ import kr.co.vividnext.sodalive.audio_content.all.AudioContentNewAllAdapter
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants 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.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.ActivityAudioContentCurationBinding import kr.co.vividnext.sodalive.databinding.ActivityAudioContentCurationBinding
import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
@ -55,10 +54,8 @@ class AudioContentCurationActivity : BaseActivity<ActivityAudioContentCurationBi
binding.toolbar.tvBack.text = title binding.toolbar.tvBack.text = title
binding.toolbar.tvBack.setOnClickListener { finish() } binding.toolbar.tvBack.setOnClickListener { finish() }
val spanCount = 3
val spacing = 40
adapter = AudioContentNewAllAdapter( adapter = AudioContentNewAllAdapter(
itemWidth = (screenWidth - (spacing * (spanCount + 1))) / 3, itemWidth = (screenWidth - 42f.dpToPx().toInt()) / 3,
onClickItem = { onClickItem = {
startActivity( startActivity(
Intent(this, AudioContentDetailActivity::class.java).apply { Intent(this, AudioContentDetailActivity::class.java).apply {
@ -75,8 +72,23 @@ class AudioContentCurationActivity : BaseActivity<ActivityAudioContentCurationBi
} }
) )
binding.rvCuration.layoutManager = GridLayoutManager(this, spanCount) binding.rvCuration.layoutManager = GridLayoutManager(this, 3)
binding.rvCuration.addItemDecoration(GridSpacingItemDecoration(spanCount, spacing, true))
binding.rvCuration.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
outRect.top = 8f.dpToPx().toInt()
outRect.bottom = 8f.dpToPx().toInt()
outRect.left = 4f.dpToPx().toInt()
outRect.right = 4f.dpToPx().toInt()
}
})
binding.rvCuration.addOnScrollListener(object : RecyclerView.OnScrollListener() { binding.rvCuration.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {

View File

@ -238,16 +238,10 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
} }
}) })
val ivCoverLp = binding.ivCover.layoutParams as RelativeLayout.LayoutParams val layoutParams = binding.ivCover.layoutParams as RelativeLayout.LayoutParams
ivCoverLp.width = (screenWidth - 13.3f.dpToPx()).toInt() layoutParams.width = (screenWidth - 13.3f.dpToPx()).toInt()
ivCoverLp.height = (screenWidth - 13.3f.dpToPx()).toInt() layoutParams.height = (screenWidth - 13.3f.dpToPx()).toInt()
binding.ivCover.layoutParams = ivCoverLp binding.ivCover.layoutParams = layoutParams
val flSoldOutLp = binding.flSoldOut.layoutParams as RelativeLayout.LayoutParams
flSoldOutLp.width = (screenWidth - 13.3f.dpToPx()).toInt()
flSoldOutLp.height = (screenWidth - 13.3f.dpToPx()).toInt()
binding.flSoldOut.layoutParams = flSoldOutLp
binding.ivPlayLoop.setOnClickListener { viewModel.togglePlayLoop() } binding.ivPlayLoop.setOnClickListener { viewModel.togglePlayLoop() }
binding.llDonation.setOnClickListener { binding.llDonation.setOnClickListener {
val dialog = LiveRoomDonationDialog( val dialog = LiveRoomDonationDialog(
@ -487,7 +481,6 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
if (response.releaseDate != null) { if (response.releaseDate != null) {
binding.llPurchase.visibility = View.VISIBLE binding.llPurchase.visibility = View.VISIBLE
binding.llPurchasePrice.visibility = View.GONE binding.llPurchasePrice.visibility = View.GONE
binding.tvPurchaseSoldOut.visibility = View.GONE
binding.tvReleaseDate.visibility = View.VISIBLE binding.tvReleaseDate.visibility = View.VISIBLE
binding.llPurchase.background = ContextCompat.getDrawable( binding.llPurchase.background = ContextCompat.getDrawable(
applicationContext, applicationContext,
@ -501,50 +494,26 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
response.orderType == null && response.orderType == null &&
response.creator.creatorId != SharedPreferenceManager.userId response.creator.creatorId != SharedPreferenceManager.userId
) { ) {
if ( binding.tvReleaseDate.visibility = View.GONE
response.totalContentCount != null && response.remainingContentCount != null && binding.llPurchase.visibility = View.VISIBLE
response.remainingContentCount <= 0 binding.llPurchasePrice.visibility = View.VISIBLE
) { binding.tvPrice.text = response.price.toString()
binding.llPurchase.visibility = View.GONE binding.llPurchase.background = ContextCompat.getDrawable(
binding.tvPurchaseSoldOut.visibility = View.VISIBLE applicationContext,
R.drawable.bg_round_corner_5_3_3bb9f1
)
binding.tvStrPurchaseOrRental.text = if (response.isOnlyRental) {
" 대여하기"
} else { } else {
binding.tvPurchaseSoldOut.visibility = View.GONE " 구매하기"
binding.tvReleaseDate.visibility = View.GONE }
binding.llPurchase.visibility = View.VISIBLE
binding.llPurchasePrice.visibility = View.VISIBLE
binding.tvPrice.text = response.price.toString()
binding.llPurchase.background = ContextCompat.getDrawable(
applicationContext,
R.drawable.bg_round_corner_5_3_3bb9f1
)
binding.tvStrPurchaseOrRental.text = if (response.isOnlyRental) { binding.llPurchase.setOnClickListener {
" 대여하기" showOrderDialog(audioContent = response, isOnlyRental = response.isOnlyRental)
} else {
" 구매하기"
}
binding.llPurchase.setOnClickListener {
if (
response.totalContentCount != null &&
response.remainingContentCount != null
) {
showOrderConfirmDialog(
audioContent = response,
isOnlyRental = false,
OrderType.KEEP
)
} else {
showOrderDialog(
audioContent = response,
isOnlyRental = response.isOnlyRental
)
}
}
} }
} else { } else {
binding.llPurchase.visibility = View.GONE binding.llPurchase.visibility = View.GONE
binding.tvPurchaseSoldOut.visibility = View.GONE
} }
} }
@ -558,8 +527,6 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
.apply(RequestOptions().override((screenWidth - 13.3f.dpToPx()).toInt())) .apply(RequestOptions().override((screenWidth - 13.3f.dpToPx()).toInt()))
.into(binding.ivCover) .into(binding.ivCover)
binding.flSoldOut.visibility = View.GONE
binding.tvSoldOutBig.visibility = View.GONE
binding.ivPlayOrPause.visibility = View.GONE binding.ivPlayOrPause.visibility = View.GONE
binding.tvPreviewNo.visibility = View.GONE binding.tvPreviewNo.visibility = View.GONE
binding.tvTotalDuration.text = " / ${response.duration}" binding.tvTotalDuration.text = " / ${response.duration}"
@ -569,16 +536,9 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
response.price > 0 response.price > 0
if ( if (
!response.existOrdered &&
response.totalContentCount != null && response.remainingContentCount != null &&
response.remainingContentCount <= 0
) {
binding.flSoldOut.visibility = View.VISIBLE
binding.tvSoldOutBig.visibility = View.VISIBLE
} else if (
response.releaseDate == null && response.releaseDate == null &&
!isAlertPreview || !isAlertPreview ||
(response.isActivePreview && response.contentUrl.isNotBlank()) (isAlertPreview && response.isActivePreview)
) { ) {
binding.ivPlayOrPause.visibility = View.VISIBLE binding.ivPlayOrPause.visibility = View.VISIBLE
binding.ivPlayOrPause.setOnClickListener { binding.ivPlayOrPause.setOnClickListener {
@ -612,7 +572,7 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
R.drawable.btn_audio_content_preview_play R.drawable.btn_audio_content_preview_play
} }
) )
} else if (response.releaseDate == null) { } else {
binding.tvPreviewNo.visibility = View.VISIBLE binding.tvPreviewNo.visibility = View.VISIBLE
} }
} }
@ -697,33 +657,6 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
startActivity(shareIntent) startActivity(shareIntent)
} }
} }
if (response.totalContentCount != null && response.remainingContentCount != null) {
binding.rlLimitedEdition.visibility = View.VISIBLE
if (response.existOrdered) {
binding.tvRemaining.visibility = View.GONE
binding.tvTotalCount.visibility = View.VISIBLE
binding.tvRemainingCount.visibility = View.VISIBLE
binding.tvRemainingCount.text = "${response.orderSequence}"
binding.tvTotalCount.text = " / ${response.totalContentCount}"
} else if (response.remainingContentCount <= 0) {
binding.tvRemainingCount.visibility = View.GONE
binding.tvTotalCount.visibility = View.GONE
binding.tvRemaining.visibility = View.GONE
binding.tvSoldOutSmall.visibility = View.VISIBLE
} else {
binding.tvRemainingCount.visibility = View.VISIBLE
binding.tvRemaining.visibility = View.VISIBLE
binding.tvTotalCount.visibility = View.GONE
binding.tvRemainingCount.text = "${response.remainingContentCount}"
}
} else {
binding.rlLimitedEdition.visibility = View.GONE
}
} }
private fun setupMosaicArea(isMosaic: Boolean) { private fun setupMosaicArea(isMosaic: Boolean) {
@ -769,7 +702,7 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
binding.ivFollow.visibility = View.VISIBLE binding.ivFollow.visibility = View.VISIBLE
if (creator.isFollowing) { if (creator.isFollowing) {
binding.ivFollow.setImageResource(R.drawable.btn_following_big) binding.ivFollow.setImageResource(R.drawable.btn_notification_selected)
binding.ivFollow.setOnClickListener { binding.ivFollow.setOnClickListener {
viewModel.unRegisterNotification( viewModel.unRegisterNotification(
contentId = audioContentId, contentId = audioContentId,
@ -777,7 +710,7 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
) )
} }
} else { } else {
binding.ivFollow.setImageResource(R.drawable.btn_follow_big) binding.ivFollow.setImageResource(R.drawable.btn_notification)
binding.ivFollow.setOnClickListener { binding.ivFollow.setOnClickListener {
viewModel.registerNotification( viewModel.registerNotification(
contentId = audioContentId, contentId = audioContentId,

View File

@ -15,9 +15,6 @@ data class GetAudioContentDetailResponse(
@SerializedName("price") val price: Int, @SerializedName("price") val price: Int,
@SerializedName("duration") val duration: String, @SerializedName("duration") val duration: String,
@SerializedName("releaseDate") val releaseDate: String?, @SerializedName("releaseDate") val releaseDate: String?,
@SerializedName("totalContentCount") val totalContentCount: Int?,
@SerializedName("remainingContentCount") val remainingContentCount: Int?,
@SerializedName("orderSequence") val orderSequence: Int?,
@SerializedName("isActivePreview") val isActivePreview: Boolean, @SerializedName("isActivePreview") val isActivePreview: Boolean,
@SerializedName("isAdult") val isAdult: Boolean, @SerializedName("isAdult") val isAdult: Boolean,
@SerializedName("isMosaic") val isMosaic: Boolean, @SerializedName("isMosaic") val isMosaic: Boolean,

View File

@ -1,37 +0,0 @@
package kr.co.vividnext.sodalive.common
import android.graphics.Rect
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ItemDecoration
class GridSpacingItemDecoration(
private val spanCount: Int,
private val spacing: Int,
private val includeEdge: Boolean
) : ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
val position = parent.getChildAdapterPosition(view) // Item position
val column = position % spanCount // Current column
if (includeEdge) {
outRect.left = spacing - column * spacing / spanCount
outRect.right = (column + 1) * spacing / spanCount
if (position < spanCount) { // Top edge
outRect.top = spacing
}
outRect.bottom = spacing // Item bottom
} else {
outRect.left = column * spacing / spanCount
outRect.right = spacing - (column + 1) * spacing / spanCount
if (position >= spanCount) {
outRect.top = spacing // Item top
}
}
}
}

View File

@ -53,7 +53,6 @@ import kr.co.vividnext.sodalive.live.room.LiveRoomViewModel
import kr.co.vividnext.sodalive.live.room.create.LiveRoomCreateViewModel import kr.co.vividnext.sodalive.live.room.create.LiveRoomCreateViewModel
import kr.co.vividnext.sodalive.live.room.detail.LiveRoomDetailViewModel import kr.co.vividnext.sodalive.live.room.detail.LiveRoomDetailViewModel
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationMessageViewModel import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationMessageViewModel
import kr.co.vividnext.sodalive.live.room.menu.MenuApi
import kr.co.vividnext.sodalive.live.room.tag.LiveTagRepository import kr.co.vividnext.sodalive.live.room.tag.LiveTagRepository
import kr.co.vividnext.sodalive.live.room.tag.LiveTagViewModel import kr.co.vividnext.sodalive.live.room.tag.LiveTagViewModel
import kr.co.vividnext.sodalive.live.room.update.LiveRoomEditViewModel import kr.co.vividnext.sodalive.live.room.update.LiveRoomEditViewModel
@ -74,8 +73,7 @@ import kr.co.vividnext.sodalive.mypage.auth.AuthApi
import kr.co.vividnext.sodalive.mypage.auth.AuthRepository import kr.co.vividnext.sodalive.mypage.auth.AuthRepository
import kr.co.vividnext.sodalive.mypage.can.CanApi import kr.co.vividnext.sodalive.mypage.can.CanApi
import kr.co.vividnext.sodalive.mypage.can.CanRepository import kr.co.vividnext.sodalive.mypage.can.CanRepository
import kr.co.vividnext.sodalive.mypage.can.charge.iap.CanChargeIapViewModel import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeViewModel
import kr.co.vividnext.sodalive.mypage.can.charge.pg.CanChargePgViewModel
import kr.co.vividnext.sodalive.mypage.can.coupon.CanCouponViewModel import kr.co.vividnext.sodalive.mypage.can.coupon.CanCouponViewModel
import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentViewModel import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentViewModel
import kr.co.vividnext.sodalive.mypage.can.status.CanStatusViewModel import kr.co.vividnext.sodalive.mypage.can.status.CanStatusViewModel
@ -153,7 +151,6 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
single { ApiBuilder().build(get(), CanApi::class.java) } single { ApiBuilder().build(get(), CanApi::class.java) }
single { ApiBuilder().build(get(), AuthApi::class.java) } single { ApiBuilder().build(get(), AuthApi::class.java) }
single { ApiBuilder().build(get(), UserApi::class.java) } single { ApiBuilder().build(get(), UserApi::class.java) }
single { ApiBuilder().build(get(), MenuApi::class.java) }
single { ApiBuilder().build(get(), LiveApi::class.java) } single { ApiBuilder().build(get(), LiveApi::class.java) }
single { ApiBuilder().build(get(), TermsApi::class.java) } single { ApiBuilder().build(get(), TermsApi::class.java) }
single { ApiBuilder().build(get(), EventApi::class.java) } single { ApiBuilder().build(get(), EventApi::class.java) }
@ -179,7 +176,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
viewModel { LiveViewModel(get(), get(), get(), get()) } viewModel { LiveViewModel(get(), get(), get(), get()) }
viewModel { MyPageViewModel(get(), get()) } viewModel { MyPageViewModel(get(), get()) }
viewModel { CanStatusViewModel(get()) } viewModel { CanStatusViewModel(get()) }
viewModel { CanChargePgViewModel(get()) } viewModel { CanChargeViewModel(get()) }
viewModel { CanPaymentViewModel(get()) } viewModel { CanPaymentViewModel(get()) }
viewModel { LiveRoomDetailViewModel(get()) } viewModel { LiveRoomDetailViewModel(get()) }
viewModel { LiveRoomCreateViewModel(get()) } viewModel { LiveRoomCreateViewModel(get()) }
@ -232,13 +229,12 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
viewModel { CreatorCommunityWriteViewModel(get()) } viewModel { CreatorCommunityWriteViewModel(get()) }
viewModel { CreatorCommunityModifyViewModel(get()) } viewModel { CreatorCommunityModifyViewModel(get()) }
viewModel { CanCouponViewModel(get()) } viewModel { CanCouponViewModel(get()) }
viewModel { CanChargeIapViewModel(get()) }
} }
private val repositoryModule = module { private val repositoryModule = module {
factory { UserRepository(get()) } factory { UserRepository(get()) }
factory { TermsRepository(get()) } factory { TermsRepository(get()) }
factory { LiveRepository(get(), get(), get()) } factory { LiveRepository(get(), get()) }
factory { EventRepository(get()) } factory { EventRepository(get()) }
factory { LiveRecommendRepository(get()) } factory { LiveRecommendRepository(get()) }
factory { AuthRepository(get()) } factory { AuthRepository(get()) }

View File

@ -615,12 +615,12 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
} }
if (creator.isNotification) { if (creator.isNotification) {
layoutUserProfile.ivNotification.setImageResource(R.drawable.btn_following_big) layoutUserProfile.ivNotification.setImageResource(R.drawable.btn_notification_selected)
layoutUserProfile.ivNotification.setOnClickListener { layoutUserProfile.ivNotification.setOnClickListener {
viewModel.unFollow(creator.creatorId) viewModel.unFollow(creator.creatorId)
} }
} else { } else {
layoutUserProfile.ivNotification.setImageResource(R.drawable.btn_follow_big) layoutUserProfile.ivNotification.setImageResource(R.drawable.btn_notification)
layoutUserProfile.ivNotification.setOnClickListener { layoutUserProfile.ivNotification.setOnClickListener {
viewModel.follow(creator.creatorId) viewModel.follow(creator.creatorId)
} }

View File

@ -31,13 +31,13 @@ class UserFollowerListAdapter(
if (item.isFollow != null) { if (item.isFollow != null) {
binding.ivNotification.visibility = View.VISIBLE binding.ivNotification.visibility = View.VISIBLE
if (item.isFollow) { if (item.isFollow) {
binding.ivNotification.setImageResource(R.drawable.btn_following_big) binding.ivNotification.setImageResource(R.drawable.btn_notification_selected)
binding.ivNotification.setOnClickListener { binding.ivNotification.setOnClickListener {
onClickUnRegisterNotification(item.userId) onClickUnRegisterNotification(item.userId)
clear() clear()
} }
} else { } else {
binding.ivNotification.setImageResource(R.drawable.btn_follow_big) binding.ivNotification.setImageResource(R.drawable.btn_notification)
binding.ivNotification.setOnClickListener { binding.ivNotification.setOnClickListener {
onClickRegisterNotification(item.userId) onClickRegisterNotification(item.userId)
clear() clear()

View File

@ -32,7 +32,7 @@ class FollowingCreatorAdapter(
binding.ivNotification.visibility = View.VISIBLE binding.ivNotification.visibility = View.VISIBLE
if (item.isFollow) { if (item.isFollow) {
binding.ivNotification.setImageResource(R.drawable.btn_following_big) binding.ivNotification.setImageResource(R.drawable.btn_notification_selected)
binding.ivNotification.setOnClickListener { binding.ivNotification.setOnClickListener {
val index = items.indexOf(item) val index = items.indexOf(item)
val copyItem = item.copy(isFollow = false) val copyItem = item.copy(isFollow = false)
@ -42,7 +42,7 @@ class FollowingCreatorAdapter(
onClickUnRegisterNotification(item.creatorId) onClickUnRegisterNotification(item.creatorId)
} }
} else { } else {
binding.ivNotification.setImageResource(R.drawable.btn_follow_big) binding.ivNotification.setImageResource(R.drawable.btn_notification)
binding.ivNotification.setOnClickListener { binding.ivNotification.setOnClickListener {
val index = items.indexOf(item) val index = items.indexOf(item)
val copyItem = item.copy(isFollow = true) val copyItem = item.copy(isFollow = true)

View File

@ -160,7 +160,7 @@ interface LiveApi {
fun donation( fun donation(
@Body request: LiveRoomDonationRequest, @Body request: LiveRoomDonationRequest,
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String
): Single<ApiResponse<String>> ): Single<ApiResponse<Any>>
@POST("/live/room/donation/refund/{id}") @POST("/live/room/donation/refund/{id}")
fun refundDonation( fun refundDonation(

View File

@ -15,7 +15,6 @@ import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailResponse
import kr.co.vividnext.sodalive.live.room.donation.DeleteLiveRoomDonationMessage import kr.co.vividnext.sodalive.live.room.donation.DeleteLiveRoomDonationMessage
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationRequest import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationRequest
import kr.co.vividnext.sodalive.live.room.kick_out.LiveRoomKickOutRequest import kr.co.vividnext.sodalive.live.room.kick_out.LiveRoomKickOutRequest
import kr.co.vividnext.sodalive.live.room.menu.MenuApi
import kr.co.vividnext.sodalive.user.CreatorFollowRequestRequest import kr.co.vividnext.sodalive.user.CreatorFollowRequestRequest
import kr.co.vividnext.sodalive.user.UserApi import kr.co.vividnext.sodalive.user.UserApi
import okhttp3.MultipartBody import okhttp3.MultipartBody
@ -24,8 +23,7 @@ import java.util.TimeZone
class LiveRepository( class LiveRepository(
private val api: LiveApi, private val api: LiveApi,
private val userApi: UserApi, private val userApi: UserApi
private val menuApi: MenuApi
) { ) {
fun roomList( fun roomList(
dateString: String? = null, dateString: String? = null,
@ -163,7 +161,7 @@ class LiveRepository(
can: Int, can: Int,
message: String, message: String,
token: String token: String
): Single<ApiResponse<String>> { ): Single<ApiResponse<Any>> {
return api.donation( return api.donation(
request = LiveRoomDonationRequest( request = LiveRoomDonationRequest(
roomId = roomId, roomId = roomId,
@ -232,9 +230,4 @@ class LiveRepository(
request: CancelLiveReservationRequest, request: CancelLiveReservationRequest,
token: String token: String
) = api.cancelReservation(request, authHeader = token) ) = api.cancelReservation(request, authHeader = token)
fun getAllMenu(creatorId: Long, token: String) = menuApi.getAllMenu(
creatorId = creatorId,
authHeader = token
)
} }

View File

@ -1,21 +1,12 @@
package kr.co.vividnext.sodalive.live.now package kr.co.vividnext.sodalive.live.now
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
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 androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.transform.CircleCropTransformation import coil.transform.CircleCropTransformation
import com.bumptech.glide.Glide import coil.transform.RoundedCornersTransformation
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.databinding.ItemLiveNowBinding import kr.co.vividnext.sodalive.databinding.ItemLiveNowBinding
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
@ -29,28 +20,15 @@ class LiveNowAdapter(
var items = mutableListOf<GetRoomListResponse>() var items = mutableListOf<GetRoomListResponse>()
inner class ViewHolder( inner class ViewHolder(
private val context: Context,
private val binding: ItemLiveNowBinding private val binding: ItemLiveNowBinding
) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root) {
@SuppressLint("SetTextI18n")
fun bind(item: GetRoomListResponse) { fun bind(item: GetRoomListResponse) {
Glide binding.ivCover.loadUrl(item.coverImageUrl) {
.with(context) crossfade(true)
.load(item.coverImageUrl) placeholder(R.drawable.ic_place_holder)
.apply( transformations(RoundedCornersTransformation(4.7f.dpToPx()))
RequestOptions().transform( }
CenterCrop(),
RoundedCorners(8)
)
)
.into(binding.ivCover)
val layoutParams = binding.ivCover.layoutParams as ConstraintLayout.LayoutParams
layoutParams.width = 128f.dpToPx().toInt()
layoutParams.height = 179f.dpToPx().toInt()
binding.ivCover.layoutParams = layoutParams
binding.ivLock.visibility = if (item.isPrivateRoom) { binding.ivLock.visibility = if (item.isPrivateRoom) {
View.VISIBLE View.VISIBLE
} else { } else {
@ -58,18 +36,11 @@ class LiveNowAdapter(
} }
if (item.price > 0) { if (item.price > 0) {
binding.tvPrice.text = "${item.price}" binding.tvPrice.text = "유료"
binding.tvPrice.setCompoundDrawablesWithIntrinsicBounds( binding.tvPrice.setBackgroundResource(R.drawable.bg_round_corner_10_881609)
R.drawable.ic_can_white,
0,
0,
0
)
binding.tvPrice.setBackgroundResource(R.drawable.bg_round_corner_13_3_dd4500)
} else { } else {
binding.tvPrice.text = "무료" binding.tvPrice.text = "무료"
binding.tvPrice.setCompoundDrawables(null, null, null, null) binding.tvPrice.setBackgroundResource(R.drawable.bg_round_corner_10_643bc8)
binding.tvPrice.setBackgroundResource(R.drawable.bg_round_corner_13_3_111111)
} }
if (item.tags.isNotEmpty()) { if (item.tags.isNotEmpty()) {
@ -92,7 +63,6 @@ class LiveNowAdapter(
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
parent.context,
ItemLiveNowBinding.inflate( ItemLiveNowBinding.inflate(
LayoutInflater.from(parent.context), LayoutInflater.from(parent.context),
parent, parent,

View File

@ -2,19 +2,19 @@ package kr.co.vividnext.sodalive.live.now.all
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.graphics.Rect
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.audio_content.AudioContentPlayService import kr.co.vividnext.sodalive.audio_content.AudioContentPlayService
import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants 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.common.LoadingDialog
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.ActivityLiveNowAllBinding import kr.co.vividnext.sodalive.databinding.ActivityLiveNowAllBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.moneyFormat import kr.co.vividnext.sodalive.extensions.moneyFormat
import kr.co.vividnext.sodalive.live.LiveViewModel import kr.co.vividnext.sodalive.live.LiveViewModel
import kr.co.vividnext.sodalive.live.room.LiveRoomActivity import kr.co.vividnext.sodalive.live.room.LiveRoomActivity
@ -46,10 +46,8 @@ class LiveNowAllActivity : BaseActivity<ActivityLiveNowAllBinding>(
binding.toolbar.tvBack.setOnClickListener { finish() } binding.toolbar.tvBack.setOnClickListener { finish() }
loadingDialog = LoadingDialog(this, layoutInflater) loadingDialog = LoadingDialog(this, layoutInflater)
val spanCount = 3
val spacing = 40
val recyclerView = binding.rvLive val recyclerView = binding.rvLive
adapter = LiveNowAllAdapter(itemWidth = (screenWidth - (spacing * (spanCount + 1))) / 3) { adapter = LiveNowAllAdapter {
val detailFragment = LiveRoomDetailFragment( val detailFragment = LiveRoomDetailFragment(
it.roomId, it.roomId,
onClickParticipant = { enterLiveRoom(it.roomId) }, onClickParticipant = { enterLiveRoom(it.roomId) },
@ -65,8 +63,41 @@ class LiveNowAllActivity : BaseActivity<ActivityLiveNowAllBinding>(
) )
} }
recyclerView.layoutManager = GridLayoutManager(this, spanCount) recyclerView.layoutManager = LinearLayoutManager(
recyclerView.addItemDecoration(GridSpacingItemDecoration(spanCount, spacing, false)) applicationContext,
LinearLayoutManager.VERTICAL,
false
)
recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
when (parent.getChildAdapterPosition(view)) {
0 -> {
outRect.top = 0.dpToPx().toInt()
outRect.bottom = 6.7f.dpToPx().toInt()
}
adapter.itemCount - 1 -> {
outRect.top = 6.7f.dpToPx().toInt()
outRect.bottom = 0.dpToPx().toInt()
}
else -> {
outRect.top = 6.7f.dpToPx().toInt()
outRect.bottom = 6.7f.dpToPx().toInt()
}
}
}
})
recyclerView.adapter = adapter
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
@ -82,8 +113,6 @@ class LiveNowAllActivity : BaseActivity<ActivityLiveNowAllBinding>(
} }
}) })
recyclerView.adapter = adapter
binding.swipeRefreshLayout.setOnRefreshListener { binding.swipeRefreshLayout.setOnRefreshListener {
adapter.clear() adapter.clear()
viewModel.page = 1 viewModel.page = 1

View File

@ -2,29 +2,20 @@ package kr.co.vividnext.sodalive.live.now.all
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
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 androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.transform.CircleCropTransformation import coil.load
import coil.transform.RoundedCornersTransformation import coil.transform.RoundedCornersTransformation
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.databinding.ItemLiveNowAllBinding import kr.co.vividnext.sodalive.databinding.ItemLiveNowAllBinding
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.loadUrl import kr.co.vividnext.sodalive.extensions.moneyFormat
import kr.co.vividnext.sodalive.live.GetRoomListResponse import kr.co.vividnext.sodalive.live.GetRoomListResponse
class LiveNowAllAdapter( class LiveNowAllAdapter(
private val itemWidth: Int,
private val onClick: (GetRoomListResponse) -> Unit private val onClick: (GetRoomListResponse) -> Unit
) : RecyclerView.Adapter<LiveNowAllAdapter.ViewHolder>() { ) : RecyclerView.Adapter<LiveNowAllAdapter.ViewHolder>() {
@ -36,72 +27,48 @@ class LiveNowAllAdapter(
) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root) {
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
fun bind(item: GetRoomListResponse) { fun bind(item: GetRoomListResponse) {
Glide binding.ivCover.load(item.coverImageUrl) {
.with(context) crossfade(true)
.load(item.coverImageUrl) placeholder(R.drawable.bg_placeholder)
.apply( transformations(RoundedCornersTransformation(4.7f.dpToPx()))
RequestOptions().transform( }
CenterCrop(), binding.tvNickname.text = item.creatorNickname
RoundedCorners(14) binding.tvTitle.text = item.title
)
)
.into(binding.ivCover)
val layoutParams = binding.ivCover
.layoutParams as ConstraintLayout.LayoutParams
layoutParams.width = itemWidth
layoutParams.height = itemWidth * 144 / 102
binding.ivLock.visibility = if (item.isPrivateRoom) { binding.ivLock.visibility = if (item.isPrivateRoom) {
View.VISIBLE View.VISIBLE
} else { } else {
View.GONE View.GONE
} }
if (item.price > 0) { if (item.numberOfPeople > item.numberOfParticipate) {
binding.tvPrice.text = "${item.price}" binding.tvAvailableParticipate.text = "참여가능"
binding.tvPrice.setCompoundDrawablesWithIntrinsicBounds( binding.tvAvailableParticipate.setTextColor(
R.drawable.ic_can_white, ContextCompat.getColor(
0, context,
0, R.color.color_3bb9f1
0 )
) )
binding.tvPrice.setBackgroundResource(R.drawable.bg_round_corner_13_3_dd4500)
} else { } else {
binding.tvAvailableParticipate.text = "Sold out"
binding.tvAvailableParticipate.setTextColor(
ContextCompat.getColor(
context,
R.color.color_ffd300
)
)
}
if (item.price < 1) {
binding.tvPrice.text = "무료" binding.tvPrice.text = "무료"
binding.tvPrice.setCompoundDrawables(null, null, null, null) binding.tvPrice.setCompoundDrawables(null, null, null, null)
binding.tvPrice.setBackgroundResource(R.drawable.bg_round_corner_13_3_111111)
}
if (item.tags.isNotEmpty()) {
binding.tvTags.visibility = View.VISIBLE
binding.tvTags.text = item.tags.joinToString(" ") { "#$it" }
} else { } else {
binding.tvTags.visibility = View.GONE binding.tvPrice.text = item.price.moneyFormat()
} binding.tvPrice.setCompoundDrawablesWithIntrinsicBounds(
0,
binding.tvTitle.text = item.title 0,
binding.tvNickname.text = item.creatorNickname R.drawable.ic_can,
binding.ivProfile.loadUrl(item.creatorProfileImage) { 0
crossfade(true) )
placeholder(R.drawable.ic_place_holder)
transformations(CircleCropTransformation())
}
if (item.numberOfPeople - item.numberOfParticipate <= 2) {
binding.llRemainingParticipant.visibility = View.VISIBLE
if (item.numberOfPeople > item.numberOfParticipate) {
binding.tvRemainingParticipantNumber.visibility = View.VISIBLE
binding.tvRemainingParticipant.text = "잔여"
binding.tvRemainingParticipantNumber.text =
"${item.numberOfPeople - item.numberOfParticipate}"
} else {
binding.tvRemainingParticipantNumber.visibility = View.GONE
binding.tvRemainingParticipant.text = "Sold out"
binding.tvRemainingParticipantNumber.text = ""
}
} else {
binding.llRemainingParticipant.visibility = View.GONE
} }
binding.root.setOnClickListener { onClick(item) } binding.root.setOnClickListener { onClick(item) }

View File

@ -26,11 +26,12 @@ import android.widget.Toast
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.load
import coil.transform.CircleCropTransformation import coil.transform.CircleCropTransformation
import com.bumptech.glide.Glide
import com.github.dhaval2404.imagepicker.ImagePicker import com.github.dhaval2404.imagepicker.ImagePicker
import com.google.gson.Gson import com.google.gson.Gson
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
@ -114,18 +115,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
private var isNoChatting = false private var isNoChatting = false
private var remainingNoChattingTime = noChattingTime private var remainingNoChattingTime = noChattingTime
private val signatureImageUrlList = mutableListOf<String>()
private var signatureImageUrl = ""
set(value) {
field = value
if (field.isNotBlank()) {
showSignatureImage()
}
}
private var isShowSignatureImage = false
private val countDownTimer = object : CountDownTimer(remainingNoChattingTime * 1000, 1000) { private val countDownTimer = object : CountDownTimer(remainingNoChattingTime * 1000, 1000) {
override fun onTick(millisUntilFinished: Long) { override fun onTick(millisUntilFinished: Long) {
remainingNoChattingTime -= 1 remainingNoChattingTime -= 1
@ -465,7 +454,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
dialog.show(screenWidth) dialog.show(screenWidth)
} }
binding.tvNotification.setOnClickListener { viewModel.toggleShowNotice() } binding.tvNotification.setOnClickListener { viewModel.toggleShowNotice() }
binding.tvMenuPan.setOnClickListener { viewModel.toggleShowMenuPan() }
binding.tvBgSwitch.setOnClickListener { viewModel.toggleBackgroundImage() } binding.tvBgSwitch.setOnClickListener { viewModel.toggleBackgroundImage() }
binding.llDonation.setOnClickListener { binding.llDonation.setOnClickListener {
@ -708,44 +696,37 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
if (response.creatorId == SharedPreferenceManager.userId) { if (response.creatorId == SharedPreferenceManager.userId) {
binding.ivEdit.setOnClickListener { binding.ivEdit.setOnClickListener {
viewModel.getAllMenuPreset { roomInfoEditDialog.setRoomInfo(response.title, response.notice)
roomInfoEditDialog.setRoomInfo(response.title, response.notice) roomInfoEditDialog.setCoverImageUrl(response.coverImageUrl)
roomInfoEditDialog.setCoverImageUrl(response.coverImageUrl) roomInfoEditDialog.setConfirmAction { newTitle, newContent, newCoverImageUri ->
roomInfoEditDialog.setMenuPreset(it) viewModel.editLiveRoomInfo(
roomInfoEditDialog.setConfirmAction { newTitle, newContent, newCoverImageUri, isActivateMenu, menuId, menu -> response.roomId,
viewModel.editLiveRoomInfo( newTitle,
response.roomId, newContent,
newTitle, newCoverImageUri,
newContent, onSuccess = {
newCoverImageUri, binding.tvTitle.text = newTitle
isActivateMenu, setNoticeAndClickableUrl(binding.tvNotice, newContent)
menuId,
menu,
onSuccess = {
Toast.makeText(
applicationContext,
"라이브 정보가 수정되었습니다.",
Toast.LENGTH_LONG
).show()
agora.sendRawMessageToGroup( if (newCoverImageUri != null) {
rawMessage = Gson().toJson( binding.ivCover.load(newCoverImageUri)
LiveRoomChatRawMessage(
type = LiveRoomChatRawMessageType.EDIT_ROOM_INFO,
message = "",
can = 0,
donationMessage = ""
)
).toByteArray()
)
} }
)
}
handler.post { agora.sendRawMessageToGroup(
roomInfoEditDialog.show(screenWidth) rawMessage = Gson().toJson(
} LiveRoomChatRawMessage(
type = LiveRoomChatRawMessageType.EDIT_ROOM_INFO,
message = "",
can = 0,
donationMessage = ""
)
).toByteArray()
)
}
)
} }
roomInfoEditDialog.show(screenWidth)
} }
binding.ivEdit.visibility = View.VISIBLE binding.ivEdit.visibility = View.VISIBLE
@ -800,7 +781,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
if (response.creatorId != SharedPreferenceManager.userId) { if (response.creatorId != SharedPreferenceManager.userId) {
binding.ivCreatorFollow.visibility = View.VISIBLE binding.ivCreatorFollow.visibility = View.VISIBLE
if (response.isFollowing) { if (response.isFollowing) {
binding.ivCreatorFollow.setImageResource(R.drawable.btn_following) binding.ivCreatorFollow.setImageResource(R.drawable.btn_select_checked)
binding.ivCreatorFollow.setOnClickListener { binding.ivCreatorFollow.setOnClickListener {
viewModel.creatorUnFollow( viewModel.creatorUnFollow(
creatorId = response.creatorId, creatorId = response.creatorId,
@ -808,7 +789,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
) )
} }
} else { } else {
binding.ivCreatorFollow.setImageResource(R.drawable.btn_follow) binding.ivCreatorFollow.setImageResource(R.drawable.btn_plus_round)
binding.ivCreatorFollow.setOnClickListener { binding.ivCreatorFollow.setOnClickListener {
viewModel.creatorFollow( viewModel.creatorFollow(
creatorId = response.creatorId, creatorId = response.creatorId,
@ -826,15 +807,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
isActiveRoulette = response.isActiveRoulette isActiveRoulette = response.isActiveRoulette
) )
if (response.menuPan.isNotBlank()) {
binding.tvMenuPan.visibility = View.VISIBLE
binding.tvMenuPanDetail.text = response.menuPan
} else {
viewModel.toggleShowMenuPan(false)
binding.tvMenuPan.visibility = View.GONE
binding.tvMenuPanDetail.text = ""
}
if (agora.rtmChannelIsNull()) { if (agora.rtmChannelIsNull()) {
joinChannel(response) joinChannel(response)
} }
@ -868,34 +840,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
} }
} }
viewModel.isShowMenuPan.observe(this) {
if (it) {
binding.tvMenuPan.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_3bb9f1
)
)
binding.tvMenuPan.setBackgroundResource(
R.drawable.bg_round_corner_5_3_transparent_3bb9f1
)
binding.llMenuPan.visibility = View.VISIBLE
} else {
binding.tvMenuPan.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_bbbbbb
)
)
binding.tvMenuPan.setBackgroundResource(
R.drawable.bg_round_corner_5_3_transparent_bbbbbb
)
binding.llMenuPan.visibility = View.GONE
}
}
viewModel.totalDonationCan.observe(this) { viewModel.totalDonationCan.observe(this) {
binding.tvTotalCan.text = it.moneyFormat() binding.tvTotalCan.text = it.moneyFormat()
} }
@ -1082,8 +1026,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
agora.muteLocalAudioStream(false) agora.muteLocalAudioStream(false)
agora.setClientRole(io.agora.rtc2.Constants.CLIENT_ROLE_AUDIENCE) agora.setClientRole(io.agora.rtc2.Constants.CLIENT_ROLE_AUDIENCE)
handler.postDelayed({ handler.postDelayed({
binding.tvChangeListener.visibility = View.GONE
binding.tvChangeListener.setOnClickListener { }
binding.ivMicrophoneMute.setImageResource(R.drawable.ic_mic_on) binding.ivMicrophoneMute.setImageResource(R.drawable.ic_mic_on)
binding.flMicrophoneMute.visibility = View.GONE binding.flMicrophoneMute.visibility = View.GONE
binding.ivNotiMicrophoneMute.visibility = View.GONE binding.ivNotiMicrophoneMute.visibility = View.GONE
@ -1269,18 +1211,16 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
private fun donation(can: Int, message: String) { private fun donation(can: Int, message: String) {
val rawMessage = "${can}캔을 후원하셨습니다.\uD83D\uDCB0\uD83E\uDE99" val rawMessage = "${can}캔을 후원하셨습니다.\uD83D\uDCB0\uD83E\uDE99"
val donationRawMessage = Gson().toJson(
viewModel.donation(roomId, can, message) { signatureImage -> LiveRoomChatRawMessage(
val donationRawMessage = Gson().toJson( type = LiveRoomChatRawMessageType.DONATION,
LiveRoomChatRawMessage( message = rawMessage,
type = LiveRoomChatRawMessageType.DONATION, can = can,
message = rawMessage, donationMessage = message
can = can,
signatureImageUrl = signatureImage,
donationMessage = message
)
) )
)
viewModel.donation(roomId, can, message) {
agora.sendRawMessageToGroup( agora.sendRawMessageToGroup(
rawMessage = donationRawMessage.toByteArray(), rawMessage = donationRawMessage.toByteArray(),
onSuccess = { onSuccess = {
@ -1300,7 +1240,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
) )
invalidateChat() invalidateChat()
viewModel.addDonationCan(can) viewModel.addDonationCan(can)
addSignatureImage(signatureImage)
} }
}, },
onFailure = { onFailure = {
@ -1407,9 +1346,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
) )
invalidateChat() invalidateChat()
viewModel.addDonationCan(rawMessage.can) viewModel.addDonationCan(rawMessage.can)
addSignatureImage(
imageUrl = rawMessage.signatureImageUrl ?: ""
)
} }
} }
@ -1686,18 +1622,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
showDialog(content = "스피커가 되었어요!") showDialog(content = "스피커가 되었어요!")
setBroadcaster() setBroadcaster()
viewModel.getRoomInfo(roomId) viewModel.getRoomInfo(roomId)
binding.tvChangeListener.visibility = View.VISIBLE
binding.tvChangeListener.setOnClickListener {
handler.post {
viewModel.setListener(
roomId,
SharedPreferenceManager.userId
) {
setAudience()
viewModel.getRoomInfo(roomId)
}
}
}
} }
} }
} }
@ -1751,38 +1675,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
} }
} }
private fun addSignatureImage(imageUrl: String) {
if (imageUrl.isNotBlank()) {
if (!isShowSignatureImage) {
isShowSignatureImage = true
signatureImageUrl = imageUrl
} else {
signatureImageUrlList.add(imageUrl)
}
}
}
private fun showSignatureImage() {
if (signatureImageUrl.isNotBlank()) {
Glide
.with(this)
.load(signatureImageUrl)
.into(binding.ivSignature)
binding.ivSignature.visibility = View.VISIBLE
handler.postDelayed({
if (signatureImageUrlList.isNotEmpty()) {
signatureImageUrl = signatureImageUrlList.removeAt(0)
} else {
signatureImageUrl = ""
isShowSignatureImage = false
binding.ivSignature.setImageDrawable(null)
binding.ivSignature.visibility = View.GONE
}
}, 7000)
}
}
companion object { companion object {
private const val noChattingTime = 180L private const val noChattingTime = 180L
} }

View File

@ -18,7 +18,6 @@ import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.live.LiveRepository import kr.co.vividnext.sodalive.live.LiveRepository
import kr.co.vividnext.sodalive.live.room.donation.GetLiveRoomDonationStatusResponse import kr.co.vividnext.sodalive.live.room.donation.GetLiveRoomDonationStatusResponse
import kr.co.vividnext.sodalive.live.room.info.GetRoomInfoResponse import kr.co.vividnext.sodalive.live.room.info.GetRoomInfoResponse
import kr.co.vividnext.sodalive.live.room.menu.GetMenuPresetResponse
import kr.co.vividnext.sodalive.live.room.profile.GetLiveRoomUserProfileResponse import kr.co.vividnext.sodalive.live.room.profile.GetLiveRoomUserProfileResponse
import kr.co.vividnext.sodalive.live.room.update.EditLiveRoomInfoRequest import kr.co.vividnext.sodalive.live.room.update.EditLiveRoomInfoRequest
import kr.co.vividnext.sodalive.live.roulette.RouletteItem import kr.co.vividnext.sodalive.live.roulette.RouletteItem
@ -55,10 +54,6 @@ class LiveRoomViewModel(
val isShowNotice: LiveData<Boolean> val isShowNotice: LiveData<Boolean>
get() = _isShowNotice get() = _isShowNotice
private val _isShowMenuPan = MutableLiveData(false)
val isShowMenuPan: LiveData<Boolean>
get() = _isShowMenuPan
private val _totalDonationCan = MutableLiveData(0) private val _totalDonationCan = MutableLiveData(0)
val totalDonationCan: LiveData<Int> val totalDonationCan: LiveData<Int>
get() = _totalDonationCan get() = _totalDonationCan
@ -360,20 +355,9 @@ class LiveRoomViewModel(
} }
fun toggleShowNotice() { fun toggleShowNotice() {
_isShowMenuPan.value = false
_isShowNotice.value = !isShowNotice.value!! _isShowNotice.value = !isShowNotice.value!!
} }
fun toggleShowMenuPan(isShowMenuPan: Boolean? = null) {
_isShowNotice.value = false
if (isShowMenuPan != null) {
_isShowMenuPan.value = isShowMenuPan
} else {
_isShowMenuPan.value = !this.isShowMenuPan.value!!
}
}
fun toggleBackgroundImage() { fun toggleBackgroundImage() {
_isBgOn.value = !isBgOn.value!! _isBgOn.value = !isBgOn.value!!
} }
@ -383,9 +367,6 @@ class LiveRoomViewModel(
newTitle: String, newTitle: String,
newContent: String, newContent: String,
newCoverImageUri: Uri? = null, newCoverImageUri: Uri? = null,
isActivateMenu: Boolean?,
menuId: Long,
menu: String,
onSuccess: () -> Unit onSuccess: () -> Unit
) { ) {
val request = EditLiveRoomInfoRequest( val request = EditLiveRoomInfoRequest(
@ -401,25 +382,17 @@ class LiveRoomViewModel(
}, },
numberOfPeople = null, numberOfPeople = null,
beginDateTimeString = null, beginDateTimeString = null,
timezone = null, timezone = null
menuPanId = if (isActivateMenu == true) menuId else 0,
menuPan = if (isActivateMenu == true) menu else "",
isActiveMenuPan = isActivateMenu
) )
val requestJson = if ( val requestJson = if (request.title != null || request.notice != null) {
request.title != null ||
request.notice != null ||
request.isActiveMenuPan != null ||
request.menuPan.isNotBlank()
) {
Gson().toJson(request) Gson().toJson(request)
} else { } else {
null null
} }
val coverImage = if (newCoverImageUri != null) { val coverImage = if (newCoverImageUri != null) {
val file = File(getRealPathFromURI(newCoverImageUri)) val file = File(getRealPathFromURI(newCoverImageUri!!))
MultipartBody.Part.createFormData( MultipartBody.Part.createFormData(
"coverImage", "coverImage",
file.name, file.name,
@ -508,7 +481,7 @@ class LiveRoomViewModel(
) )
} }
fun donation(roomId: Long, can: Int, message: String, onSuccess: (String) -> Unit) { fun donation(roomId: Long, can: Int, message: String, onSuccess: () -> Unit) {
_isLoading.postValue(true) _isLoading.postValue(true)
compositeDisposable.add( compositeDisposable.add(
repository.donation(roomId, can, message, "Bearer ${SharedPreferenceManager.token}") repository.donation(roomId, can, message, "Bearer ${SharedPreferenceManager.token}")
@ -519,7 +492,7 @@ class LiveRoomViewModel(
_isLoading.value = false _isLoading.value = false
if (it.success) { if (it.success) {
SharedPreferenceManager.can -= can SharedPreferenceManager.can -= can
onSuccess(it.data ?: "") onSuccess()
} else { } else {
if (it.message != null) { if (it.message != null) {
_toastLiveData.postValue(it.message) _toastLiveData.postValue(it.message)
@ -927,40 +900,6 @@ class LiveRoomViewModel(
) )
} }
fun getAllMenuPreset(onSuccess: (List<GetMenuPresetResponse>) -> Unit) {
_isLoading.value = true
compositeDisposable.add(
repository.getAllMenu(
creatorId = SharedPreferenceManager.userId,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success) {
onSuccess(it.data ?: listOf())
} 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("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
private fun randomSelectRouletteItem( private fun randomSelectRouletteItem(
can: Int, can: Int,
items: List<RouletteItem>, items: List<RouletteItem>,

View File

@ -257,7 +257,7 @@ data class LiveRoomDonationChat(
) )
), ),
0, 0,
chat.indexOf("", 0, true) + 1, chat.indexOf("", 0, true) + 2,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
) )

View File

@ -6,7 +6,6 @@ data class LiveRoomChatRawMessage(
@SerializedName("type") val type: LiveRoomChatRawMessageType, @SerializedName("type") val type: LiveRoomChatRawMessageType,
@SerializedName("message") val message: String, @SerializedName("message") val message: String,
@SerializedName("can") val can: Int, @SerializedName("can") val can: Int,
@SerializedName("signatureImageUrl") val signatureImageUrl: String? = null,
@SerializedName("donationMessage") val donationMessage: String?, @SerializedName("donationMessage") val donationMessage: String?,
@SerializedName("isActiveRoulette") val isActiveRoulette: Boolean? = null @SerializedName("isActiveRoulette") val isActiveRoulette: Boolean? = null
) )

View File

@ -14,8 +14,5 @@ data class CreateLiveRoomRequest(
@SerializedName("beginDateTimeString") val beginDateTimeString: String? = null, @SerializedName("beginDateTimeString") val beginDateTimeString: String? = null,
@SerializedName("timezone") val timezone: String, @SerializedName("timezone") val timezone: String,
@SerializedName("type") val type: LiveRoomType, @SerializedName("type") val type: LiveRoomType,
@SerializedName("password") val password: String? = null, @SerializedName("password") val password: String? = null
@SerializedName("menuPanId") val menuPanId: Long = 0,
@SerializedName("menuPan") val menuPan: String = "",
@SerializedName("isActiveMenuPan") val isActiveMenuPan: Boolean = false
) )

View File

@ -9,7 +9,6 @@ import android.os.Handler
import android.os.Looper import android.os.Looper
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View import android.view.View
import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
@ -128,8 +127,6 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
viewModel.setTimeNow( viewModel.setTimeNow(
intent.getBooleanExtra(Constants.EXTRA_LIVE_TIME_NOW, true) intent.getBooleanExtra(Constants.EXTRA_LIVE_TIME_NOW, true)
) )
viewModel.getAllMenuPreset()
} }
@SuppressLint("SetTextI18n", "ClickableViewAccessibility") @SuppressLint("SetTextI18n", "ClickableViewAccessibility")
@ -298,30 +295,6 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
} }
false false
} }
binding.etMenu.setOnTouchListener { view, motionEvent ->
view.parent.parent.requestDisallowInterceptTouchEvent(true)
if ((motionEvent.action and MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
view.parent.parent.requestDisallowInterceptTouchEvent(false)
}
false
}
binding.ivSwitch.setOnClickListener {
viewModel.toggleIsActivateMenu()
}
binding.llSelectMenu1.setOnClickListener {
viewModel.selectMenuPreset(LiveRoomCreateViewModel.SelectedMenu.MENU_1)
}
binding.llSelectMenu2.setOnClickListener {
viewModel.selectMenuPreset(LiveRoomCreateViewModel.SelectedMenu.MENU_2)
}
binding.llSelectMenu3.setOnClickListener {
viewModel.selectMenuPreset(LiveRoomCreateViewModel.SelectedMenu.MENU_3)
}
} }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
@ -375,15 +348,6 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
} }
) )
compositeDisposable.add(
binding.etMenu.textChanges().skip(1)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
viewModel.menu = it.toString()
}
)
viewModel.toastLiveData.observe(this) { viewModel.toastLiveData.observe(this) {
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() } it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
} }
@ -609,44 +573,6 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
else -> binding.rlPrice.isSelected = true else -> binding.rlPrice.isSelected = true
} }
} }
viewModel.isActivateMenuLiveData.observe(this) {
if (it) {
binding.llEditMenu.visibility = View.VISIBLE
binding.ivSwitch.setImageResource(R.drawable.btn_toggle_on_big)
} else {
binding.llEditMenu.visibility = View.GONE
binding.ivSwitch.setImageResource(R.drawable.btn_toggle_off_big)
}
}
viewModel.selectedMenuLiveData.observe(this) {
deselectAllMenuPreset()
when(it) {
LiveRoomCreateViewModel.SelectedMenu.MENU_2 -> selectMenuPresetButton(
binding.ivSelectMenu2,
binding.llSelectMenu2,
binding.tvSelectMenu2
)
LiveRoomCreateViewModel.SelectedMenu.MENU_3 -> selectMenuPresetButton(
binding.ivSelectMenu3,
binding.llSelectMenu3,
binding.tvSelectMenu3
)
else -> selectMenuPresetButton(
binding.ivSelectMenu1,
binding.llSelectMenu1,
binding.tvSelectMenu1
)
}
}
viewModel.menuLiveData.observe(this) {
binding.etMenu.setText(it)
}
} }
} }
@ -687,69 +613,4 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
R.font.gmarket_sans_bold R.font.gmarket_sans_bold
) )
} }
private fun deselectAllMenuPreset() {
binding.ivSelectMenu1.visibility = View.GONE
binding.ivSelectMenu2.visibility = View.GONE
binding.ivSelectMenu3.visibility = View.GONE
binding.llSelectMenu1.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b)
binding.tvSelectMenu1.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_3bb9f1
)
)
if (viewModel.menuList.size > 0) {
binding.llSelectMenu2.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b)
binding.tvSelectMenu2.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_3bb9f1
)
)
} else {
binding.llSelectMenu2.setBackgroundResource(R.drawable.bg_round_corner_6_7_777777)
binding.tvSelectMenu2.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_555555
)
)
}
if (viewModel.menuList.size > 1) {
binding.llSelectMenu3.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b)
binding.tvSelectMenu3.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_3bb9f1
)
)
} else {
binding.llSelectMenu3.setBackgroundResource(R.drawable.bg_round_corner_6_7_777777)
binding.tvSelectMenu3.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_555555
)
)
}
}
private fun selectMenuPresetButton(
ivSelectMenuPreset: ImageView,
llSelectMenuPreset: LinearLayout,
tvSelectMenuPreset: TextView
) {
ivSelectMenuPreset.visibility = View.VISIBLE
llSelectMenuPreset.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
tvSelectMenuPreset.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_eeeeee
)
)
}
} }

View File

@ -11,7 +11,6 @@ import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.live.LiveRepository import kr.co.vividnext.sodalive.live.LiveRepository
import kr.co.vividnext.sodalive.live.room.LiveRoomType import kr.co.vividnext.sodalive.live.room.LiveRoomType
import kr.co.vividnext.sodalive.live.room.menu.GetMenuPresetResponse
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody import okhttp3.RequestBody.Companion.asRequestBody
@ -23,10 +22,6 @@ class LiveRoomCreateViewModel(
private val repository: LiveRepository private val repository: LiveRepository
) : BaseViewModel() { ) : BaseViewModel() {
enum class SelectedMenu {
MENU_1, MENU_2, MENU_3
}
private val _roomTypeLiveData = MutableLiveData(LiveRoomType.OPEN) private val _roomTypeLiveData = MutableLiveData(LiveRoomType.OPEN)
val roomTypeLiveData: LiveData<LiveRoomType> val roomTypeLiveData: LiveData<LiveRoomType>
get() = _roomTypeLiveData get() = _roomTypeLiveData
@ -63,18 +58,6 @@ class LiveRoomCreateViewModel(
val isAdultLiveData: LiveData<Boolean> val isAdultLiveData: LiveData<Boolean>
get() = _isAdultLiveData get() = _isAdultLiveData
private val _selectedMenuLiveData = MutableLiveData<SelectedMenu>()
val selectedMenuLiveData: LiveData<SelectedMenu>
get() = _selectedMenuLiveData
private val _isActivateMenuLiveData = MutableLiveData(false)
val isActivateMenuLiveData: LiveData<Boolean>
get() = _isActivateMenuLiveData
private val _menuLiveData = MutableLiveData("")
val menuLiveData: LiveData<String>
get() = _menuLiveData
lateinit var getRealPathFromURI: (Uri) -> String? lateinit var getRealPathFromURI: (Uri) -> String?
var title = "" var title = ""
@ -87,10 +70,6 @@ class LiveRoomCreateViewModel(
var coverImagePath: String? = null var coverImagePath: String? = null
var password: String? = null var password: String? = null
private var menuId = 0L
var menu = ""
val menuList = mutableListOf<GetMenuPresetResponse>()
fun setRoomType(roomType: LiveRoomType) { fun setRoomType(roomType: LiveRoomType) {
if (_roomTypeLiveData.value!! != roomType) { if (_roomTypeLiveData.value!! != roomType) {
_roomTypeLiveData.postValue(roomType) _roomTypeLiveData.postValue(roomType)
@ -136,18 +115,7 @@ class LiveRoomCreateViewModel(
password password
} else { } else {
null null
}, }
menuPanId = if (_isActivateMenuLiveData.value!!) {
menuId
} else {
0
},
menuPan = if (_isActivateMenuLiveData.value!!) {
menu
} else {
""
},
isActiveMenuPan = _isActivateMenuLiveData.value!!
) )
val requestJson = Gson().toJson(request) val requestJson = Gson().toJson(request)
@ -239,11 +207,6 @@ class LiveRoomCreateViewModel(
return false return false
} }
if (_isActivateMenuLiveData.value!! && menu.isBlank()) {
_toastLiveData.postValue("메뉴판은 빈칸일 수 없습니다.")
return false
}
return true return true
} }
@ -289,80 +252,4 @@ class LiveRoomCreateViewModel(
) )
) )
} }
fun selectMenuPreset(selectedMenuPreset: SelectedMenu) {
if (
menuList.isEmpty() &&
(
selectedMenuPreset == SelectedMenu.MENU_2 ||
selectedMenuPreset == SelectedMenu.MENU_3
)
) {
_toastLiveData.value = "메뉴 1을 먼저 설정하세요"
return
}
if (menuList.size == 1 && selectedMenuPreset == SelectedMenu.MENU_3) {
_toastLiveData.value = "메뉴 1과 메뉴 2를 먼저 설정하세요"
return
}
if (_selectedMenuLiveData.value != selectedMenuPreset) {
_selectedMenuLiveData.value = selectedMenuPreset
if (menuList.size > selectedMenuPreset.ordinal) {
val menuPreset = menuList[selectedMenuPreset.ordinal]
_menuLiveData.value = menuPreset.menu
menu = menuPreset.menu
menuId = menuPreset.id
} else {
_menuLiveData.value = ""
menu = ""
menuId = 0
}
}
}
fun toggleIsActivateMenu() {
_isActivateMenuLiveData.value = !_isActivateMenuLiveData.value!!
}
fun getAllMenuPreset() {
_isLoading.value = true
compositeDisposable.add(
repository.getAllMenu(
creatorId = SharedPreferenceManager.userId,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success) {
val data = it.data
menuList.clear()
menuList.addAll(data ?: listOf())
selectMenuPreset(SelectedMenu.MENU_1)
} 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

@ -21,7 +21,6 @@ data class GetRoomInfoResponse(
@SerializedName("listenerList") val listenerList: List<LiveRoomMember>, @SerializedName("listenerList") val listenerList: List<LiveRoomMember>,
@SerializedName("managerList") val managerList: List<LiveRoomMember>, @SerializedName("managerList") val managerList: List<LiveRoomMember>,
@SerializedName("donationRankingTop3UserIds") val donationRankingTop3UserIds: List<Long>, @SerializedName("donationRankingTop3UserIds") val donationRankingTop3UserIds: List<Long>,
@SerializedName("menuPan") val menuPan: String,
@SerializedName("isActiveRoulette") val isActiveRoulette: Boolean, @SerializedName("isActiveRoulette") val isActiveRoulette: Boolean,
@SerializedName("isPrivateRoom") val isPrivateRoom: Boolean, @SerializedName("isPrivateRoom") val isPrivateRoom: Boolean,
@SerializedName("password") val password: String? = null @SerializedName("password") val password: String? = null

View File

@ -1,9 +0,0 @@
package kr.co.vividnext.sodalive.live.room.menu
import com.google.gson.annotations.SerializedName
data class GetMenuPresetResponse(
@SerializedName("id") val id: Long,
@SerializedName("menu") val menu: String,
@SerializedName("isActive") val isActive: Boolean
)

View File

@ -1,15 +0,0 @@
package kr.co.vividnext.sodalive.live.room.menu
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 MenuApi {
@GET("/live/room/menu/all")
fun getAllMenu(
@Query("creatorId") creatorId: Long,
@Header("Authorization") authHeader: String
): Single<ApiResponse<List<GetMenuPresetResponse>>>
}

View File

@ -7,8 +7,5 @@ data class EditLiveRoomInfoRequest(
@SerializedName("notice") val notice: String?, @SerializedName("notice") val notice: String?,
@SerializedName("numberOfPeople") val numberOfPeople: Int?, @SerializedName("numberOfPeople") val numberOfPeople: Int?,
@SerializedName("beginDateTimeString") val beginDateTimeString: String?, @SerializedName("beginDateTimeString") val beginDateTimeString: String?,
@SerializedName("timezone") val timezone: String?, @SerializedName("timezone") val timezone: String?
@SerializedName("menuPanId") val menuPanId: Long = 0,
@SerializedName("menuPan") val menuPan: String = "",
@SerializedName("isActiveMenuPan") val isActiveMenuPan: Boolean? = null
) )

View File

@ -1,32 +1,20 @@
package kr.co.vividnext.sodalive.live.room.update package kr.co.vividnext.sodalive.live.room.update
import android.annotation.SuppressLint import android.app.Activity
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.net.Uri import android.net.Uri
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.WindowManager import android.view.WindowManager
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.lifecycle.MutableLiveData
import coil.load import coil.load
import coil.transform.RoundedCornersTransformation import coil.transform.RoundedCornersTransformation
import kr.co.vividnext.sodalive.R import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.databinding.DialogLiveRoomInfoUpdateBinding import kr.co.vividnext.sodalive.databinding.DialogLiveRoomInfoUpdateBinding
import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.live.room.create.LiveRoomCreateViewModel
import kr.co.vividnext.sodalive.live.room.menu.GetMenuPresetResponse
@SuppressLint("ClickableViewAccessibility")
class LiveRoomInfoEditDialog( class LiveRoomInfoEditDialog(
private val activity: AppCompatActivity, activity: Activity,
layoutInflater: LayoutInflater, layoutInflater: LayoutInflater,
onClickImagePicker: () -> Unit onClickImagePicker: () -> Unit
) { ) {
@ -36,17 +24,6 @@ class LiveRoomInfoEditDialog(
private var coverImageUrl: String? = null private var coverImageUrl: String? = null
private var coverImageUri: Uri? = null private var coverImageUri: Uri? = null
private var menuId = 0L
private val menuList = mutableListOf<GetMenuPresetResponse>()
private val isActivateMenuLiveData = MutableLiveData(false)
private val selectedMenuLiveData = MutableLiveData<LiveRoomCreateViewModel.SelectedMenu>()
private var menu: String = ""
private var isActivateMenu: Boolean? = null
private lateinit var selectedMenu: LiveRoomCreateViewModel.SelectedMenu
init { init {
val dialogBuilder = AlertDialog.Builder(activity) val dialogBuilder = AlertDialog.Builder(activity)
dialogBuilder.setView(dialogView.root) dialogBuilder.setView(dialogView.root)
@ -58,77 +35,6 @@ class LiveRoomInfoEditDialog(
dialogView.ivPhotoPicker.setOnClickListener { onClickImagePicker() } dialogView.ivPhotoPicker.setOnClickListener { onClickImagePicker() }
dialogView.ivClose.setOnClickListener { alertDialog.dismiss() } dialogView.ivClose.setOnClickListener { alertDialog.dismiss() }
dialogView.tvCancel.setOnClickListener { alertDialog.dismiss() } dialogView.tvCancel.setOnClickListener { alertDialog.dismiss() }
dialogView.etNotice.setOnTouchListener { view, motionEvent ->
view.parent.parent.requestDisallowInterceptTouchEvent(true)
if ((motionEvent.action and MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
view.parent.parent.requestDisallowInterceptTouchEvent(false)
}
false
}
dialogView.etMenu.setOnTouchListener { view, motionEvent ->
view.parent.parent.requestDisallowInterceptTouchEvent(true)
if ((motionEvent.action and MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
view.parent.parent.requestDisallowInterceptTouchEvent(false)
}
false
}
dialogView.ivSwitch.setOnClickListener {
isActivateMenuLiveData.value = !isActivateMenuLiveData.value!!
isActivateMenu = isActivateMenuLiveData.value!!
if (selectedMenuLiveData.value == null) {
selectMenuPreset(LiveRoomCreateViewModel.SelectedMenu.MENU_1)
}
}
dialogView.llSelectMenu1.setOnClickListener {
selectMenuPreset(LiveRoomCreateViewModel.SelectedMenu.MENU_1)
}
dialogView.llSelectMenu2.setOnClickListener {
selectMenuPreset(LiveRoomCreateViewModel.SelectedMenu.MENU_2)
}
dialogView.llSelectMenu3.setOnClickListener {
selectMenuPreset(LiveRoomCreateViewModel.SelectedMenu.MENU_3)
}
selectedMenuLiveData.observe(activity) {
deselectAllMenuPreset()
when (it) {
LiveRoomCreateViewModel.SelectedMenu.MENU_2 -> selectMenuPresetButton(
dialogView.ivSelectMenu2,
dialogView.llSelectMenu2,
dialogView.tvSelectMenu2
)
LiveRoomCreateViewModel.SelectedMenu.MENU_3 -> selectMenuPresetButton(
dialogView.ivSelectMenu3,
dialogView.llSelectMenu3,
dialogView.tvSelectMenu3
)
else -> selectMenuPresetButton(
dialogView.ivSelectMenu1,
dialogView.llSelectMenu1,
dialogView.tvSelectMenu1
)
}
}
isActivateMenuLiveData.observe(activity) {
if (it) {
dialogView.llEditMenu.visibility = View.VISIBLE
dialogView.ivSwitch.setImageResource(R.drawable.btn_toggle_on_big)
} else {
dialogView.llEditMenu.visibility = View.GONE
dialogView.ivSwitch.setImageResource(R.drawable.btn_toggle_off_big)
}
}
} }
fun setRoomInfo( fun setRoomInfo(
@ -136,7 +42,7 @@ class LiveRoomInfoEditDialog(
currentContent: String, currentContent: String,
) { ) {
dialogView.etTitle.setText(currentTitle) dialogView.etTitle.setText(currentTitle)
dialogView.etNotice.setText(currentContent) dialogView.etContent.setText(currentContent)
} }
fun setCoverImageUri(coverImageUri: Uri) { fun setCoverImageUri(coverImageUri: Uri) {
@ -157,53 +63,13 @@ class LiveRoomInfoEditDialog(
} }
} }
fun setMenuPreset(menuList: List<GetMenuPresetResponse>) { fun setConfirmAction(confirmAction: (String, String, Uri?) -> Unit) {
this.menuList.clear()
this.isActivateMenu = null
this.menuList.addAll(menuList)
this.selectedMenuLiveData.value = null
menuList.forEachIndexed { index, menuPreset ->
if (menuPreset.isActive) {
selectedMenu = when (index) {
1 -> LiveRoomCreateViewModel.SelectedMenu.MENU_2
2 -> LiveRoomCreateViewModel.SelectedMenu.MENU_3
else -> LiveRoomCreateViewModel.SelectedMenu.MENU_1
}
isActivateMenuLiveData.value = true
selectMenuPreset(selectedMenu)
}
}
}
fun setConfirmAction(confirmAction: (String, String, Uri?, Boolean?, Long, String) -> Unit) {
dialogView.tvConfirm.setOnClickListener { dialogView.tvConfirm.setOnClickListener {
alertDialog.dismiss() alertDialog.dismiss()
val newTitle = dialogView.etTitle.text.toString() val newTitle = dialogView.etTitle.text.toString()
val newContent = dialogView.etNotice.text.toString() val newContent = dialogView.etContent.text.toString()
val menu = dialogView.etMenu.text.toString() confirmAction(newTitle, newContent, coverImageUri)
confirmAction(
newTitle,
newContent,
coverImageUri,
if (isActivateMenu != null) {
isActivateMenu
} else if (
this.menu != menu ||
(
this::selectedMenu.isInitialized &&
this.selectedMenu != selectedMenuLiveData.value!!
)
) {
true
} else {
isActivateMenu
},
menuId,
menu
)
coverImageUri = null coverImageUri = null
coverImageUrl = null coverImageUrl = null
} }
@ -219,103 +85,4 @@ class LiveRoomInfoEditDialog(
alertDialog.window?.attributes = lp alertDialog.window?.attributes = lp
} }
private fun deselectAllMenuPreset() {
dialogView.ivSelectMenu1.visibility = View.GONE
dialogView.ivSelectMenu2.visibility = View.GONE
dialogView.ivSelectMenu3.visibility = View.GONE
dialogView.llSelectMenu1.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b)
dialogView.tvSelectMenu1.setTextColor(
ContextCompat.getColor(
activity,
R.color.color_3bb9f1
)
)
if (menuList.size > 0) {
dialogView.llSelectMenu2.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b)
dialogView.tvSelectMenu2.setTextColor(
ContextCompat.getColor(
activity,
R.color.color_3bb9f1
)
)
} else {
dialogView.llSelectMenu2.setBackgroundResource(R.drawable.bg_round_corner_6_7_777777)
dialogView.tvSelectMenu2.setTextColor(
ContextCompat.getColor(
activity,
R.color.color_555555
)
)
}
if (menuList.size > 1) {
dialogView.llSelectMenu3.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b)
dialogView.tvSelectMenu3.setTextColor(
ContextCompat.getColor(
activity,
R.color.color_3bb9f1
)
)
} else {
dialogView.llSelectMenu3.setBackgroundResource(R.drawable.bg_round_corner_6_7_777777)
dialogView.tvSelectMenu3.setTextColor(
ContextCompat.getColor(
activity,
R.color.color_555555
)
)
}
}
private fun selectMenuPresetButton(
ivSelectMenuPreset: ImageView,
llSelectMenuPreset: LinearLayout,
tvSelectMenuPreset: TextView
) {
ivSelectMenuPreset.visibility = View.VISIBLE
llSelectMenuPreset.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
tvSelectMenuPreset.setTextColor(
ContextCompat.getColor(
activity,
R.color.color_eeeeee
)
)
}
private fun selectMenuPreset(selectedMenuPreset: LiveRoomCreateViewModel.SelectedMenu) {
if (
menuList.isEmpty() &&
(
selectedMenuPreset == LiveRoomCreateViewModel.SelectedMenu.MENU_2 ||
selectedMenuPreset == LiveRoomCreateViewModel.SelectedMenu.MENU_3
)
) {
Toast.makeText(activity, "메뉴 1을 먼저 설정하세요", Toast.LENGTH_SHORT).show()
return
}
if (menuList.size == 1 && selectedMenuPreset == LiveRoomCreateViewModel.SelectedMenu.MENU_3) {
Toast.makeText(activity, "메뉴 1과 메뉴 2를 먼저 설정하세요", Toast.LENGTH_SHORT).show()
return
}
if (selectedMenuLiveData.value != selectedMenuPreset) {
selectedMenuLiveData.value = selectedMenuPreset
if (menuList.size > selectedMenuPreset.ordinal) {
val menuPreset = menuList[selectedMenuPreset.ordinal]
menu = menuPreset.menu
menuId = menuPreset.id
dialogView.etMenu.setText(menuPreset.menu)
} else {
menu = ""
menuId = 0
dialogView.etMenu.setText("")
}
}
}
} }

View File

@ -1,10 +0,0 @@
package kr.co.vividnext.sodalive.live.roulette
import com.google.gson.annotations.SerializedName
data class GetNewRouletteResponse(
@SerializedName("id") val id: Long,
@SerializedName("can") val can: Int,
@SerializedName("isActive") val isActive: Boolean,
@SerializedName("items") val items: List<RouletteItem>
)

View File

@ -2,32 +2,18 @@ package kr.co.vividnext.sodalive.live.roulette
import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.live.roulette.config.CreateRouletteRequest import kr.co.vividnext.sodalive.live.roulette.config.CreateOrUpdateRouletteRequest
import kr.co.vividnext.sodalive.live.roulette.config.RouletteApi import kr.co.vividnext.sodalive.live.roulette.config.RouletteApi
import kr.co.vividnext.sodalive.live.roulette.config.UpdateRouletteRequest
class RouletteRepository(private val api: RouletteApi) { class RouletteRepository(private val api: RouletteApi) {
fun createRoulette( fun createOrUpdateRoulette(
request: CreateRouletteRequest, request: CreateOrUpdateRouletteRequest,
token: String token: String
) = api.createRoulette( ) = api.createOrUpdateRoulette(
request = request, request = request,
authHeader = token authHeader = token
) )
fun updateRoulette(
request: UpdateRouletteRequest,
token: String
) = api.updateRoulette(
request = request,
authHeader = token
)
fun getAllRoulette(creatorId: Long, token: String) = api.getAllRoulette(
creatorId = creatorId,
authHeader = token
)
fun getRoulette(creatorId: Long, token: String) = api.getRoulette( fun getRoulette(creatorId: Long, token: String) = api.getRoulette(
creatorId = creatorId, creatorId = creatorId,
authHeader = token authHeader = token

View File

@ -3,7 +3,7 @@ package kr.co.vividnext.sodalive.live.roulette.config
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import kr.co.vividnext.sodalive.live.roulette.RouletteItem import kr.co.vividnext.sodalive.live.roulette.RouletteItem
data class CreateRouletteRequest( data class CreateOrUpdateRouletteRequest(
@SerializedName("can") val can: Int, @SerializedName("can") val can: Int,
@SerializedName("isActive") val isActive: Boolean, @SerializedName("isActive") val isActive: Boolean,
@SerializedName("items") val items: List<RouletteItem> @SerializedName("items") val items: List<RouletteItem>

View File

@ -2,49 +2,35 @@ package kr.co.vividnext.sodalive.live.roulette.config
import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.live.roulette.GetNewRouletteResponse
import kr.co.vividnext.sodalive.live.roulette.GetRouletteResponse import kr.co.vividnext.sodalive.live.roulette.GetRouletteResponse
import kr.co.vividnext.sodalive.live.roulette.SpinRouletteRequest import kr.co.vividnext.sodalive.live.roulette.SpinRouletteRequest
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Header import retrofit2.http.Header
import retrofit2.http.POST import retrofit2.http.POST
import retrofit2.http.PUT
import retrofit2.http.Path import retrofit2.http.Path
import retrofit2.http.Query import retrofit2.http.Query
interface RouletteApi { interface RouletteApi {
@POST("/new-roulette") @POST("/roulette")
fun createRoulette( fun createOrUpdateRoulette(
@Body request: CreateRouletteRequest, @Body request: CreateOrUpdateRouletteRequest,
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String
): Single<ApiResponse<Any>> ): Single<ApiResponse<Any>>
@PUT("/new-roulette") @GET("/roulette")
fun updateRoulette(
@Body request: UpdateRouletteRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
@GET("/new-roulette/creator")
fun getAllRoulette(
@Query("creatorId") creatorId: Long,
@Header("Authorization") authHeader: String
): Single<ApiResponse<List<GetNewRouletteResponse>>>
@GET("/new-roulette")
fun getRoulette( fun getRoulette(
@Query("creatorId") creatorId: Long, @Query("creatorId") creatorId: Long,
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String
): Single<ApiResponse<GetRouletteResponse>> ): Single<ApiResponse<GetRouletteResponse>>
@POST("/new-roulette/spin") @POST("/roulette/spin")
fun spinRoulette( fun spinRoulette(
@Body request: SpinRouletteRequest, @Body request: SpinRouletteRequest,
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String
): Single<ApiResponse<GetRouletteResponse>> ): Single<ApiResponse<GetRouletteResponse>>
@POST("/new-roulette/refund/{id}") @POST("/roulette/refund/{id}")
fun refundRouletteDonation( fun refundRouletteDonation(
@Path("id") id: Long, @Path("id") id: Long,
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String

View File

@ -13,9 +13,7 @@ import android.view.View
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.EditText import android.widget.EditText
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.core.content.ContextCompat
import com.jakewharton.rxbinding4.widget.textChanges import com.jakewharton.rxbinding4.widget.textChanges
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.schedulers.Schedulers
@ -43,7 +41,7 @@ class RouletteSettingsFragment : BaseFragment<FragmentRouletteSettingsBinding>(
setupView() setupView()
bindData() bindData()
viewModel.getAllRoulette() viewModel.getRoulette()
} }
private fun setupView() { private fun setupView() {
@ -76,57 +74,15 @@ class RouletteSettingsFragment : BaseFragment<FragmentRouletteSettingsBinding>(
handler.postDelayed({ handler.postDelayed({
imm.hideSoftInputFromWindow(view?.windowToken, 0) imm.hideSoftInputFromWindow(view?.windowToken, 0)
}, 100) }, 100)
viewModel.createOrUpdateRoulette { viewModel.createOrUpdateRoulette {
val resultIntent = Intent().apply { putExtra(Constants.EXTRA_RESULT_ROULETTE, it) } val resultIntent = Intent().apply { putExtra(Constants.EXTRA_RESULT_ROULETTE, it) }
requireActivity().setResult(Activity.RESULT_OK, resultIntent) requireActivity().setResult(Activity.RESULT_OK, resultIntent)
requireActivity().finish() requireActivity().finish()
} }
} }
binding.llSelectRoulette1.setOnClickListener {
viewModel.selectRoulette(
RouletteSettingsViewModel.SelectedRoulette.ROULETTE_1
)
}
binding.llSelectRoulette2.setOnClickListener {
viewModel.selectRoulette(
RouletteSettingsViewModel.SelectedRoulette.ROULETTE_2
)
}
binding.llSelectRoulette3.setOnClickListener {
viewModel.selectRoulette(
RouletteSettingsViewModel.SelectedRoulette.ROULETTE_3
)
}
} }
private fun bindData() { private fun bindData() {
viewModel.selectedRouletteLiveData.observe(viewLifecycleOwner) {
deselectAllRoulette()
when (it) {
RouletteSettingsViewModel.SelectedRoulette.ROULETTE_2 -> selectRouletteButton(
binding.ivSelectRoulette2,
binding.llSelectRoulette2,
binding.tvSelectRoulette2
)
RouletteSettingsViewModel.SelectedRoulette.ROULETTE_3 -> selectRouletteButton(
binding.ivSelectRoulette3,
binding.llSelectRoulette3,
binding.tvSelectRoulette3
)
else -> selectRouletteButton(
binding.ivSelectRoulette1,
binding.llSelectRoulette1,
binding.tvSelectRoulette1
)
}
}
viewModel.isActiveLiveData.observe(viewLifecycleOwner) { viewModel.isActiveLiveData.observe(viewLifecycleOwner) {
binding.ivRouletteIsActive.setImageResource( binding.ivRouletteIsActive.setImageResource(
if (it) R.drawable.btn_toggle_on_big else R.drawable.btn_toggle_off_big if (it) R.drawable.btn_toggle_on_big else R.drawable.btn_toggle_off_big
@ -229,69 +185,4 @@ class RouletteSettingsFragment : BaseFragment<FragmentRouletteSettingsBinding>(
return optionView return optionView
} }
private fun deselectAllRoulette() {
binding.ivSelectRoulette1.visibility = View.GONE
binding.ivSelectRoulette2.visibility = View.GONE
binding.ivSelectRoulette3.visibility = View.GONE
binding.llSelectRoulette1.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b)
binding.tvSelectRoulette1.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.color_3bb9f1
)
)
if (viewModel.rouletteList.size > 0) {
binding.llSelectRoulette2.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b)
binding.tvSelectRoulette2.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.color_3bb9f1
)
)
} else {
binding.llSelectRoulette2.setBackgroundResource(R.drawable.bg_round_corner_6_7_777777)
binding.tvSelectRoulette2.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.color_555555
)
)
}
if (viewModel.rouletteList.size > 1) {
binding.llSelectRoulette3.setBackgroundResource(R.drawable.bg_round_corner_6_7_13181b)
binding.tvSelectRoulette3.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.color_3bb9f1
)
)
} else {
binding.llSelectRoulette3.setBackgroundResource(R.drawable.bg_round_corner_6_7_777777)
binding.tvSelectRoulette3.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.color_555555
)
)
}
}
private fun selectRouletteButton(
ivSelectRoulette: ImageView,
llSelectRoulette: LinearLayout,
tvSelectRoulette: TextView
) {
ivSelectRoulette.visibility = View.VISIBLE
llSelectRoulette.setBackgroundResource(R.drawable.bg_round_corner_6_7_3bb9f1)
tvSelectRoulette.setTextColor(
ContextCompat.getColor(
requireContext(),
R.color.color_eeeeee
)
)
}
} }

View File

@ -7,7 +7,6 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.base.BaseViewModel import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.live.roulette.GetNewRouletteResponse
import kr.co.vividnext.sodalive.live.roulette.RouletteItem import kr.co.vividnext.sodalive.live.roulette.RouletteItem
import kr.co.vividnext.sodalive.live.roulette.RoulettePreview import kr.co.vividnext.sodalive.live.roulette.RoulettePreview
import kr.co.vividnext.sodalive.live.roulette.RoulettePreviewItem import kr.co.vividnext.sodalive.live.roulette.RoulettePreviewItem
@ -16,10 +15,6 @@ import kotlin.math.floor
class RouletteSettingsViewModel(private val repository: RouletteRepository) : BaseViewModel() { class RouletteSettingsViewModel(private val repository: RouletteRepository) : BaseViewModel() {
enum class SelectedRoulette {
ROULETTE_1, ROULETTE_2, ROULETTE_3
}
private var _isLoading = MutableLiveData(false) private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean> val isLoading: LiveData<Boolean>
get() = _isLoading get() = _isLoading
@ -44,16 +39,9 @@ class RouletteSettingsViewModel(private val repository: RouletteRepository) : Ba
val roulettePreviewLiveData: LiveData<RoulettePreview> val roulettePreviewLiveData: LiveData<RoulettePreview>
get() = _roulettePreviewLiveData get() = _roulettePreviewLiveData
private val _selectedRouletteLiveData = MutableLiveData<SelectedRoulette>() private val options = mutableListOf<RouletteOption>()
val selectedRouletteLiveData: LiveData<SelectedRoulette>
get() = _selectedRouletteLiveData
var can = 0 var can = 0
var isActive = false var isActive = false
private var rouletteId = 0L
private val options = mutableListOf<RouletteOption>()
val rouletteList = mutableListOf<GetNewRouletteResponse>()
fun plusWeight(optionIndex: Int) { fun plusWeight(optionIndex: Int) {
val currentOption = options[optionIndex] val currentOption = options[optionIndex]
@ -125,166 +113,66 @@ class RouletteSettingsViewModel(private val repository: RouletteRepository) : Ba
if (!_isLoading.value!!) { if (!_isLoading.value!!) {
_isLoading.value = true _isLoading.value = true
if (rouletteId > 0) { val items = mutableListOf<RouletteItem>()
updateRoulette(onSuccess) for (option in options) {
} else { if (option.title.trim().isEmpty()) {
createRoulette(onSuccess) _toastLiveData.value = "옵션은 빈칸을 할 수 없습니다."
} _isLoading.value = false
} return
}
private fun updateRoulette(onSuccess: (Boolean) -> Unit) {
val items = mutableListOf<RouletteItem>()
for (option in options) {
if (option.title.trim().isEmpty()) {
_toastLiveData.value = "옵션은 빈칸을 할 수 없습니다."
_isLoading.value = false
return
}
items.add(RouletteItem(title = option.title, weight = option.weight))
}
val selectedRoulette = rouletteList[_selectedRouletteLiveData.value!!.ordinal]
if (
selectedRoulette.isActive == isActive &&
selectedRoulette.can == can &&
selectedRoulette.items == items
) {
_toastLiveData.value = "변동사항이 없습니다."
_isLoading.value = false
return
}
val request = UpdateRouletteRequest(
id = rouletteId,
can = can,
isActive = isActive,
items = items
)
val selectedRouletteTitle = when (_selectedRouletteLiveData.value!!) {
SelectedRoulette.ROULETTE_1 -> "룰렛 1"
SelectedRoulette.ROULETTE_2 -> "룰렛 2"
SelectedRoulette.ROULETTE_3 -> "룰렛 3"
}
var isAllActive = false
rouletteList
.filter {
it.id != selectedRoulette.id
}
.forEach {
if (it.isActive) {
isAllActive = true
} }
items.add(RouletteItem(title = option.title, weight = option.weight))
} }
val successMessage = if (isActive) { val request = CreateOrUpdateRouletteRequest(
"${selectedRouletteTitle}로 설정하였습니다." can = can,
} else if (!isAllActive) { isActive = isActive,
"${selectedRouletteTitle}이 비활성화 되었습니다." items = items
} else {
"${selectedRouletteTitle}을 설정했습니다."
}
compositeDisposable.add(
repository.updateRoulette(
request = request,
token = "Bearer ${SharedPreferenceManager.token}"
) )
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) compositeDisposable.add(
.subscribe( repository.createOrUpdateRoulette(
{ request = request,
_isLoading.value = false token = "Bearer ${SharedPreferenceManager.token}"
if (it.success && it.data != null && it.data is Boolean) {
_toastLiveData.postValue(successMessage)
onSuccess(it.data)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
) )
) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success && it.data != null && it.data is Boolean) {
val message = if (it.data) {
"룰렛을 활성화 했습니다."
} else {
"룰렛을 비활성화 했습니다."
}
_toastLiveData.postValue(message)
onSuccess(it.data)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
} }
private fun createRoulette(onSuccess: (Boolean) -> Unit) { fun getRoulette() {
val items = mutableListOf<RouletteItem>()
for (option in options) {
if (option.title.trim().isEmpty()) {
_toastLiveData.value = "옵션은 빈칸을 할 수 없습니다."
_isLoading.value = false
return
}
items.add(RouletteItem(title = option.title, weight = option.weight))
}
val request = CreateRouletteRequest(
can = can,
isActive = isActive,
items = items
)
val selectedRouletteTitle = when (_selectedRouletteLiveData.value!!) {
SelectedRoulette.ROULETTE_1 -> "룰렛 1"
SelectedRoulette.ROULETTE_2 -> "룰렛 2"
SelectedRoulette.ROULETTE_3 -> "룰렛 3"
}
val successMessage = "$selectedRouletteTitle " +
if (isActive) "로 설정하였습니다." else "을 설정했습니다."
compositeDisposable.add(
repository.createRoulette(
request = request,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success && it.data != null && it.data is Boolean) {
_toastLiveData.postValue(successMessage)
onSuccess(it.data)
} 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 getAllRoulette() {
if (!_isLoading.value!!) { if (!_isLoading.value!!) {
_isLoading.value = true _isLoading.value = true
compositeDisposable.add( compositeDisposable.add(
repository.getAllRoulette( repository.getRoulette(
creatorId = SharedPreferenceManager.userId, creatorId = SharedPreferenceManager.userId,
token = "Bearer ${SharedPreferenceManager.token}" token = "Bearer ${SharedPreferenceManager.token}"
) )
@ -294,9 +182,30 @@ class RouletteSettingsViewModel(private val repository: RouletteRepository) : Ba
{ {
if (it.success) { if (it.success) {
val data = it.data val data = it.data
rouletteList.clear()
rouletteList.addAll(data ?: listOf()) if (data != null && data.items.isNotEmpty()) {
selectRoulette(SelectedRoulette.ROULETTE_1) _isActiveLiveData.value = data.isActive
_canLiveData.value = data.can
isActive = data.isActive
can = data.can
val options = data.items.asSequence().map { item ->
RouletteOption(title = item.title, weight = item.weight)
}.toList()
removeAllAndAddOptions(options = options)
recalculatePercentages(options)
} else {
_isActiveLiveData.value = false
_canLiveData.value = 0
isActive = false
can = 0
options.add(RouletteOption(title = "", weight = 1))
options.add(RouletteOption(title = "", weight = 1))
recalculatePercentages(options)
}
} else { } else {
if (it.message != null) { if (it.message != null) {
_toastLiveData.postValue(it.message) _toastLiveData.postValue(it.message)
@ -318,56 +227,6 @@ class RouletteSettingsViewModel(private val repository: RouletteRepository) : Ba
} }
} }
fun selectRoulette(selectedRoulette: SelectedRoulette) {
if (
rouletteList.isEmpty() &&
(
selectedRoulette == SelectedRoulette.ROULETTE_2 ||
selectedRoulette == SelectedRoulette.ROULETTE_3
)
) {
_toastLiveData.value = "룰렛 1을 먼저 설정하세요"
return
}
if (rouletteList.size == 1 && selectedRoulette == SelectedRoulette.ROULETTE_3) {
_toastLiveData.value = "룰렛 1과 룰렛 2를 먼저 설정하세요"
return
}
if (_selectedRouletteLiveData.value != selectedRoulette) {
_selectedRouletteLiveData.value = selectedRoulette
if (rouletteList.size > selectedRoulette.ordinal) {
val roulette = rouletteList[selectedRoulette.ordinal]
_canLiveData.value = roulette.can
_isActiveLiveData.value = roulette.isActive
can = roulette.can
rouletteId = roulette.id
isActive = roulette.isActive
val options = roulette.items.asSequence().map { item ->
RouletteOption(title = item.title, weight = item.weight)
}.toList()
removeAllAndAddOptions(options = options)
recalculatePercentages(options)
} else {
_canLiveData.value = 0
_isActiveLiveData.value = false
can = 0
rouletteId = 0
isActive = false
options.clear()
options.add(RouletteOption(title = "", weight = 1))
options.add(RouletteOption(title = "", weight = 1))
recalculatePercentages(options)
}
}
}
private fun removeAllAndAddOptions(options: List<RouletteOption>) { private fun removeAllAndAddOptions(options: List<RouletteOption>) {
this.options.clear() this.options.clear()
this.options.addAll(options) this.options.addAll(options)

View File

@ -1,11 +0,0 @@
package kr.co.vividnext.sodalive.live.roulette.config
import com.google.gson.annotations.SerializedName
import kr.co.vividnext.sodalive.live.roulette.RouletteItem
data class UpdateRouletteRequest(
@SerializedName("id") val id: Long,
@SerializedName("can") val can: Int,
@SerializedName("isActive") val isActive: Boolean,
@SerializedName("items") val items: List<RouletteItem>
)

View File

@ -1,7 +0,0 @@
package kr.co.vividnext.sodalive.main
import com.google.gson.annotations.SerializedName
data class GaidUpdateRequest(
@SerializedName("adid") val adid: String
)

View File

@ -59,7 +59,6 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
checkPermissions() checkPermissions()
pushTokenUpdate() pushTokenUpdate()
gaidUpdate()
getMemberInfo() getMemberInfo()
getEventPopup() getEventPopup()
@ -339,10 +338,6 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
} }
} }
private fun gaidUpdate() {
viewModel.fetchAndUpdateGaid(context = applicationContext)
}
private fun getMemberInfo() { private fun getMemberInfo() {
viewModel.getMemberInfo { viewModel.getMemberInfo {
notificationSettingsDialog.show(screenWidth) notificationSettingsDialog.show(screenWidth)

View File

@ -1,9 +1,7 @@
package kr.co.vividnext.sodalive.main package kr.co.vividnext.sodalive.main
import android.content.Context
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.google.android.gms.ads.identifier.AdvertisingIdClient
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers import io.reactivex.rxjava3.schedulers.Schedulers
@ -17,7 +15,6 @@ import kr.co.vividnext.sodalive.settings.event.EventItem
import kr.co.vividnext.sodalive.settings.event.EventRepository import kr.co.vividnext.sodalive.settings.event.EventRepository
import kr.co.vividnext.sodalive.settings.notification.UpdateNotificationSettingRequest import kr.co.vividnext.sodalive.settings.notification.UpdateNotificationSettingRequest
import kr.co.vividnext.sodalive.user.UserRepository import kr.co.vividnext.sodalive.user.UserRepository
import java.util.concurrent.Executors
class MainViewModel( class MainViewModel(
private val userRepository: UserRepository, private val userRepository: UserRepository,
@ -156,26 +153,4 @@ class MainViewModel(
) )
) )
} }
fun fetchAndUpdateGaid(context: Context) {
Executors.newSingleThreadExecutor().execute {
try {
val adInfo = AdvertisingIdClient.getAdvertisingIdInfo(context)
adInfo.id?.let { gaid ->
val request = GaidUpdateRequest(adid = gaid)
compositeDisposable.add(
userRepository.updateGaid(
request,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({}, {})
)
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
} }

View File

@ -200,6 +200,7 @@ class VoiceMessageFragment : BaseFragment<FragmentVoiceMessageBinding>(
override fun run() { override fun run() {
handler.post { handler.post {
seekbar.progress = mediaPlayer?.currentPosition ?: 0 seekbar.progress = mediaPlayer?.currentPosition ?: 0
Logger.e("test")
} }
} }
} }

View File

@ -2,11 +2,10 @@ package kr.co.vividnext.sodalive.mypage.can
import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.mypage.can.charge.iap.GoogleChargeRequest import kr.co.vividnext.sodalive.mypage.can.charge.CanResponse
import kr.co.vividnext.sodalive.mypage.can.charge.pg.CanResponse import kr.co.vividnext.sodalive.mypage.can.charge.ChargeRequest
import kr.co.vividnext.sodalive.mypage.can.charge.pg.ChargeRequest import kr.co.vividnext.sodalive.mypage.can.charge.ChargeResponse
import kr.co.vividnext.sodalive.mypage.can.charge.pg.ChargeResponse import kr.co.vividnext.sodalive.mypage.can.charge.VerifyRequest
import kr.co.vividnext.sodalive.mypage.can.charge.pg.VerifyRequest
import kr.co.vividnext.sodalive.mypage.can.coupon.UseCanCouponRequest import kr.co.vividnext.sodalive.mypage.can.coupon.UseCanCouponRequest
import kr.co.vividnext.sodalive.mypage.can.status.GetCanStatusResponse import kr.co.vividnext.sodalive.mypage.can.status.GetCanStatusResponse
import kr.co.vividnext.sodalive.mypage.can.status.charge.GetCanChargeStatusResponseItem import kr.co.vividnext.sodalive.mypage.can.status.charge.GetCanChargeStatusResponseItem
@ -18,12 +17,6 @@ import retrofit2.http.POST
import retrofit2.http.Query import retrofit2.http.Query
interface CanApi { interface CanApi {
@POST("/charge/google")
fun googleChargeCan(
@Body request: GoogleChargeRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
@POST("/charge") @POST("/charge")
fun chargeCan( fun chargeCan(
@Body chargeRequest: ChargeRequest, @Body chargeRequest: ChargeRequest,

View File

@ -1,17 +1,11 @@
package kr.co.vividnext.sodalive.mypage.can package kr.co.vividnext.sodalive.mypage.can
import kr.co.vividnext.sodalive.mypage.can.charge.iap.GoogleChargeRequest import kr.co.vividnext.sodalive.mypage.can.charge.ChargeRequest
import kr.co.vividnext.sodalive.mypage.can.charge.pg.ChargeRequest import kr.co.vividnext.sodalive.mypage.can.charge.VerifyRequest
import kr.co.vividnext.sodalive.mypage.can.charge.pg.VerifyRequest
import kr.co.vividnext.sodalive.mypage.can.coupon.UseCanCouponRequest import kr.co.vividnext.sodalive.mypage.can.coupon.UseCanCouponRequest
import java.util.TimeZone import java.util.TimeZone
class CanRepository(private val api: CanApi) { class CanRepository(private val api: CanApi) {
fun googleChargeCan(
request: GoogleChargeRequest,
token: String
) = api.googleChargeCan(request, authHeader = token)
fun chargeCan( fun chargeCan(
chargeRequest: ChargeRequest, chargeRequest: ChargeRequest,
token: String token: String

View File

@ -1,28 +1,33 @@
package kr.co.vividnext.sodalive.mypage.can.charge package kr.co.vividnext.sodalive.mypage.can.charge
import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import android.graphics.Rect
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import com.google.android.material.tabs.TabLayout import androidx.recyclerview.widget.LinearLayoutManager
import kr.co.vividnext.sodalive.R import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.ActivityCanChargeBinding import kr.co.vividnext.sodalive.databinding.ActivityCanChargeBinding
import kr.co.vividnext.sodalive.mypage.can.charge.iap.CanChargeIapFragment import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.mypage.can.charge.pg.CanChargePgFragment
import kr.co.vividnext.sodalive.mypage.can.charge.pg.CanResponse
import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentActivity import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentActivity
import kr.co.vividnext.sodalive.mypage.can.status.CanStatusActivity import org.koin.android.ext.android.inject
class CanChargeActivity : BaseActivity<ActivityCanChargeBinding>( class CanChargeActivity : BaseActivity<ActivityCanChargeBinding>(
ActivityCanChargeBinding::inflate ActivityCanChargeBinding::inflate
) { ) {
private val viewModel: CanChargeViewModel by inject()
private var gotoPrevPage: Boolean = false private var gotoPrevPage: Boolean = false
private lateinit var adapter: CanChargeAdapter
private lateinit var loadingDialog: LoadingDialog
private lateinit var activityResultLauncher: ActivityResultLauncher<Intent> private lateinit var activityResultLauncher: ActivityResultLauncher<Intent>
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -35,71 +40,87 @@ class CanChargeActivity : BaseActivity<ActivityCanChargeBinding>(
} }
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
bindData()
viewModel.getCanCharges()
}
override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater)
binding.toolbar.tvBack.text = "충전하기"
binding.toolbar.tvBack.setOnClickListener { finish() }
gotoPrevPage = intent.getBooleanExtra( gotoPrevPage = intent.getBooleanExtra(
Constants.EXTRA_GO_TO_PREV_PAGE, Constants.EXTRA_GO_TO_PREV_PAGE,
false false
) )
supportFragmentManager.beginTransaction() val recyclerView = binding.rvChargeCan
.replace(R.id.fl_container, CanChargeIapFragment()).commit() adapter = CanChargeAdapter {
} val intent = Intent(applicationContext, CanPaymentActivity::class.java)
intent.putExtra(Constants.EXTRA_CAN, it)
intent.putExtra(Constants.EXTRA_GO_TO_PREV_PAGE, gotoPrevPage)
activityResultLauncher.launch(intent)
}
override fun setupView() { recyclerView.layoutManager = LinearLayoutManager(
setupToolbar() applicationContext,
setupTabs() LinearLayoutManager.VERTICAL,
} false
)
private fun setupToolbar() { recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
binding.toolbar.tvBack.text = "충전하기" override fun getItemOffsets(
binding.toolbar.tvBack.setOnClickListener { finish() } outRect: Rect,
} view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
private fun setupTabs() { outRect.left = 13.3f.dpToPx().toInt()
if (SharedPreferenceManager.isAuth) { outRect.right = 13.3f.dpToPx().toInt()
val tabs = binding.tabs
tabs.visibility = View.VISIBLE when (parent.getChildAdapterPosition(view)) {
tabs.addTab(tabs.newTab().setText("인 앱 결제")) 0 -> {
tabs.addTab(tabs.newTab().setText("PG")) outRect.top = 13.3f.dpToPx().toInt()
outRect.bottom = 6.7f.dpToPx().toInt()
}
tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { adapter.itemCount - 1 -> {
override fun onTabSelected(tab: TabLayout.Tab) { outRect.top = 6.7f.dpToPx().toInt()
when (tab.position) { outRect.bottom = 26.7f.dpToPx().toInt()
0 -> supportFragmentManager.beginTransaction() }
.replace(R.id.fl_container, CanChargeIapFragment()).commit()
1 -> supportFragmentManager.beginTransaction() else -> {
.replace(R.id.fl_container, CanChargePgFragment()).commit() outRect.top = 6.7f.dpToPx().toInt()
outRect.bottom = 6.7f.dpToPx().toInt()
} }
} }
}
})
override fun onTabUnselected(tab: TabLayout.Tab) { recyclerView.adapter = adapter
}
override fun onTabReselected(tab: TabLayout.Tab) {
}
})
} else {
binding.tabs.visibility = View.GONE
}
} }
fun selectCan(model: CanResponse) { @SuppressLint("NotifyDataSetChanged")
val intent = Intent(applicationContext, CanPaymentActivity::class.java) private fun bindData() {
intent.putExtra(Constants.EXTRA_CAN, model) viewModel.canChargeLiveData.observe(this) {
intent.putExtra(Constants.EXTRA_GO_TO_PREV_PAGE, gotoPrevPage) adapter.items.addAll(it)
activityResultLauncher.launch(intent) adapter.notifyDataSetChanged()
}
fun successIapCharge() {
if (gotoPrevPage) {
setResult(RESULT_OK)
} else {
val intent = Intent(applicationContext, CanStatusActivity::class.java)
startActivity(intent)
} }
finish() 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()
}
}
} }
} }

View File

@ -1,4 +1,4 @@
package kr.co.vividnext.sodalive.mypage.can.charge.pg package kr.co.vividnext.sodalive.mypage.can.charge
import android.content.Context import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
@ -10,9 +10,9 @@ import kr.co.vividnext.sodalive.databinding.ItemCanChargeBinding
import kr.co.vividnext.sodalive.extensions.fontSpan import kr.co.vividnext.sodalive.extensions.fontSpan
import kr.co.vividnext.sodalive.extensions.moneyFormat import kr.co.vividnext.sodalive.extensions.moneyFormat
class CanChargePgAdapter( class CanChargeAdapter(
private val onClick: (CanResponse) -> Unit private val onClick: (CanResponse) -> Unit
) : RecyclerView.Adapter<CanChargePgAdapter.ViewHolder>() { ) : RecyclerView.Adapter<CanChargeAdapter.ViewHolder>() {
val items = mutableListOf<CanResponse>() val items = mutableListOf<CanResponse>()

View File

@ -1,4 +1,4 @@
package kr.co.vividnext.sodalive.mypage.can.charge.pg package kr.co.vividnext.sodalive.mypage.can.charge
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
@ -9,7 +9,7 @@ import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.mypage.can.CanRepository import kr.co.vividnext.sodalive.mypage.can.CanRepository
class CanChargePgViewModel(private val repository: CanRepository) : BaseViewModel() { class CanChargeViewModel(private val repository: CanRepository) : BaseViewModel() {
private val _canChargesLiveData = MutableLiveData<List<CanResponse>>() private val _canChargesLiveData = MutableLiveData<List<CanResponse>>()
val canChargeLiveData: LiveData<List<CanResponse>> val canChargeLiveData: LiveData<List<CanResponse>>
get() = _canChargesLiveData get() = _canChargesLiveData

View File

@ -1,4 +1,4 @@
package kr.co.vividnext.sodalive.mypage.can.charge.pg package kr.co.vividnext.sodalive.mypage.can.charge
import android.os.Parcelable import android.os.Parcelable
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName

View File

@ -1,4 +1,4 @@
package kr.co.vividnext.sodalive.mypage.can.charge.pg package kr.co.vividnext.sodalive.mypage.can.charge
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
import kr.co.vividnext.sodalive.mypage.can.payment.PaymentGateway import kr.co.vividnext.sodalive.mypage.can.payment.PaymentGateway

View File

@ -1,57 +0,0 @@
package kr.co.vividnext.sodalive.mypage.can.charge.iap
import android.annotation.SuppressLint
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.content.res.ResourcesCompat
import androidx.recyclerview.widget.RecyclerView
import com.android.billingclient.api.ProductDetails
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.databinding.ItemCanChargeBinding
import kr.co.vividnext.sodalive.extensions.fontSpan
class CanChargeIapAdapter(
private val onClick: (ProductDetails) -> Unit
) : RecyclerView.Adapter<CanChargeIapAdapter.ViewHolder>() {
val items = mutableListOf<ProductDetails>()
inner class ViewHolder(
private val context: Context,
private val binding: ItemCanChargeBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: ProductDetails) {
binding.tvPrice.text = item.oneTimePurchaseOfferDetails?.formattedPrice
val typeface = ResourcesCompat.getFont(context, R.font.gmarket_sans_medium)
binding.tvTitle.text = item.name.fontSpan(
typeface,
""
)
binding.root.setOnClickListener { onClick(item) }
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
parent.context,
ItemCanChargeBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(items[position])
}
override fun getItemCount() = items.count()
@SuppressLint("NotifyDataSetChanged")
fun addItems(items: List<ProductDetails>) {
this.items.addAll(items.sortedBy { it.description.toInt() })
notifyDataSetChanged()
}
}

View File

@ -1,282 +0,0 @@
package kr.co.vividnext.sodalive.mypage.can.charge.iap
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Rect
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.View
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.android.billingclient.api.BillingClient
import com.android.billingclient.api.BillingClientStateListener
import com.android.billingclient.api.BillingFlowParams
import com.android.billingclient.api.BillingResult
import com.android.billingclient.api.ConsumeParams
import com.android.billingclient.api.ProductDetails
import com.android.billingclient.api.Purchase
import com.android.billingclient.api.PurchasesUpdatedListener
import com.android.billingclient.api.QueryProductDetailsParams
import com.android.billingclient.api.QueryPurchasesParams
import kr.co.vividnext.sodalive.base.BaseFragment
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.FragmentCanChargeIapBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeActivity
import org.koin.android.ext.android.inject
class CanChargeIapFragment : BaseFragment<FragmentCanChargeIapBinding>(
FragmentCanChargeIapBinding::inflate
) {
private val viewModel: CanChargeIapViewModel by inject()
private lateinit var adapter: CanChargeIapAdapter
private lateinit var loadingDialog: LoadingDialog
private lateinit var billingClient: BillingClient
private val handler = Handler(Looper.getMainLooper())
private var selectedProductDetails: ProductDetails? = null
private lateinit var purchaseUpdateListener: PurchasesUpdatedListener
fun safeContext(): Context? {
return if (isAdded) context else null
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
loadingDialog = LoadingDialog(requireActivity(), layoutInflater)
bindData()
setupRecyclerView()
setupBillingClient()
}
override fun onStart() {
super.onStart()
billingClient.startConnection(object : BillingClientStateListener {
override fun onBillingServiceDisconnected() {
viewModel.showToast("인 앱 결제 이용이 불가능 합니다. 다시 시도해 주세요.")
viewModel.setLoading(false)
}
override fun onBillingSetupFinished(billingResult: BillingResult) {
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
queryAndConsumeUnconsumedPurchases()
} else {
viewModel.showToast("인 앱 결제 이용이 불가능 합니다. 다시 시도해 주세요.")
viewModel.setLoading(false)
}
}
})
}
override fun onStop() {
super.onStop()
billingClient.endConnection()
}
private fun bindData() {
viewModel.isLoading.observe(viewLifecycleOwner) {
if (it) {
loadingDialog.show(screenWidth)
} else {
loadingDialog.dismiss()
}
}
viewModel.toastLiveData.observe(viewLifecycleOwner) {
Toast.makeText(requireActivity(), it, Toast.LENGTH_LONG).show()
}
}
private fun setupRecyclerView() {
val recyclerView = binding.rvChargeCan
adapter = CanChargeIapAdapter { productDetails ->
selectedProductDetails = productDetails
launchPurchaseFlow(productDetails)
}
recyclerView.layoutManager = LinearLayoutManager(
requireContext(),
LinearLayoutManager.VERTICAL,
false
)
recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
outRect.left = 13.3f.dpToPx().toInt()
outRect.right = 13.3f.dpToPx().toInt()
when (parent.getChildAdapterPosition(view)) {
0 -> {
outRect.top = 13.3f.dpToPx().toInt()
outRect.bottom = 6.7f.dpToPx().toInt()
}
adapter.itemCount - 1 -> {
outRect.top = 6.7f.dpToPx().toInt()
outRect.bottom = 26.7f.dpToPx().toInt()
}
else -> {
outRect.top = 6.7f.dpToPx().toInt()
outRect.bottom = 6.7f.dpToPx().toInt()
}
}
}
})
recyclerView.adapter = adapter
}
private fun setupBillingClient() {
purchaseUpdateListener = PurchasesUpdatedListener { billingResult, purchases ->
handler.post {
if (
billingResult.responseCode == BillingClient.BillingResponseCode.OK &&
purchases != null &&
selectedProductDetails != null
) {
for (purchase in purchases) {
handlePurchase(purchase)
}
} else if (
billingResult.responseCode == BillingClient.BillingResponseCode.USER_CANCELED
) {
selectedProductDetails = null
viewModel.showToast("구매를 취소했습니다.")
} else {
selectedProductDetails = null
viewModel.showToast("구매를 하지 못했습니다.\n다시 시도해 주세요.")
}
}
}
billingClient = BillingClient.newBuilder(requireActivity())
.enablePendingPurchases()
.setListener(purchaseUpdateListener)
.build()
loadingDialog.show(screenWidth)
}
@SuppressLint("NotifyDataSetChanged")
private fun queryAvailableCans() {
val productList = listOf(
"${requireContext().packageName}.can_35",
"${requireContext().packageName}.can_55",
"${requireContext().packageName}.can_105",
"${requireContext().packageName}.can_350",
"${requireContext().packageName}.can_550",
"${requireContext().packageName}.can_1170",
"${requireContext().packageName}.can_1970"
)
val params = QueryProductDetailsParams.newBuilder()
.setProductList(
productList.map {
QueryProductDetailsParams.Product.newBuilder()
.setProductId(it)
.setProductType(BillingClient.ProductType.INAPP)
.build()
}
)
.build()
billingClient.queryProductDetailsAsync(params) { billingResult, productDetailsList ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
handler.post { adapter.addItems(productDetailsList) }
} else {
viewModel.showToast("인 앱 결제 이용이 불가능 합니다. 다시 시도해 주세요.")
}
viewModel.setLoading(false)
}
}
private fun queryAndConsumeUnconsumedPurchases() {
val queryPurchaseParams = QueryPurchasesParams.newBuilder()
.setProductType(BillingClient.ProductType.INAPP)
.build()
billingClient.queryPurchasesAsync(
queryPurchaseParams
) { result, purchaseList ->
if (result.responseCode == BillingClient.BillingResponseCode.OK) {
if (purchaseList.isNotEmpty()) {
for (purchase in purchaseList) {
if (!purchase.isAcknowledged) {
consumePurchase(purchase)
}
}
} else {
queryAvailableCans()
}
}
}
}
private fun consumePurchase(purchase: Purchase) {
val params = ConsumeParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
.build()
billingClient.consumeAsync(params) { billingResult, _ ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
queryAvailableCans()
}
}
}
private fun handlePurchase(purchase: Purchase) {
if (
purchase.purchaseState == Purchase.PurchaseState.PURCHASED &&
!purchase.isAcknowledged
) {
viewModel.chargeCan(
title = selectedProductDetails!!.name,
selectedProductDetails = selectedProductDetails!!,
purchase = purchase
) { chargeCan ->
handler.post {
viewModel.showToast("캔이 충전되었습니다")
SharedPreferenceManager.can += chargeCan
if (activity != null) {
if (activity as? CanChargeActivity != null) {
(activity as CanChargeActivity).successIapCharge()
} else {
requireActivity().finish()
}
}
}
}
}
}
private fun launchPurchaseFlow(productDetails: ProductDetails) {
val billingFlowParams = BillingFlowParams.newBuilder()
.setProductDetailsParamsList(
listOf(
BillingFlowParams.ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.build()
)
).build()
billingClient.launchBillingFlow(requireActivity(), billingFlowParams)
}
}

View File

@ -1,82 +0,0 @@
package kr.co.vividnext.sodalive.mypage.can.charge.iap
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.android.billingclient.api.ProductDetails
import com.android.billingclient.api.Purchase
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
import kr.co.vividnext.sodalive.mypage.can.CanRepository
class CanChargeIapViewModel(private val repository: CanRepository) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
fun chargeCan(
title: String,
selectedProductDetails: ProductDetails,
purchase: Purchase,
onSuccess: (Int) -> Unit
) {
val productId = purchase.products.firstOrNull()
if (productId != null) {
_isLoading.value = true
compositeDisposable.add(
repository.googleChargeCan(
request = GoogleChargeRequest(
title = title,
chargeCan = selectedProductDetails.description.toInt(),
price = (
selectedProductDetails.oneTimePurchaseOfferDetails?.priceAmountMicros
?: 0L).toDouble() / 1000000,
currencyCode = selectedProductDetails.oneTimePurchaseOfferDetails?.priceCurrencyCode
?: "KRW",
productId = purchase.products[0],
purchaseToken = purchase.purchaseToken
),
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success) {
onSuccess(selectedProductDetails.description.toInt())
} else {
if (it.message != null) {
_toastLiveData.value = it.message
} else {
_toastLiveData.value = "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
}
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.value = "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
}
)
)
} else {
_toastLiveData.value = "구매를 하지 못했습니다.\n고객센터로 문의해 주시기 바랍니다."
}
}
fun showToast(message: String) {
_toastLiveData.value = message
}
fun setLoading(isLoading: Boolean) {
_isLoading.value = isLoading
}
}

View File

@ -1,14 +0,0 @@
package kr.co.vividnext.sodalive.mypage.can.charge.iap
import com.google.gson.annotations.SerializedName
import kr.co.vividnext.sodalive.mypage.can.payment.PaymentGateway
data class GoogleChargeRequest(
@SerializedName("title") val title: String,
@SerializedName("chargeCan") val chargeCan: Int,
@SerializedName("price") val price: Double,
@SerializedName("currencyCode") val currencyCode: String,
@SerializedName("productId") val productId: String,
@SerializedName("purchaseToken") val purchaseToken: String,
@SerializedName("paymentGateway") val paymentGateway: PaymentGateway = PaymentGateway.GOOGLE_IAP
)

View File

@ -1,101 +0,0 @@
package kr.co.vividnext.sodalive.mypage.can.charge.pg
import android.annotation.SuppressLint
import android.graphics.Rect
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.base.BaseFragment
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.FragmentCanChargePgBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeActivity
import org.koin.android.ext.android.inject
class CanChargePgFragment : BaseFragment<FragmentCanChargePgBinding>(
FragmentCanChargePgBinding::inflate
) {
private val viewModel: CanChargePgViewModel by inject()
private lateinit var adapter: CanChargePgAdapter
private lateinit var loadingDialog: LoadingDialog
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupView()
bindData()
viewModel.getCanCharges()
}
fun setupView() {
loadingDialog = LoadingDialog(requireActivity(), layoutInflater)
val recyclerView = binding.rvChargeCan
adapter = CanChargePgAdapter {
(requireActivity() as CanChargeActivity).selectCan(it)
}
recyclerView.layoutManager = LinearLayoutManager(
requireContext(),
LinearLayoutManager.VERTICAL,
false
)
recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
outRect.left = 13.3f.dpToPx().toInt()
outRect.right = 13.3f.dpToPx().toInt()
when (parent.getChildAdapterPosition(view)) {
0 -> {
outRect.top = 13.3f.dpToPx().toInt()
outRect.bottom = 6.7f.dpToPx().toInt()
}
adapter.itemCount - 1 -> {
outRect.top = 6.7f.dpToPx().toInt()
outRect.bottom = 26.7f.dpToPx().toInt()
}
else -> {
outRect.top = 6.7f.dpToPx().toInt()
outRect.bottom = 6.7f.dpToPx().toInt()
}
}
}
})
recyclerView.adapter = adapter
}
@SuppressLint("NotifyDataSetChanged")
private fun bindData() {
viewModel.canChargeLiveData.observe(viewLifecycleOwner) {
adapter.items.addAll(it)
adapter.notifyDataSetChanged()
}
viewModel.toastLiveData.observe(viewLifecycleOwner) {
it?.let { Toast.makeText(requireActivity(), it, Toast.LENGTH_LONG).show() }
}
viewModel.isLoading.observe(viewLifecycleOwner) {
if (it) {
loadingDialog.show(screenWidth)
} else {
loadingDialog.dismiss()
}
}
}
}

View File

@ -23,8 +23,8 @@ import kr.co.vividnext.sodalive.databinding.ActivityCanPaymentBinding
import kr.co.vividnext.sodalive.extensions.fontSpan import kr.co.vividnext.sodalive.extensions.fontSpan
import kr.co.vividnext.sodalive.extensions.moneyFormat import kr.co.vividnext.sodalive.extensions.moneyFormat
import kr.co.vividnext.sodalive.mypage.auth.BootpayResponse import kr.co.vividnext.sodalive.mypage.auth.BootpayResponse
import kr.co.vividnext.sodalive.mypage.can.charge.pg.CanResponse import kr.co.vividnext.sodalive.mypage.can.charge.CanResponse
import kr.co.vividnext.sodalive.mypage.can.charge.pg.VerifyRequest import kr.co.vividnext.sodalive.mypage.can.charge.VerifyRequest
import kr.co.vividnext.sodalive.mypage.can.status.CanStatusActivity import kr.co.vividnext.sodalive.mypage.can.status.CanStatusActivity
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
@ -140,8 +140,8 @@ class CanPaymentActivity : BaseActivity<ActivityCanPaymentBinding>(
R.font.gmarket_sans_bold R.font.gmarket_sans_bold
) )
view.setTextColor(ContextCompat.getColor(applicationContext, R.color.color_3bb9f1)) view.setTextColor(ContextCompat.getColor(applicationContext, R.color.color_9970ff))
view.setBackgroundResource(R.drawable.bg_round_corner_10_13181b_3bb9f1) view.setBackgroundResource(R.drawable.bg_round_corner_10_4d9970ff_9970ff)
} }
private fun requestCharge() { private fun requestCharge() {
@ -211,13 +211,15 @@ class CanPaymentActivity : BaseActivity<ActivityCanPaymentBinding>(
request, request,
onSuccess = { onSuccess = {
Toast.makeText(applicationContext, "캔이 충전되었습니다", Toast.LENGTH_LONG).show() Toast.makeText(applicationContext, "캔이 충전되었습니다", Toast.LENGTH_LONG).show()
SharedPreferenceManager.can += (canResponse!!.rewardCan + canResponse!!.can)
if (gotoPrevPage) { if (gotoPrevPage) {
setResult(RESULT_OK) setResult(RESULT_OK)
} else { } else {
val intent = Intent(applicationContext, CanStatusActivity::class.java) val intent = Intent(applicationContext, CanStatusActivity::class.java)
startActivity(intent) startActivity(intent)
} }
SharedPreferenceManager.can += (canResponse!!.rewardCan + canResponse!!.can)
finish() finish()
}, },
onFailure = { onFailure = {

View File

@ -8,8 +8,8 @@ import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.base.BaseViewModel import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.mypage.can.CanRepository import kr.co.vividnext.sodalive.mypage.can.CanRepository
import kr.co.vividnext.sodalive.mypage.can.charge.pg.ChargeRequest import kr.co.vividnext.sodalive.mypage.can.charge.ChargeRequest
import kr.co.vividnext.sodalive.mypage.can.charge.pg.VerifyRequest import kr.co.vividnext.sodalive.mypage.can.charge.VerifyRequest
class CanPaymentViewModel(private val repository: CanRepository) : BaseViewModel() { class CanPaymentViewModel(private val repository: CanRepository) : BaseViewModel() {

View File

@ -31,6 +31,8 @@ class OnBoardingActivity : BaseActivity<ActivityOnboardingBinding>(
adapter.addFragment(OnBoardingFragment(R.drawable.img_guide_2)) adapter.addFragment(OnBoardingFragment(R.drawable.img_guide_2))
adapter.addFragment(OnBoardingFragment(R.drawable.img_guide_3)) adapter.addFragment(OnBoardingFragment(R.drawable.img_guide_3))
adapter.addFragment(OnBoardingFragment(R.drawable.img_guide_4)) adapter.addFragment(OnBoardingFragment(R.drawable.img_guide_4))
adapter.addFragment(OnBoardingFragment(R.drawable.img_guide_5))
adapter.addFragment(OnBoardingFragment(R.drawable.img_guide_6))
binding.viewPager.adapter = adapter binding.viewPager.adapter = adapter
} }

View File

@ -6,7 +6,6 @@ import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import com.google.firebase.dynamiclinks.PendingDynamicLinkData import com.google.firebase.dynamiclinks.PendingDynamicLinkData
import com.google.firebase.dynamiclinks.ktx.dynamicLinks import com.google.firebase.dynamiclinks.ktx.dynamicLinks
@ -33,9 +32,6 @@ class SplashActivity : BaseActivity<ActivitySplashBinding>(ActivitySplashBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val lp = binding.ivText.layoutParams as ConstraintLayout.LayoutParams
lp.topMargin = screenHeight * 787 / 2337
binding.ivText.layoutParams = lp
setupRemoteConfig() setupRemoteConfig()
fetchAndroidLatestVersion() fetchAndroidLatestVersion()
} }

View File

@ -4,7 +4,6 @@ import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.explorer.profile.MemberBlockRequest import kr.co.vividnext.sodalive.explorer.profile.MemberBlockRequest
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser
import kr.co.vividnext.sodalive.main.GaidUpdateRequest
import kr.co.vividnext.sodalive.main.PushTokenUpdateRequest import kr.co.vividnext.sodalive.main.PushTokenUpdateRequest
import kr.co.vividnext.sodalive.mypage.MyPageResponse import kr.co.vividnext.sodalive.mypage.MyPageResponse
import kr.co.vividnext.sodalive.mypage.profile.ProfileResponse import kr.co.vividnext.sodalive.mypage.profile.ProfileResponse
@ -138,10 +137,4 @@ interface UserApi {
@Part multipartFile: MultipartBody.Part, @Part multipartFile: MultipartBody.Part,
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String
): Single<ApiResponse<String>> ): Single<ApiResponse<String>>
@PUT("/member/adid/update")
fun updateGaid(
@Body request: GaidUpdateRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
} }

View File

@ -4,7 +4,6 @@ import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.explorer.profile.MemberBlockRequest import kr.co.vividnext.sodalive.explorer.profile.MemberBlockRequest
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser
import kr.co.vividnext.sodalive.main.GaidUpdateRequest
import kr.co.vividnext.sodalive.main.PushTokenUpdateRequest import kr.co.vividnext.sodalive.main.PushTokenUpdateRequest
import kr.co.vividnext.sodalive.mypage.MyPageResponse import kr.co.vividnext.sodalive.mypage.MyPageResponse
import kr.co.vividnext.sodalive.mypage.profile.ProfileResponse import kr.co.vividnext.sodalive.mypage.profile.ProfileResponse
@ -109,11 +108,4 @@ class UserRepository(private val userApi: UserApi) {
fun getProfile(token: String): Single<ApiResponse<ProfileResponse>> { fun getProfile(token: String): Single<ApiResponse<ProfileResponse>> {
return userApi.getMyProfile(authHeader = token) return userApi.getMyProfile(authHeader = token)
} }
fun updateGaid(
request: GaidUpdateRequest,
token: String
): Single<ApiResponse<Any>> {
return userApi.updateGaid(request, authHeader = token)
}
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 825 B

After

Width:  |  Height:  |  Size: 824 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

After

Width:  |  Height:  |  Size: 775 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 560 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 320 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 652 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 699 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -1,4 +0,0 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/color_b3333333" />
</shape>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"> <shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/color_111111" /> <solid android:color="@color/color_4d9970ff" />
<corners android:radius="13.3dp" /> <corners android:radius="10dp" />
<stroke <stroke
android:width="1dp" android:width="1dp"
android:color="@color/color_111111" /> android:color="@color/color_9970ff" />
</shape> </shape>

View File

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

View File

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

View File

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

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_ecf9ff" />
<corners android:radius="2.6dp" />
<stroke
android:width="1dp"
android:color="@color/color_ecf9ff" />
</shape>

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_ffecf7" />
<corners android:radius="2.6dp" />
<stroke
android:width="1dp"
android:color="@color/color_ffecf7" />
</shape>

View File

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

View File

@ -4,5 +4,5 @@
<corners android:radius="33.3dp" /> <corners android:radius="33.3dp" />
<stroke <stroke
android:width="3dp" android:width="3dp"
android:color="@color/color_3bb9f1" /> android:color="@color/color_9970ff" />
</shape> </shape>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"> <shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/color_777777" /> <solid android:color="@color/color_4d9970ff" />
<corners android:radius="6.7dp" /> <corners android:radius="6.7dp" />
<stroke <stroke
android:width="1dp" android:width="1.3dp"
android:color="@color/color_777777" /> android:color="@color/color_9970ff" />
</shape> </shape>

View File

@ -75,7 +75,7 @@
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:contentDescription="@null" android:contentDescription="@null"
android:visibility="gone" android:visibility="gone"
tools:src="@drawable/btn_following_big" /> tools:src="@drawable/btn_notification_selected" />
</RelativeLayout> </RelativeLayout>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
@ -122,13 +122,13 @@
android:id="@+id/tv_preview_no" android:id="@+id/tv_preview_no"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@drawable/bg_round_corner_46_7_333333" android:background="@drawable/bg_round_corner_46_7_333333"
android:padding="13.3dp" android:padding="13.3dp"
android:visibility="gone"
android:layout_centerInParent="true"
android:text="해당 콘텐츠는 크리에이터의 요청으로\n미리듣기를 제공하지 않습니다" android:text="해당 콘텐츠는 크리에이터의 요청으로\n미리듣기를 제공하지 않습니다"
android:textColor="@color/color_eeeeee" android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" android:textSize="16.7sp" />
android:visibility="gone" />
<SeekBar <SeekBar
android:id="@+id/sb_progress" android:id="@+id/sb_progress"
@ -140,24 +140,6 @@
android:paddingEnd="0dp" android:paddingEnd="0dp"
android:progressDrawable="@drawable/audio_content_player_seekbar" android:progressDrawable="@drawable/audio_content_player_seekbar"
android:thumb="@null" /> android:thumb="@null" />
<FrameLayout
android:id="@+id/fl_sold_out"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/color_99000000"
android:visibility="gone" />
<TextView
android:id="@+id/tv_sold_out_big"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:fontFamily="@font/gmarket_sans_bold"
android:text="Sold Out"
android:textColor="@color/white"
android:textSize="36.7sp"
android:visibility="gone" />
</RelativeLayout> </RelativeLayout>
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
@ -382,75 +364,6 @@
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
<RelativeLayout
android:id="@+id/rl_limited_edition"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="13.3dp"
android:background="@drawable/bg_round_corner_5_3_14262d"
android:paddingHorizontal="10.3dp"
android:paddingVertical="8dp"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:text="한정판"
android:textColor="@color/color_3bb9f1"
android:textSize="13.3sp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:orientation="horizontal"
tools:ignore="RelativeOverlap">
<TextView
android:id="@+id/tv_sold_out_small"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_round_corner_2_6_transparent_d2d2d2"
android:fontFamily="@font/gmarket_sans_medium"
android:paddingHorizontal="5.3dp"
android:paddingVertical="3.3dp"
android:text="Sold Out"
android:textColor="@color/color_d2d2d2"
android:textSize="12sp"
android:visibility="gone" />
<TextView
android:id="@+id/tv_remaining"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:text="잔여수량"
android:textColor="@color/color_d2d2d2"
android:textSize="13.3sp" />
<TextView
android:id="@+id/tv_remaining_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5.3dp"
android:fontFamily="@font/gmarket_sans_bold"
android:textColor="@color/color_3bb9f1"
android:textSize="13.3sp"
tools:text="10" />
<TextView
android:id="@+id/tv_total_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:textColor="@color/color_d2d2d2"
android:textSize="13.3sp"
tools:text=" / 10" />
</LinearLayout>
</RelativeLayout>
<FrameLayout <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -509,21 +422,6 @@
</RelativeLayout> </RelativeLayout>
</FrameLayout> </FrameLayout>
<TextView
android:id="@+id/tv_purchase_sold_out"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="18.3dp"
android:background="@drawable/bg_round_corner_5_3_525252"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:paddingVertical="16dp"
android:text="해당 콘텐츠가 매진되었습니다."
android:textColor="@color/white"
android:textSize="13.3sp"
android:visibility="gone" />
<RelativeLayout <RelativeLayout
android:id="@+id/ll_purchase" android:id="@+id/ll_purchase"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -681,7 +579,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="11dp" android:layout_marginStart="11dp"
android:ellipsize="end" android:ellipsize="end"
android:lineSpacingExtra="8dp"
android:maxLines="1" android:maxLines="1"
android:textColor="@color/color_bbbbbb" android:textColor="@color/color_bbbbbb"
android:textSize="12sp" android:textSize="12sp"

View File

@ -1,29 +1,20 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <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_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> xmlns:app="http://schemas.android.com/apk/res-auto">
<include <include
android:id="@+id/toolbar" android:id="@+id/toolbar"
layout="@layout/detail_toolbar" /> layout="@layout/detail_toolbar" />
<com.google.android.material.tabs.TabLayout <androidx.recyclerview.widget.RecyclerView
android:id="@+id/tabs" android:id="@+id/rv_charge_can"
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="50dp" android:layout_height="0dp"
android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent"
app:tabIndicatorColor="@color/color_80d8ff" app:layout_constraintEnd_toEndOf="parent"
app:tabIndicatorFullWidth="true" app:layout_constraintStart_toStartOf="parent"
app:tabIndicatorHeight="1.3dp" app:layout_constraintTop_toBottomOf="@+id/toolbar" />
app:tabSelectedTextColor="@color/color_eeeeee"
app:tabTextAppearance="@style/tabText"
app:tabTextColor="@color/color_777777" />
<FrameLayout </androidx.constraintlayout.widget.ConstraintLayout>
android:id="@+id/fl_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

Some files were not shown because too many files have changed in this diff Show More