Compare commits
59 Commits
1ec0d8540a
...
e52075a692
Author | SHA1 | Date |
---|---|---|
|
e52075a692 | |
|
a29c50eae3 | |
|
8a2a497fcf | |
|
6786988e63 | |
|
2dd5e2d96c | |
|
03320d9cec | |
|
2101cdeb86 | |
|
f0a8ca5823 | |
|
daed389264 | |
|
6a558ad25c | |
|
6e3a4e1125 | |
|
79cb4b995a | |
|
84b9dd0841 | |
|
4004dcd99e | |
|
ebbe7e8917 | |
|
196d5c6cfd | |
|
f4626a7cd5 | |
|
4ed97d4600 | |
|
c2cf05ef64 | |
|
0e131f6661 | |
|
d091c47a0c | |
|
6bd5d26882 | |
|
e02ea116ff | |
|
012d1d94d5 | |
|
39e49b08d9 | |
|
2a84d2dc41 | |
|
66c9b38e04 | |
|
8f35f7a573 | |
|
c653563512 | |
|
a75821ed06 | |
|
ca6416c697 | |
|
f7b3caf320 | |
|
62d839b69b | |
|
a9c3ea953d | |
|
5be9720fcc | |
|
ec096b5831 | |
|
51c5e5f32c | |
|
b6d7e0b0e9 | |
|
49209c4c4a | |
|
6b466ac7d7 | |
|
d6182f9e03 | |
|
2e24a298ff | |
|
d7d43bc7be | |
|
2d0c4ea738 | |
|
af4e802259 | |
|
ad9e97161c | |
|
1712f509dc | |
|
78dd3b2785 | |
|
f7f4638526 | |
|
66690a6f89 | |
|
4e56eb9bde | |
|
e3349e41a1 | |
|
239ccb9018 | |
|
6ff17e1ba6 | |
|
1c88a90024 | |
|
dd54e5b97a | |
|
f4180eec14 | |
|
b81576bdaf | |
|
bf43926d7d |
|
@ -40,8 +40,8 @@ android {
|
|||
applicationId "kr.co.vividnext.sodalive"
|
||||
minSdk 23
|
||||
targetSdk 33
|
||||
versionCode 22
|
||||
versionName "1.6.0"
|
||||
versionCode 46
|
||||
versionName "1.8.21"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
@ -149,4 +149,7 @@ dependencies {
|
|||
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
|
||||
|
||||
implementation "com.michalsvec:single-row-calednar:1.0.0"
|
||||
|
||||
// google in-app-purchase
|
||||
implementation "com.android.billingclient:billing-ktx:6.2.0"
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
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.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
@ -84,7 +85,9 @@
|
|||
<activity android:name=".settings.terms.TermsActivity" />
|
||||
<activity android:name=".user.find_password.FindPasswordActivity" />
|
||||
<activity android:name=".mypage.can.status.CanStatusActivity" />
|
||||
<activity android:name=".mypage.can.charge.CanChargeActivity" />
|
||||
<activity
|
||||
android:name=".mypage.can.charge.CanChargeActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden" />
|
||||
<activity android:name=".mypage.can.payment.CanPaymentActivity" />
|
||||
<activity android:name=".mypage.can.coupon.CanCouponActivity" />
|
||||
<activity android:name=".live.room.create.LiveRoomCreateActivity" />
|
||||
|
|
|
@ -12,6 +12,7 @@ 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.base.BaseActivity
|
||||
import kr.co.vividnext.sodalive.common.Constants
|
||||
import kr.co.vividnext.sodalive.common.GridSpacingItemDecoration
|
||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||
import kr.co.vividnext.sodalive.databinding.ActivityAudioContentNewAllBinding
|
||||
import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
|
||||
|
@ -89,8 +90,10 @@ class AudioContentNewAllActivity : BaseActivity<ActivityAudioContentNewAllBindin
|
|||
}
|
||||
|
||||
private fun setupNewContent() {
|
||||
val spanCount = 3
|
||||
val spacing = 40
|
||||
newContentAdapter = AudioContentNewAllAdapter(
|
||||
itemWidth = (screenWidth - 42f.dpToPx().toInt()) / 3,
|
||||
itemWidth = (screenWidth - (spacing * (spanCount + 1))) / 3,
|
||||
onClickItem = {
|
||||
startActivity(
|
||||
Intent(this, AudioContentDetailActivity::class.java).apply {
|
||||
|
@ -107,23 +110,8 @@ class AudioContentNewAllActivity : BaseActivity<ActivityAudioContentNewAllBindin
|
|||
}
|
||||
)
|
||||
|
||||
binding.rvContent.layoutManager = GridLayoutManager(this, 3)
|
||||
|
||||
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.layoutManager = GridLayoutManager(this, spanCount)
|
||||
binding.rvContent.addItemDecoration(GridSpacingItemDecoration(spanCount, spacing, true))
|
||||
|
||||
binding.rvContent.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package kr.co.vividnext.sodalive.audio_content.all
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
@ -8,8 +11,12 @@ import androidx.constraintlayout.widget.ConstraintLayout
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import coil.load
|
||||
import coil.transform.CircleCropTransformation
|
||||
import coil.transform.RoundedCornersTransformation
|
||||
import com.orhanobut.logger.Logger
|
||||
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.audio_content.main.GetAudioContentMainItem
|
||||
import kr.co.vividnext.sodalive.databinding.ItemAudioContentNewAllBinding
|
||||
|
@ -23,24 +30,27 @@ class AudioContentNewAllAdapter(
|
|||
) : RecyclerView.Adapter<AudioContentNewAllAdapter.ViewHolder>() {
|
||||
|
||||
inner class ViewHolder(
|
||||
private val context: Context,
|
||||
private val binding: ItemAudioContentNewAllBinding,
|
||||
private val onClickItem: (Long) -> Unit,
|
||||
private val onClickCreator: (Long) -> Unit
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
fun bind(item: GetAudioContentMainItem) {
|
||||
Logger.e("item: $item")
|
||||
binding.ivAudioContentCoverImage.load(item.coverImageUrl) {
|
||||
crossfade(true)
|
||||
placeholder(R.drawable.ic_place_holder)
|
||||
transformations(RoundedCornersTransformation(2.7f.dpToPx()))
|
||||
Glide
|
||||
.with(context)
|
||||
.load(item.coverImageUrl)
|
||||
.apply(
|
||||
RequestOptions().transform(
|
||||
CenterCrop(),
|
||||
RoundedCorners(8)
|
||||
)
|
||||
)
|
||||
.into(binding.ivAudioContentCoverImage)
|
||||
|
||||
val layoutParams = binding.ivAudioContentCoverImage
|
||||
.layoutParams as ConstraintLayout.LayoutParams
|
||||
|
||||
layoutParams.width = itemWidth
|
||||
layoutParams.height = itemWidth
|
||||
binding.ivAudioContentCoverImage.layoutParams = layoutParams
|
||||
}
|
||||
val layoutParams = binding.ivAudioContentCoverImage.layoutParams as ConstraintLayout.LayoutParams
|
||||
layoutParams.width = itemWidth
|
||||
layoutParams.height = itemWidth
|
||||
binding.ivAudioContentCoverImage.layoutParams = layoutParams
|
||||
|
||||
binding.ivAudioContentCreator.load(item.creatorProfileImageUrl) {
|
||||
crossfade(true)
|
||||
|
@ -72,6 +82,7 @@ class AudioContentNewAllAdapter(
|
|||
parent: ViewGroup,
|
||||
viewType: Int
|
||||
) = ViewHolder(
|
||||
parent.context,
|
||||
ItemAudioContentNewAllBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
|
|
|
@ -16,6 +16,7 @@ import kr.co.vividnext.sodalive.audio_content.all.AudioContentNewAllAdapter
|
|||
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
|
||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||
import kr.co.vividnext.sodalive.common.Constants
|
||||
import kr.co.vividnext.sodalive.common.GridSpacingItemDecoration
|
||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||
import kr.co.vividnext.sodalive.databinding.ActivityAudioContentAllByThemeBinding
|
||||
import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
|
||||
|
@ -55,8 +56,10 @@ class AudioContentAllByThemeActivity : BaseActivity<ActivityAudioContentAllByThe
|
|||
loadingDialog = LoadingDialog(this, layoutInflater)
|
||||
binding.toolbar.tvBack.setOnClickListener { finish() }
|
||||
|
||||
val spanCount = 3
|
||||
val spacing = 40
|
||||
adapter = AudioContentNewAllAdapter(
|
||||
itemWidth = (screenWidth - 42f.dpToPx().toInt()) / 3,
|
||||
itemWidth = (screenWidth - (spacing * (spanCount + 1))) / 3,
|
||||
onClickItem = {
|
||||
startActivity(
|
||||
Intent(this, AudioContentDetailActivity::class.java).apply {
|
||||
|
@ -73,23 +76,8 @@ class AudioContentAllByThemeActivity : BaseActivity<ActivityAudioContentAllByThe
|
|||
}
|
||||
)
|
||||
|
||||
binding.rvContentAll.layoutManager = GridLayoutManager(this, 3)
|
||||
|
||||
binding.rvContentAll.addItemDecoration(object : RecyclerView.ItemDecoration() {
|
||||
override fun getItemOffsets(
|
||||
outRect: Rect,
|
||||
view: View,
|
||||
parent: RecyclerView,
|
||||
state: RecyclerView.State
|
||||
) {
|
||||
super.getItemOffsets(outRect, view, parent, state)
|
||||
|
||||
outRect.top = 8f.dpToPx().toInt()
|
||||
outRect.bottom = 8f.dpToPx().toInt()
|
||||
outRect.left = 4f.dpToPx().toInt()
|
||||
outRect.right = 4f.dpToPx().toInt()
|
||||
}
|
||||
})
|
||||
binding.rvContentAll.layoutManager = GridLayoutManager(this, spanCount)
|
||||
binding.rvContentAll.addItemDecoration(GridSpacingItemDecoration(spanCount, spacing, true))
|
||||
|
||||
binding.rvContentAll.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
|
|
|
@ -17,6 +17,7 @@ import kr.co.vividnext.sodalive.audio_content.all.AudioContentNewAllAdapter
|
|||
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
|
||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||
import kr.co.vividnext.sodalive.common.Constants
|
||||
import kr.co.vividnext.sodalive.common.GridSpacingItemDecoration
|
||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||
import kr.co.vividnext.sodalive.databinding.ActivityAudioContentCurationBinding
|
||||
import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
|
||||
|
@ -54,8 +55,10 @@ class AudioContentCurationActivity : BaseActivity<ActivityAudioContentCurationBi
|
|||
binding.toolbar.tvBack.text = title
|
||||
binding.toolbar.tvBack.setOnClickListener { finish() }
|
||||
|
||||
val spanCount = 3
|
||||
val spacing = 40
|
||||
adapter = AudioContentNewAllAdapter(
|
||||
itemWidth = (screenWidth - 42f.dpToPx().toInt()) / 3,
|
||||
itemWidth = (screenWidth - (spacing * (spanCount + 1))) / 3,
|
||||
onClickItem = {
|
||||
startActivity(
|
||||
Intent(this, AudioContentDetailActivity::class.java).apply {
|
||||
|
@ -72,23 +75,8 @@ class AudioContentCurationActivity : BaseActivity<ActivityAudioContentCurationBi
|
|||
}
|
||||
)
|
||||
|
||||
binding.rvCuration.layoutManager = GridLayoutManager(this, 3)
|
||||
|
||||
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.layoutManager = GridLayoutManager(this, spanCount)
|
||||
binding.rvCuration.addItemDecoration(GridSpacingItemDecoration(spanCount, spacing, true))
|
||||
|
||||
binding.rvCuration.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
|
|
|
@ -238,10 +238,16 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
|
|||
}
|
||||
})
|
||||
|
||||
val layoutParams = binding.ivCover.layoutParams as RelativeLayout.LayoutParams
|
||||
layoutParams.width = (screenWidth - 13.3f.dpToPx()).toInt()
|
||||
layoutParams.height = (screenWidth - 13.3f.dpToPx()).toInt()
|
||||
binding.ivCover.layoutParams = layoutParams
|
||||
val ivCoverLp = binding.ivCover.layoutParams as RelativeLayout.LayoutParams
|
||||
ivCoverLp.width = (screenWidth - 13.3f.dpToPx()).toInt()
|
||||
ivCoverLp.height = (screenWidth - 13.3f.dpToPx()).toInt()
|
||||
binding.ivCover.layoutParams = ivCoverLp
|
||||
|
||||
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.llDonation.setOnClickListener {
|
||||
val dialog = LiveRoomDonationDialog(
|
||||
|
@ -481,6 +487,7 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
|
|||
if (response.releaseDate != null) {
|
||||
binding.llPurchase.visibility = View.VISIBLE
|
||||
binding.llPurchasePrice.visibility = View.GONE
|
||||
binding.tvPurchaseSoldOut.visibility = View.GONE
|
||||
binding.tvReleaseDate.visibility = View.VISIBLE
|
||||
binding.llPurchase.background = ContextCompat.getDrawable(
|
||||
applicationContext,
|
||||
|
@ -494,26 +501,50 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
|
|||
response.orderType == null &&
|
||||
response.creator.creatorId != SharedPreferenceManager.userId
|
||||
) {
|
||||
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) {
|
||||
" 대여하기"
|
||||
if (
|
||||
response.totalContentCount != null && response.remainingContentCount != null &&
|
||||
response.remainingContentCount <= 0
|
||||
) {
|
||||
binding.llPurchase.visibility = View.GONE
|
||||
binding.tvPurchaseSoldOut.visibility = View.VISIBLE
|
||||
} 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.llPurchase.setOnClickListener {
|
||||
showOrderDialog(audioContent = response, isOnlyRental = response.isOnlyRental)
|
||||
binding.tvStrPurchaseOrRental.text = if (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 {
|
||||
binding.llPurchase.visibility = View.GONE
|
||||
binding.tvPurchaseSoldOut.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -527,6 +558,8 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
|
|||
.apply(RequestOptions().override((screenWidth - 13.3f.dpToPx()).toInt()))
|
||||
.into(binding.ivCover)
|
||||
|
||||
binding.flSoldOut.visibility = View.GONE
|
||||
binding.tvSoldOutBig.visibility = View.GONE
|
||||
binding.ivPlayOrPause.visibility = View.GONE
|
||||
binding.tvPreviewNo.visibility = View.GONE
|
||||
binding.tvTotalDuration.text = " / ${response.duration}"
|
||||
|
@ -536,9 +569,16 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
|
|||
response.price > 0
|
||||
|
||||
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 &&
|
||||
!isAlertPreview ||
|
||||
(isAlertPreview && response.isActivePreview)
|
||||
(response.isActivePreview && response.contentUrl.isNotBlank())
|
||||
) {
|
||||
binding.ivPlayOrPause.visibility = View.VISIBLE
|
||||
binding.ivPlayOrPause.setOnClickListener {
|
||||
|
@ -572,7 +612,7 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
|
|||
R.drawable.btn_audio_content_preview_play
|
||||
}
|
||||
)
|
||||
} else {
|
||||
} else if (response.releaseDate == null) {
|
||||
binding.tvPreviewNo.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
@ -657,6 +697,33 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
|
|||
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) {
|
||||
|
@ -702,7 +769,7 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
|
|||
binding.ivFollow.visibility = View.VISIBLE
|
||||
|
||||
if (creator.isFollowing) {
|
||||
binding.ivFollow.setImageResource(R.drawable.btn_notification_selected)
|
||||
binding.ivFollow.setImageResource(R.drawable.btn_following_big)
|
||||
binding.ivFollow.setOnClickListener {
|
||||
viewModel.unRegisterNotification(
|
||||
contentId = audioContentId,
|
||||
|
@ -710,7 +777,7 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
|
|||
)
|
||||
}
|
||||
} else {
|
||||
binding.ivFollow.setImageResource(R.drawable.btn_notification)
|
||||
binding.ivFollow.setImageResource(R.drawable.btn_follow_big)
|
||||
binding.ivFollow.setOnClickListener {
|
||||
viewModel.registerNotification(
|
||||
contentId = audioContentId,
|
||||
|
|
|
@ -15,6 +15,9 @@ data class GetAudioContentDetailResponse(
|
|||
@SerializedName("price") val price: Int,
|
||||
@SerializedName("duration") val duration: 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("isAdult") val isAdult: Boolean,
|
||||
@SerializedName("isMosaic") val isMosaic: Boolean,
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -53,6 +53,7 @@ import kr.co.vividnext.sodalive.live.room.LiveRoomViewModel
|
|||
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.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.LiveTagViewModel
|
||||
import kr.co.vividnext.sodalive.live.room.update.LiveRoomEditViewModel
|
||||
|
@ -73,7 +74,8 @@ import kr.co.vividnext.sodalive.mypage.auth.AuthApi
|
|||
import kr.co.vividnext.sodalive.mypage.auth.AuthRepository
|
||||
import kr.co.vividnext.sodalive.mypage.can.CanApi
|
||||
import kr.co.vividnext.sodalive.mypage.can.CanRepository
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeViewModel
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.iap.CanChargeIapViewModel
|
||||
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.payment.CanPaymentViewModel
|
||||
import kr.co.vividnext.sodalive.mypage.can.status.CanStatusViewModel
|
||||
|
@ -151,6 +153,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
|
|||
single { ApiBuilder().build(get(), CanApi::class.java) }
|
||||
single { ApiBuilder().build(get(), AuthApi::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(), TermsApi::class.java) }
|
||||
single { ApiBuilder().build(get(), EventApi::class.java) }
|
||||
|
@ -176,7 +179,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
|
|||
viewModel { LiveViewModel(get(), get(), get(), get()) }
|
||||
viewModel { MyPageViewModel(get(), get()) }
|
||||
viewModel { CanStatusViewModel(get()) }
|
||||
viewModel { CanChargeViewModel(get()) }
|
||||
viewModel { CanChargePgViewModel(get()) }
|
||||
viewModel { CanPaymentViewModel(get()) }
|
||||
viewModel { LiveRoomDetailViewModel(get()) }
|
||||
viewModel { LiveRoomCreateViewModel(get()) }
|
||||
|
@ -229,12 +232,13 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
|
|||
viewModel { CreatorCommunityWriteViewModel(get()) }
|
||||
viewModel { CreatorCommunityModifyViewModel(get()) }
|
||||
viewModel { CanCouponViewModel(get()) }
|
||||
viewModel { CanChargeIapViewModel(get()) }
|
||||
}
|
||||
|
||||
private val repositoryModule = module {
|
||||
factory { UserRepository(get()) }
|
||||
factory { TermsRepository(get()) }
|
||||
factory { LiveRepository(get(), get()) }
|
||||
factory { LiveRepository(get(), get(), get()) }
|
||||
factory { EventRepository(get()) }
|
||||
factory { LiveRecommendRepository(get()) }
|
||||
factory { AuthRepository(get()) }
|
||||
|
|
|
@ -615,12 +615,12 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
|
|||
}
|
||||
|
||||
if (creator.isNotification) {
|
||||
layoutUserProfile.ivNotification.setImageResource(R.drawable.btn_notification_selected)
|
||||
layoutUserProfile.ivNotification.setImageResource(R.drawable.btn_following_big)
|
||||
layoutUserProfile.ivNotification.setOnClickListener {
|
||||
viewModel.unFollow(creator.creatorId)
|
||||
}
|
||||
} else {
|
||||
layoutUserProfile.ivNotification.setImageResource(R.drawable.btn_notification)
|
||||
layoutUserProfile.ivNotification.setImageResource(R.drawable.btn_follow_big)
|
||||
layoutUserProfile.ivNotification.setOnClickListener {
|
||||
viewModel.follow(creator.creatorId)
|
||||
}
|
||||
|
|
|
@ -31,13 +31,13 @@ class UserFollowerListAdapter(
|
|||
if (item.isFollow != null) {
|
||||
binding.ivNotification.visibility = View.VISIBLE
|
||||
if (item.isFollow) {
|
||||
binding.ivNotification.setImageResource(R.drawable.btn_notification_selected)
|
||||
binding.ivNotification.setImageResource(R.drawable.btn_following_big)
|
||||
binding.ivNotification.setOnClickListener {
|
||||
onClickUnRegisterNotification(item.userId)
|
||||
clear()
|
||||
}
|
||||
} else {
|
||||
binding.ivNotification.setImageResource(R.drawable.btn_notification)
|
||||
binding.ivNotification.setImageResource(R.drawable.btn_follow_big)
|
||||
binding.ivNotification.setOnClickListener {
|
||||
onClickRegisterNotification(item.userId)
|
||||
clear()
|
||||
|
|
|
@ -32,7 +32,7 @@ class FollowingCreatorAdapter(
|
|||
|
||||
binding.ivNotification.visibility = View.VISIBLE
|
||||
if (item.isFollow) {
|
||||
binding.ivNotification.setImageResource(R.drawable.btn_notification_selected)
|
||||
binding.ivNotification.setImageResource(R.drawable.btn_following_big)
|
||||
binding.ivNotification.setOnClickListener {
|
||||
val index = items.indexOf(item)
|
||||
val copyItem = item.copy(isFollow = false)
|
||||
|
@ -42,7 +42,7 @@ class FollowingCreatorAdapter(
|
|||
onClickUnRegisterNotification(item.creatorId)
|
||||
}
|
||||
} else {
|
||||
binding.ivNotification.setImageResource(R.drawable.btn_notification)
|
||||
binding.ivNotification.setImageResource(R.drawable.btn_follow_big)
|
||||
binding.ivNotification.setOnClickListener {
|
||||
val index = items.indexOf(item)
|
||||
val copyItem = item.copy(isFollow = true)
|
||||
|
|
|
@ -160,7 +160,7 @@ interface LiveApi {
|
|||
fun donation(
|
||||
@Body request: LiveRoomDonationRequest,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<Any>>
|
||||
): Single<ApiResponse<String>>
|
||||
|
||||
@POST("/live/room/donation/refund/{id}")
|
||||
fun refundDonation(
|
||||
|
|
|
@ -15,6 +15,7 @@ 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.LiveRoomDonationRequest
|
||||
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.UserApi
|
||||
import okhttp3.MultipartBody
|
||||
|
@ -23,7 +24,8 @@ import java.util.TimeZone
|
|||
|
||||
class LiveRepository(
|
||||
private val api: LiveApi,
|
||||
private val userApi: UserApi
|
||||
private val userApi: UserApi,
|
||||
private val menuApi: MenuApi
|
||||
) {
|
||||
fun roomList(
|
||||
dateString: String? = null,
|
||||
|
@ -161,7 +163,7 @@ class LiveRepository(
|
|||
can: Int,
|
||||
message: String,
|
||||
token: String
|
||||
): Single<ApiResponse<Any>> {
|
||||
): Single<ApiResponse<String>> {
|
||||
return api.donation(
|
||||
request = LiveRoomDonationRequest(
|
||||
roomId = roomId,
|
||||
|
@ -230,4 +232,9 @@ class LiveRepository(
|
|||
request: CancelLiveReservationRequest,
|
||||
token: String
|
||||
) = api.cancelReservation(request, authHeader = token)
|
||||
|
||||
fun getAllMenu(creatorId: Long, token: String) = menuApi.getAllMenu(
|
||||
creatorId = creatorId,
|
||||
authHeader = token
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,12 +1,21 @@
|
|||
package kr.co.vividnext.sodalive.live.now
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import coil.transform.CircleCropTransformation
|
||||
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.databinding.ItemLiveNowBinding
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
|
@ -20,15 +29,28 @@ class LiveNowAdapter(
|
|||
var items = mutableListOf<GetRoomListResponse>()
|
||||
|
||||
inner class ViewHolder(
|
||||
private val context: Context,
|
||||
private val binding: ItemLiveNowBinding
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
fun bind(item: GetRoomListResponse) {
|
||||
binding.ivCover.loadUrl(item.coverImageUrl) {
|
||||
crossfade(true)
|
||||
placeholder(R.drawable.ic_place_holder)
|
||||
transformations(RoundedCornersTransformation(4.7f.dpToPx()))
|
||||
}
|
||||
Glide
|
||||
.with(context)
|
||||
.load(item.coverImageUrl)
|
||||
.apply(
|
||||
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) {
|
||||
View.VISIBLE
|
||||
} else {
|
||||
|
@ -36,11 +58,18 @@ class LiveNowAdapter(
|
|||
}
|
||||
|
||||
if (item.price > 0) {
|
||||
binding.tvPrice.text = "유료"
|
||||
binding.tvPrice.setBackgroundResource(R.drawable.bg_round_corner_10_881609)
|
||||
binding.tvPrice.text = "${item.price}"
|
||||
binding.tvPrice.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_can_white,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
)
|
||||
binding.tvPrice.setBackgroundResource(R.drawable.bg_round_corner_13_3_dd4500)
|
||||
} else {
|
||||
binding.tvPrice.text = "무료"
|
||||
binding.tvPrice.setBackgroundResource(R.drawable.bg_round_corner_10_643bc8)
|
||||
binding.tvPrice.setCompoundDrawables(null, null, null, null)
|
||||
binding.tvPrice.setBackgroundResource(R.drawable.bg_round_corner_13_3_111111)
|
||||
}
|
||||
|
||||
if (item.tags.isNotEmpty()) {
|
||||
|
@ -63,6 +92,7 @@ class LiveNowAdapter(
|
|||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
|
||||
parent.context,
|
||||
ItemLiveNowBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
|
|
|
@ -2,19 +2,19 @@ package kr.co.vividnext.sodalive.live.now.all
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.graphics.Rect
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kr.co.vividnext.sodalive.audio_content.AudioContentPlayService
|
||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||
import kr.co.vividnext.sodalive.common.Constants
|
||||
import kr.co.vividnext.sodalive.common.GridSpacingItemDecoration
|
||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
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.live.LiveViewModel
|
||||
import kr.co.vividnext.sodalive.live.room.LiveRoomActivity
|
||||
|
@ -46,8 +46,10 @@ class LiveNowAllActivity : BaseActivity<ActivityLiveNowAllBinding>(
|
|||
binding.toolbar.tvBack.setOnClickListener { finish() }
|
||||
loadingDialog = LoadingDialog(this, layoutInflater)
|
||||
|
||||
val spanCount = 3
|
||||
val spacing = 40
|
||||
val recyclerView = binding.rvLive
|
||||
adapter = LiveNowAllAdapter {
|
||||
adapter = LiveNowAllAdapter(itemWidth = (screenWidth - (spacing * (spanCount + 1))) / 3) {
|
||||
val detailFragment = LiveRoomDetailFragment(
|
||||
it.roomId,
|
||||
onClickParticipant = { enterLiveRoom(it.roomId) },
|
||||
|
@ -63,41 +65,8 @@ class LiveNowAllActivity : BaseActivity<ActivityLiveNowAllBinding>(
|
|||
)
|
||||
}
|
||||
|
||||
recyclerView.layoutManager = LinearLayoutManager(
|
||||
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.layoutManager = GridLayoutManager(this, spanCount)
|
||||
recyclerView.addItemDecoration(GridSpacingItemDecoration(spanCount, spacing, false))
|
||||
|
||||
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
|
@ -113,6 +82,8 @@ class LiveNowAllActivity : BaseActivity<ActivityLiveNowAllBinding>(
|
|||
}
|
||||
})
|
||||
|
||||
recyclerView.adapter = adapter
|
||||
|
||||
binding.swipeRefreshLayout.setOnRefreshListener {
|
||||
adapter.clear()
|
||||
viewModel.page = 1
|
||||
|
|
|
@ -2,20 +2,29 @@ package kr.co.vividnext.sodalive.live.now.all
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import coil.load
|
||||
import coil.transform.CircleCropTransformation
|
||||
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.databinding.ItemLiveNowAllBinding
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
import kr.co.vividnext.sodalive.extensions.moneyFormat
|
||||
import kr.co.vividnext.sodalive.extensions.loadUrl
|
||||
import kr.co.vividnext.sodalive.live.GetRoomListResponse
|
||||
|
||||
class LiveNowAllAdapter(
|
||||
private val itemWidth: Int,
|
||||
private val onClick: (GetRoomListResponse) -> Unit
|
||||
) : RecyclerView.Adapter<LiveNowAllAdapter.ViewHolder>() {
|
||||
|
||||
|
@ -27,48 +36,72 @@ class LiveNowAllAdapter(
|
|||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
@SuppressLint("SetTextI18n")
|
||||
fun bind(item: GetRoomListResponse) {
|
||||
binding.ivCover.load(item.coverImageUrl) {
|
||||
crossfade(true)
|
||||
placeholder(R.drawable.bg_placeholder)
|
||||
transformations(RoundedCornersTransformation(4.7f.dpToPx()))
|
||||
}
|
||||
binding.tvNickname.text = item.creatorNickname
|
||||
binding.tvTitle.text = item.title
|
||||
Glide
|
||||
.with(context)
|
||||
.load(item.coverImageUrl)
|
||||
.apply(
|
||||
RequestOptions().transform(
|
||||
CenterCrop(),
|
||||
RoundedCorners(14)
|
||||
)
|
||||
)
|
||||
.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) {
|
||||
View.VISIBLE
|
||||
} else {
|
||||
View.GONE
|
||||
}
|
||||
|
||||
if (item.numberOfPeople > item.numberOfParticipate) {
|
||||
binding.tvAvailableParticipate.text = "참여가능"
|
||||
binding.tvAvailableParticipate.setTextColor(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.color_3bb9f1
|
||||
)
|
||||
)
|
||||
} 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.setCompoundDrawables(null, null, null, null)
|
||||
} else {
|
||||
binding.tvPrice.text = item.price.moneyFormat()
|
||||
if (item.price > 0) {
|
||||
binding.tvPrice.text = "${item.price}"
|
||||
binding.tvPrice.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.ic_can_white,
|
||||
0,
|
||||
0,
|
||||
R.drawable.ic_can,
|
||||
0
|
||||
)
|
||||
binding.tvPrice.setBackgroundResource(R.drawable.bg_round_corner_13_3_dd4500)
|
||||
} else {
|
||||
binding.tvPrice.text = "무료"
|
||||
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 {
|
||||
binding.tvTags.visibility = View.GONE
|
||||
}
|
||||
|
||||
binding.tvTitle.text = item.title
|
||||
binding.tvNickname.text = item.creatorNickname
|
||||
binding.ivProfile.loadUrl(item.creatorProfileImage) {
|
||||
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) }
|
||||
|
|
|
@ -26,12 +26,11 @@ import android.widget.Toast
|
|||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import coil.load
|
||||
import coil.transform.CircleCropTransformation
|
||||
import com.bumptech.glide.Glide
|
||||
import com.github.dhaval2404.imagepicker.ImagePicker
|
||||
import com.google.gson.Gson
|
||||
import com.orhanobut.logger.Logger
|
||||
|
@ -115,6 +114,18 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
|||
private var isNoChatting = false
|
||||
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) {
|
||||
override fun onTick(millisUntilFinished: Long) {
|
||||
remainingNoChattingTime -= 1
|
||||
|
@ -454,6 +465,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
|||
dialog.show(screenWidth)
|
||||
}
|
||||
binding.tvNotification.setOnClickListener { viewModel.toggleShowNotice() }
|
||||
binding.tvMenuPan.setOnClickListener { viewModel.toggleShowMenuPan() }
|
||||
|
||||
binding.tvBgSwitch.setOnClickListener { viewModel.toggleBackgroundImage() }
|
||||
binding.llDonation.setOnClickListener {
|
||||
|
@ -696,37 +708,44 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
|||
|
||||
if (response.creatorId == SharedPreferenceManager.userId) {
|
||||
binding.ivEdit.setOnClickListener {
|
||||
roomInfoEditDialog.setRoomInfo(response.title, response.notice)
|
||||
roomInfoEditDialog.setCoverImageUrl(response.coverImageUrl)
|
||||
roomInfoEditDialog.setConfirmAction { newTitle, newContent, newCoverImageUri ->
|
||||
viewModel.editLiveRoomInfo(
|
||||
response.roomId,
|
||||
newTitle,
|
||||
newContent,
|
||||
newCoverImageUri,
|
||||
onSuccess = {
|
||||
binding.tvTitle.text = newTitle
|
||||
setNoticeAndClickableUrl(binding.tvNotice, newContent)
|
||||
viewModel.getAllMenuPreset {
|
||||
roomInfoEditDialog.setRoomInfo(response.title, response.notice)
|
||||
roomInfoEditDialog.setCoverImageUrl(response.coverImageUrl)
|
||||
roomInfoEditDialog.setMenuPreset(it)
|
||||
roomInfoEditDialog.setConfirmAction { newTitle, newContent, newCoverImageUri, isActivateMenu, menuId, menu ->
|
||||
viewModel.editLiveRoomInfo(
|
||||
response.roomId,
|
||||
newTitle,
|
||||
newContent,
|
||||
newCoverImageUri,
|
||||
isActivateMenu,
|
||||
menuId,
|
||||
menu,
|
||||
onSuccess = {
|
||||
Toast.makeText(
|
||||
applicationContext,
|
||||
"라이브 정보가 수정되었습니다.",
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
|
||||
if (newCoverImageUri != null) {
|
||||
binding.ivCover.load(newCoverImageUri)
|
||||
agora.sendRawMessageToGroup(
|
||||
rawMessage = Gson().toJson(
|
||||
LiveRoomChatRawMessage(
|
||||
type = LiveRoomChatRawMessageType.EDIT_ROOM_INFO,
|
||||
message = "",
|
||||
can = 0,
|
||||
donationMessage = ""
|
||||
)
|
||||
).toByteArray()
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
agora.sendRawMessageToGroup(
|
||||
rawMessage = Gson().toJson(
|
||||
LiveRoomChatRawMessage(
|
||||
type = LiveRoomChatRawMessageType.EDIT_ROOM_INFO,
|
||||
message = "",
|
||||
can = 0,
|
||||
donationMessage = ""
|
||||
)
|
||||
).toByteArray()
|
||||
)
|
||||
}
|
||||
)
|
||||
handler.post {
|
||||
roomInfoEditDialog.show(screenWidth)
|
||||
}
|
||||
}
|
||||
|
||||
roomInfoEditDialog.show(screenWidth)
|
||||
}
|
||||
|
||||
binding.ivEdit.visibility = View.VISIBLE
|
||||
|
@ -781,7 +800,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
|||
if (response.creatorId != SharedPreferenceManager.userId) {
|
||||
binding.ivCreatorFollow.visibility = View.VISIBLE
|
||||
if (response.isFollowing) {
|
||||
binding.ivCreatorFollow.setImageResource(R.drawable.btn_select_checked)
|
||||
binding.ivCreatorFollow.setImageResource(R.drawable.btn_following)
|
||||
binding.ivCreatorFollow.setOnClickListener {
|
||||
viewModel.creatorUnFollow(
|
||||
creatorId = response.creatorId,
|
||||
|
@ -789,7 +808,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
|||
)
|
||||
}
|
||||
} else {
|
||||
binding.ivCreatorFollow.setImageResource(R.drawable.btn_plus_round)
|
||||
binding.ivCreatorFollow.setImageResource(R.drawable.btn_follow)
|
||||
binding.ivCreatorFollow.setOnClickListener {
|
||||
viewModel.creatorFollow(
|
||||
creatorId = response.creatorId,
|
||||
|
@ -807,6 +826,15 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
|||
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()) {
|
||||
joinChannel(response)
|
||||
}
|
||||
|
@ -840,6 +868,34 @@ 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) {
|
||||
binding.tvTotalCan.text = it.moneyFormat()
|
||||
}
|
||||
|
@ -1026,6 +1082,8 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
|||
agora.muteLocalAudioStream(false)
|
||||
agora.setClientRole(io.agora.rtc2.Constants.CLIENT_ROLE_AUDIENCE)
|
||||
handler.postDelayed({
|
||||
binding.tvChangeListener.visibility = View.GONE
|
||||
binding.tvChangeListener.setOnClickListener { }
|
||||
binding.ivMicrophoneMute.setImageResource(R.drawable.ic_mic_on)
|
||||
binding.flMicrophoneMute.visibility = View.GONE
|
||||
binding.ivNotiMicrophoneMute.visibility = View.GONE
|
||||
|
@ -1211,16 +1269,18 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
|||
|
||||
private fun donation(can: Int, message: String) {
|
||||
val rawMessage = "${can}캔을 후원하셨습니다.\uD83D\uDCB0\uD83E\uDE99"
|
||||
val donationRawMessage = Gson().toJson(
|
||||
LiveRoomChatRawMessage(
|
||||
type = LiveRoomChatRawMessageType.DONATION,
|
||||
message = rawMessage,
|
||||
can = can,
|
||||
donationMessage = message
|
||||
)
|
||||
)
|
||||
|
||||
viewModel.donation(roomId, can, message) {
|
||||
viewModel.donation(roomId, can, message) { signatureImage ->
|
||||
val donationRawMessage = Gson().toJson(
|
||||
LiveRoomChatRawMessage(
|
||||
type = LiveRoomChatRawMessageType.DONATION,
|
||||
message = rawMessage,
|
||||
can = can,
|
||||
signatureImageUrl = signatureImage,
|
||||
donationMessage = message
|
||||
)
|
||||
)
|
||||
|
||||
agora.sendRawMessageToGroup(
|
||||
rawMessage = donationRawMessage.toByteArray(),
|
||||
onSuccess = {
|
||||
|
@ -1240,6 +1300,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
|||
)
|
||||
invalidateChat()
|
||||
viewModel.addDonationCan(can)
|
||||
addSignatureImage(signatureImage)
|
||||
}
|
||||
},
|
||||
onFailure = {
|
||||
|
@ -1346,6 +1407,9 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
|||
)
|
||||
invalidateChat()
|
||||
viewModel.addDonationCan(rawMessage.can)
|
||||
addSignatureImage(
|
||||
imageUrl = rawMessage.signatureImageUrl ?: ""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1622,6 +1686,18 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
|||
showDialog(content = "스피커가 되었어요!")
|
||||
setBroadcaster()
|
||||
viewModel.getRoomInfo(roomId)
|
||||
binding.tvChangeListener.visibility = View.VISIBLE
|
||||
binding.tvChangeListener.setOnClickListener {
|
||||
handler.post {
|
||||
viewModel.setListener(
|
||||
roomId,
|
||||
SharedPreferenceManager.userId
|
||||
) {
|
||||
setAudience()
|
||||
viewModel.getRoomInfo(roomId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1675,6 +1751,38 @@ 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 {
|
||||
private const val noChattingTime = 180L
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
|||
import kr.co.vividnext.sodalive.live.LiveRepository
|
||||
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.menu.GetMenuPresetResponse
|
||||
import kr.co.vividnext.sodalive.live.room.profile.GetLiveRoomUserProfileResponse
|
||||
import kr.co.vividnext.sodalive.live.room.update.EditLiveRoomInfoRequest
|
||||
import kr.co.vividnext.sodalive.live.roulette.RouletteItem
|
||||
|
@ -54,6 +55,10 @@ class LiveRoomViewModel(
|
|||
val isShowNotice: LiveData<Boolean>
|
||||
get() = _isShowNotice
|
||||
|
||||
private val _isShowMenuPan = MutableLiveData(false)
|
||||
val isShowMenuPan: LiveData<Boolean>
|
||||
get() = _isShowMenuPan
|
||||
|
||||
private val _totalDonationCan = MutableLiveData(0)
|
||||
val totalDonationCan: LiveData<Int>
|
||||
get() = _totalDonationCan
|
||||
|
@ -355,9 +360,20 @@ class LiveRoomViewModel(
|
|||
}
|
||||
|
||||
fun toggleShowNotice() {
|
||||
_isShowMenuPan.value = false
|
||||
_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() {
|
||||
_isBgOn.value = !isBgOn.value!!
|
||||
}
|
||||
|
@ -367,6 +383,9 @@ class LiveRoomViewModel(
|
|||
newTitle: String,
|
||||
newContent: String,
|
||||
newCoverImageUri: Uri? = null,
|
||||
isActivateMenu: Boolean?,
|
||||
menuId: Long,
|
||||
menu: String,
|
||||
onSuccess: () -> Unit
|
||||
) {
|
||||
val request = EditLiveRoomInfoRequest(
|
||||
|
@ -382,17 +401,25 @@ class LiveRoomViewModel(
|
|||
},
|
||||
numberOfPeople = 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 (request.title != null || request.notice != null) {
|
||||
val requestJson = if (
|
||||
request.title != null ||
|
||||
request.notice != null ||
|
||||
request.isActiveMenuPan != null ||
|
||||
request.menuPan.isNotBlank()
|
||||
) {
|
||||
Gson().toJson(request)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val coverImage = if (newCoverImageUri != null) {
|
||||
val file = File(getRealPathFromURI(newCoverImageUri!!))
|
||||
val file = File(getRealPathFromURI(newCoverImageUri))
|
||||
MultipartBody.Part.createFormData(
|
||||
"coverImage",
|
||||
file.name,
|
||||
|
@ -481,7 +508,7 @@ class LiveRoomViewModel(
|
|||
)
|
||||
}
|
||||
|
||||
fun donation(roomId: Long, can: Int, message: String, onSuccess: () -> Unit) {
|
||||
fun donation(roomId: Long, can: Int, message: String, onSuccess: (String) -> Unit) {
|
||||
_isLoading.postValue(true)
|
||||
compositeDisposable.add(
|
||||
repository.donation(roomId, can, message, "Bearer ${SharedPreferenceManager.token}")
|
||||
|
@ -492,7 +519,7 @@ class LiveRoomViewModel(
|
|||
_isLoading.value = false
|
||||
if (it.success) {
|
||||
SharedPreferenceManager.can -= can
|
||||
onSuccess()
|
||||
onSuccess(it.data ?: "")
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
|
@ -900,6 +927,40 @@ 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(
|
||||
can: Int,
|
||||
items: List<RouletteItem>,
|
||||
|
|
|
@ -257,7 +257,7 @@ data class LiveRoomDonationChat(
|
|||
)
|
||||
),
|
||||
0,
|
||||
chat.indexOf("캔", 0, true) + 2,
|
||||
chat.indexOf("캔", 0, true) + 1,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ data class LiveRoomChatRawMessage(
|
|||
@SerializedName("type") val type: LiveRoomChatRawMessageType,
|
||||
@SerializedName("message") val message: String,
|
||||
@SerializedName("can") val can: Int,
|
||||
@SerializedName("signatureImageUrl") val signatureImageUrl: String? = null,
|
||||
@SerializedName("donationMessage") val donationMessage: String?,
|
||||
@SerializedName("isActiveRoulette") val isActiveRoulette: Boolean? = null
|
||||
)
|
||||
|
|
|
@ -14,5 +14,8 @@ data class CreateLiveRoomRequest(
|
|||
@SerializedName("beginDateTimeString") val beginDateTimeString: String? = null,
|
||||
@SerializedName("timezone") val timezone: String,
|
||||
@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
|
||||
)
|
||||
|
|
|
@ -9,6 +9,7 @@ import android.os.Handler
|
|||
import android.os.Looper
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
|
@ -127,6 +128,8 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
|
|||
viewModel.setTimeNow(
|
||||
intent.getBooleanExtra(Constants.EXTRA_LIVE_TIME_NOW, true)
|
||||
)
|
||||
|
||||
viewModel.getAllMenuPreset()
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n", "ClickableViewAccessibility")
|
||||
|
@ -295,6 +298,30 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
|
|||
}
|
||||
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")
|
||||
|
@ -348,6 +375,15 @@ 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) {
|
||||
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
|
||||
}
|
||||
|
@ -573,6 +609,44 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
|
|||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -613,4 +687,69 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
|
|||
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
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import kr.co.vividnext.sodalive.base.BaseViewModel
|
|||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.live.LiveRepository
|
||||
import kr.co.vividnext.sodalive.live.room.LiveRoomType
|
||||
import kr.co.vividnext.sodalive.live.room.menu.GetMenuPresetResponse
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
|
@ -22,6 +23,10 @@ class LiveRoomCreateViewModel(
|
|||
private val repository: LiveRepository
|
||||
) : BaseViewModel() {
|
||||
|
||||
enum class SelectedMenu {
|
||||
MENU_1, MENU_2, MENU_3
|
||||
}
|
||||
|
||||
private val _roomTypeLiveData = MutableLiveData(LiveRoomType.OPEN)
|
||||
val roomTypeLiveData: LiveData<LiveRoomType>
|
||||
get() = _roomTypeLiveData
|
||||
|
@ -58,6 +63,18 @@ class LiveRoomCreateViewModel(
|
|||
val isAdultLiveData: LiveData<Boolean>
|
||||
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?
|
||||
|
||||
var title = ""
|
||||
|
@ -70,6 +87,10 @@ class LiveRoomCreateViewModel(
|
|||
var coverImagePath: String? = null
|
||||
var password: String? = null
|
||||
|
||||
private var menuId = 0L
|
||||
var menu = ""
|
||||
val menuList = mutableListOf<GetMenuPresetResponse>()
|
||||
|
||||
fun setRoomType(roomType: LiveRoomType) {
|
||||
if (_roomTypeLiveData.value!! != roomType) {
|
||||
_roomTypeLiveData.postValue(roomType)
|
||||
|
@ -115,7 +136,18 @@ class LiveRoomCreateViewModel(
|
|||
password
|
||||
} else {
|
||||
null
|
||||
}
|
||||
},
|
||||
menuPanId = if (_isActivateMenuLiveData.value!!) {
|
||||
menuId
|
||||
} else {
|
||||
0
|
||||
},
|
||||
menuPan = if (_isActivateMenuLiveData.value!!) {
|
||||
menu
|
||||
} else {
|
||||
""
|
||||
},
|
||||
isActiveMenuPan = _isActivateMenuLiveData.value!!
|
||||
)
|
||||
|
||||
val requestJson = Gson().toJson(request)
|
||||
|
@ -207,6 +239,11 @@ class LiveRoomCreateViewModel(
|
|||
return false
|
||||
}
|
||||
|
||||
if (_isActivateMenuLiveData.value!! && menu.isBlank()) {
|
||||
_toastLiveData.postValue("메뉴판은 빈칸일 수 없습니다.")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -252,4 +289,80 @@ 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("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ data class GetRoomInfoResponse(
|
|||
@SerializedName("listenerList") val listenerList: List<LiveRoomMember>,
|
||||
@SerializedName("managerList") val managerList: List<LiveRoomMember>,
|
||||
@SerializedName("donationRankingTop3UserIds") val donationRankingTop3UserIds: List<Long>,
|
||||
@SerializedName("menuPan") val menuPan: String,
|
||||
@SerializedName("isActiveRoulette") val isActiveRoulette: Boolean,
|
||||
@SerializedName("isPrivateRoom") val isPrivateRoom: Boolean,
|
||||
@SerializedName("password") val password: String? = null
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
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
|
||||
)
|
|
@ -0,0 +1,15 @@
|
|||
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>>>
|
||||
}
|
|
@ -7,5 +7,8 @@ data class EditLiveRoomInfoRequest(
|
|||
@SerializedName("notice") val notice: String?,
|
||||
@SerializedName("numberOfPeople") val numberOfPeople: Int?,
|
||||
@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
|
||||
)
|
||||
|
|
|
@ -1,20 +1,32 @@
|
|||
package kr.co.vividnext.sodalive.live.room.update
|
||||
|
||||
import android.app.Activity
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.net.Uri
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
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.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import coil.load
|
||||
import coil.transform.RoundedCornersTransformation
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.databinding.DialogLiveRoomInfoUpdateBinding
|
||||
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(
|
||||
activity: Activity,
|
||||
private val activity: AppCompatActivity,
|
||||
layoutInflater: LayoutInflater,
|
||||
onClickImagePicker: () -> Unit
|
||||
) {
|
||||
|
@ -24,6 +36,17 @@ class LiveRoomInfoEditDialog(
|
|||
private var coverImageUrl: String? = 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 {
|
||||
val dialogBuilder = AlertDialog.Builder(activity)
|
||||
dialogBuilder.setView(dialogView.root)
|
||||
|
@ -35,6 +58,77 @@ class LiveRoomInfoEditDialog(
|
|||
dialogView.ivPhotoPicker.setOnClickListener { onClickImagePicker() }
|
||||
dialogView.ivClose.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(
|
||||
|
@ -42,7 +136,7 @@ class LiveRoomInfoEditDialog(
|
|||
currentContent: String,
|
||||
) {
|
||||
dialogView.etTitle.setText(currentTitle)
|
||||
dialogView.etContent.setText(currentContent)
|
||||
dialogView.etNotice.setText(currentContent)
|
||||
}
|
||||
|
||||
fun setCoverImageUri(coverImageUri: Uri) {
|
||||
|
@ -63,13 +157,53 @@ class LiveRoomInfoEditDialog(
|
|||
}
|
||||
}
|
||||
|
||||
fun setConfirmAction(confirmAction: (String, String, Uri?) -> Unit) {
|
||||
fun setMenuPreset(menuList: List<GetMenuPresetResponse>) {
|
||||
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 {
|
||||
alertDialog.dismiss()
|
||||
|
||||
val newTitle = dialogView.etTitle.text.toString()
|
||||
val newContent = dialogView.etContent.text.toString()
|
||||
confirmAction(newTitle, newContent, coverImageUri)
|
||||
val newContent = dialogView.etNotice.text.toString()
|
||||
val menu = dialogView.etMenu.text.toString()
|
||||
|
||||
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
|
||||
coverImageUrl = null
|
||||
}
|
||||
|
@ -85,4 +219,103 @@ class LiveRoomInfoEditDialog(
|
|||
|
||||
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("")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
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>
|
||||
)
|
|
@ -2,18 +2,32 @@ package kr.co.vividnext.sodalive.live.roulette
|
|||
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import kr.co.vividnext.sodalive.live.roulette.config.CreateOrUpdateRouletteRequest
|
||||
import kr.co.vividnext.sodalive.live.roulette.config.CreateRouletteRequest
|
||||
import kr.co.vividnext.sodalive.live.roulette.config.RouletteApi
|
||||
import kr.co.vividnext.sodalive.live.roulette.config.UpdateRouletteRequest
|
||||
|
||||
class RouletteRepository(private val api: RouletteApi) {
|
||||
fun createOrUpdateRoulette(
|
||||
request: CreateOrUpdateRouletteRequest,
|
||||
fun createRoulette(
|
||||
request: CreateRouletteRequest,
|
||||
token: String
|
||||
) = api.createOrUpdateRoulette(
|
||||
) = api.createRoulette(
|
||||
request = request,
|
||||
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(
|
||||
creatorId = creatorId,
|
||||
authHeader = token
|
||||
|
|
|
@ -3,7 +3,7 @@ package kr.co.vividnext.sodalive.live.roulette.config
|
|||
import com.google.gson.annotations.SerializedName
|
||||
import kr.co.vividnext.sodalive.live.roulette.RouletteItem
|
||||
|
||||
data class CreateOrUpdateRouletteRequest(
|
||||
data class CreateRouletteRequest(
|
||||
@SerializedName("can") val can: Int,
|
||||
@SerializedName("isActive") val isActive: Boolean,
|
||||
@SerializedName("items") val items: List<RouletteItem>
|
|
@ -2,35 +2,49 @@ package kr.co.vividnext.sodalive.live.roulette.config
|
|||
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
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.SpinRouletteRequest
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.PUT
|
||||
import retrofit2.http.Path
|
||||
import retrofit2.http.Query
|
||||
|
||||
interface RouletteApi {
|
||||
@POST("/roulette")
|
||||
fun createOrUpdateRoulette(
|
||||
@Body request: CreateOrUpdateRouletteRequest,
|
||||
@POST("/new-roulette")
|
||||
fun createRoulette(
|
||||
@Body request: CreateRouletteRequest,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<Any>>
|
||||
|
||||
@GET("/roulette")
|
||||
@PUT("/new-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(
|
||||
@Query("creatorId") creatorId: Long,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<GetRouletteResponse>>
|
||||
|
||||
@POST("/roulette/spin")
|
||||
@POST("/new-roulette/spin")
|
||||
fun spinRoulette(
|
||||
@Body request: SpinRouletteRequest,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<GetRouletteResponse>>
|
||||
|
||||
@POST("/roulette/refund/{id}")
|
||||
@POST("/new-roulette/refund/{id}")
|
||||
fun refundRouletteDonation(
|
||||
@Path("id") id: Long,
|
||||
@Header("Authorization") authHeader: String
|
||||
|
|
|
@ -13,7 +13,9 @@ import android.view.View
|
|||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.jakewharton.rxbinding4.widget.textChanges
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
|
@ -41,7 +43,7 @@ class RouletteSettingsFragment : BaseFragment<FragmentRouletteSettingsBinding>(
|
|||
|
||||
setupView()
|
||||
bindData()
|
||||
viewModel.getRoulette()
|
||||
viewModel.getAllRoulette()
|
||||
}
|
||||
|
||||
private fun setupView() {
|
||||
|
@ -74,15 +76,57 @@ class RouletteSettingsFragment : BaseFragment<FragmentRouletteSettingsBinding>(
|
|||
handler.postDelayed({
|
||||
imm.hideSoftInputFromWindow(view?.windowToken, 0)
|
||||
}, 100)
|
||||
|
||||
viewModel.createOrUpdateRoulette {
|
||||
val resultIntent = Intent().apply { putExtra(Constants.EXTRA_RESULT_ROULETTE, it) }
|
||||
requireActivity().setResult(Activity.RESULT_OK, resultIntent)
|
||||
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() {
|
||||
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) {
|
||||
binding.ivRouletteIsActive.setImageResource(
|
||||
if (it) R.drawable.btn_toggle_on_big else R.drawable.btn_toggle_off_big
|
||||
|
@ -185,4 +229,69 @@ class RouletteSettingsFragment : BaseFragment<FragmentRouletteSettingsBinding>(
|
|||
|
||||
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
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ 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.live.roulette.GetNewRouletteResponse
|
||||
import kr.co.vividnext.sodalive.live.roulette.RouletteItem
|
||||
import kr.co.vividnext.sodalive.live.roulette.RoulettePreview
|
||||
import kr.co.vividnext.sodalive.live.roulette.RoulettePreviewItem
|
||||
|
@ -15,6 +16,10 @@ import kotlin.math.floor
|
|||
|
||||
class RouletteSettingsViewModel(private val repository: RouletteRepository) : BaseViewModel() {
|
||||
|
||||
enum class SelectedRoulette {
|
||||
ROULETTE_1, ROULETTE_2, ROULETTE_3
|
||||
}
|
||||
|
||||
private var _isLoading = MutableLiveData(false)
|
||||
val isLoading: LiveData<Boolean>
|
||||
get() = _isLoading
|
||||
|
@ -39,9 +44,16 @@ class RouletteSettingsViewModel(private val repository: RouletteRepository) : Ba
|
|||
val roulettePreviewLiveData: LiveData<RoulettePreview>
|
||||
get() = _roulettePreviewLiveData
|
||||
|
||||
private val options = mutableListOf<RouletteOption>()
|
||||
private val _selectedRouletteLiveData = MutableLiveData<SelectedRoulette>()
|
||||
val selectedRouletteLiveData: LiveData<SelectedRoulette>
|
||||
get() = _selectedRouletteLiveData
|
||||
|
||||
var can = 0
|
||||
var isActive = false
|
||||
private var rouletteId = 0L
|
||||
private val options = mutableListOf<RouletteOption>()
|
||||
|
||||
val rouletteList = mutableListOf<GetNewRouletteResponse>()
|
||||
|
||||
fun plusWeight(optionIndex: Int) {
|
||||
val currentOption = options[optionIndex]
|
||||
|
@ -113,66 +125,166 @@ class RouletteSettingsViewModel(private val repository: RouletteRepository) : Ba
|
|||
if (!_isLoading.value!!) {
|
||||
_isLoading.value = true
|
||||
|
||||
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))
|
||||
if (rouletteId > 0) {
|
||||
updateRoulette(onSuccess)
|
||||
} else {
|
||||
createRoulette(onSuccess)
|
||||
}
|
||||
|
||||
val request = CreateOrUpdateRouletteRequest(
|
||||
can = can,
|
||||
isActive = isActive,
|
||||
items = items
|
||||
)
|
||||
|
||||
compositeDisposable.add(
|
||||
repository.createOrUpdateRoulette(
|
||||
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) {
|
||||
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("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun getRoulette() {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
val successMessage = if (isActive) {
|
||||
"${selectedRouletteTitle}로 설정하였습니다."
|
||||
} else if (!isAllActive) {
|
||||
"${selectedRouletteTitle}이 비활성화 되었습니다."
|
||||
} else {
|
||||
"${selectedRouletteTitle}을 설정했습니다."
|
||||
}
|
||||
|
||||
compositeDisposable.add(
|
||||
repository.updateRoulette(
|
||||
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("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun createRoulette(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 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!!) {
|
||||
_isLoading.value = true
|
||||
compositeDisposable.add(
|
||||
repository.getRoulette(
|
||||
repository.getAllRoulette(
|
||||
creatorId = SharedPreferenceManager.userId,
|
||||
token = "Bearer ${SharedPreferenceManager.token}"
|
||||
)
|
||||
|
@ -182,30 +294,9 @@ class RouletteSettingsViewModel(private val repository: RouletteRepository) : Ba
|
|||
{
|
||||
if (it.success) {
|
||||
val data = it.data
|
||||
|
||||
if (data != null && data.items.isNotEmpty()) {
|
||||
_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)
|
||||
}
|
||||
rouletteList.clear()
|
||||
rouletteList.addAll(data ?: listOf())
|
||||
selectRoulette(SelectedRoulette.ROULETTE_1)
|
||||
} else {
|
||||
if (it.message != null) {
|
||||
_toastLiveData.postValue(it.message)
|
||||
|
@ -227,6 +318,56 @@ 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>) {
|
||||
this.options.clear()
|
||||
this.options.addAll(options)
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
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>
|
||||
)
|
|
@ -0,0 +1,7 @@
|
|||
package kr.co.vividnext.sodalive.main
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class GaidUpdateRequest(
|
||||
@SerializedName("adid") val adid: String
|
||||
)
|
|
@ -59,6 +59,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
|
|||
super.onCreate(savedInstanceState)
|
||||
checkPermissions()
|
||||
pushTokenUpdate()
|
||||
gaidUpdate()
|
||||
|
||||
getMemberInfo()
|
||||
getEventPopup()
|
||||
|
@ -338,6 +339,10 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
|
|||
}
|
||||
}
|
||||
|
||||
private fun gaidUpdate() {
|
||||
viewModel.fetchAndUpdateGaid(context = applicationContext)
|
||||
}
|
||||
|
||||
private fun getMemberInfo() {
|
||||
viewModel.getMemberInfo {
|
||||
notificationSettingsDialog.show(screenWidth)
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package kr.co.vividnext.sodalive.main
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.google.android.gms.ads.identifier.AdvertisingIdClient
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
|
@ -15,6 +17,7 @@ import kr.co.vividnext.sodalive.settings.event.EventItem
|
|||
import kr.co.vividnext.sodalive.settings.event.EventRepository
|
||||
import kr.co.vividnext.sodalive.settings.notification.UpdateNotificationSettingRequest
|
||||
import kr.co.vividnext.sodalive.user.UserRepository
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
class MainViewModel(
|
||||
private val userRepository: UserRepository,
|
||||
|
@ -153,4 +156,26 @@ 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -200,7 +200,6 @@ class VoiceMessageFragment : BaseFragment<FragmentVoiceMessageBinding>(
|
|||
override fun run() {
|
||||
handler.post {
|
||||
seekbar.progress = mediaPlayer?.currentPosition ?: 0
|
||||
Logger.e("test")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,11 @@ package kr.co.vividnext.sodalive.mypage.can
|
|||
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.CanResponse
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.ChargeRequest
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.ChargeResponse
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.VerifyRequest
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.iap.GoogleChargeRequest
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.pg.CanResponse
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.pg.ChargeRequest
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.pg.ChargeResponse
|
||||
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.status.GetCanStatusResponse
|
||||
import kr.co.vividnext.sodalive.mypage.can.status.charge.GetCanChargeStatusResponseItem
|
||||
|
@ -17,6 +18,12 @@ import retrofit2.http.POST
|
|||
import retrofit2.http.Query
|
||||
|
||||
interface CanApi {
|
||||
@POST("/charge/google")
|
||||
fun googleChargeCan(
|
||||
@Body request: GoogleChargeRequest,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<Any>>
|
||||
|
||||
@POST("/charge")
|
||||
fun chargeCan(
|
||||
@Body chargeRequest: ChargeRequest,
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
package kr.co.vividnext.sodalive.mypage.can
|
||||
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.ChargeRequest
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.VerifyRequest
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.iap.GoogleChargeRequest
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.pg.ChargeRequest
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.pg.VerifyRequest
|
||||
import kr.co.vividnext.sodalive.mypage.can.coupon.UseCanCouponRequest
|
||||
import java.util.TimeZone
|
||||
|
||||
class CanRepository(private val api: CanApi) {
|
||||
fun googleChargeCan(
|
||||
request: GoogleChargeRequest,
|
||||
token: String
|
||||
) = api.googleChargeCan(request, authHeader = token)
|
||||
|
||||
fun chargeCan(
|
||||
chargeRequest: ChargeRequest,
|
||||
token: String
|
||||
|
|
|
@ -1,33 +1,28 @@
|
|||
package kr.co.vividnext.sodalive.mypage.can.charge
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.graphics.Rect
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||
import kr.co.vividnext.sodalive.common.Constants
|
||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||
import kr.co.vividnext.sodalive.databinding.ActivityCanChargeBinding
|
||||
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.iap.CanChargeIapFragment
|
||||
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 org.koin.android.ext.android.inject
|
||||
import kr.co.vividnext.sodalive.mypage.can.status.CanStatusActivity
|
||||
|
||||
class CanChargeActivity : BaseActivity<ActivityCanChargeBinding>(
|
||||
ActivityCanChargeBinding::inflate
|
||||
) {
|
||||
|
||||
private val viewModel: CanChargeViewModel by inject()
|
||||
private var gotoPrevPage: Boolean = false
|
||||
|
||||
private lateinit var adapter: CanChargeAdapter
|
||||
private lateinit var loadingDialog: LoadingDialog
|
||||
private lateinit var activityResultLauncher: ActivityResultLauncher<Intent>
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
@ -40,87 +35,71 @@ class CanChargeActivity : BaseActivity<ActivityCanChargeBinding>(
|
|||
}
|
||||
|
||||
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(
|
||||
Constants.EXTRA_GO_TO_PREV_PAGE,
|
||||
false
|
||||
)
|
||||
|
||||
val recyclerView = binding.rvChargeCan
|
||||
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)
|
||||
}
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fl_container, CanChargeIapFragment()).commit()
|
||||
}
|
||||
|
||||
recyclerView.layoutManager = LinearLayoutManager(
|
||||
applicationContext,
|
||||
LinearLayoutManager.VERTICAL,
|
||||
false
|
||||
)
|
||||
override fun setupView() {
|
||||
setupToolbar()
|
||||
setupTabs()
|
||||
}
|
||||
|
||||
recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
|
||||
override fun getItemOffsets(
|
||||
outRect: Rect,
|
||||
view: View,
|
||||
parent: RecyclerView,
|
||||
state: RecyclerView.State
|
||||
) {
|
||||
super.getItemOffsets(outRect, view, parent, state)
|
||||
private fun setupToolbar() {
|
||||
binding.toolbar.tvBack.text = "충전하기"
|
||||
binding.toolbar.tvBack.setOnClickListener { finish() }
|
||||
}
|
||||
|
||||
outRect.left = 13.3f.dpToPx().toInt()
|
||||
outRect.right = 13.3f.dpToPx().toInt()
|
||||
private fun setupTabs() {
|
||||
if (SharedPreferenceManager.isAuth) {
|
||||
val tabs = binding.tabs
|
||||
|
||||
when (parent.getChildAdapterPosition(view)) {
|
||||
0 -> {
|
||||
outRect.top = 13.3f.dpToPx().toInt()
|
||||
outRect.bottom = 6.7f.dpToPx().toInt()
|
||||
}
|
||||
tabs.visibility = View.VISIBLE
|
||||
tabs.addTab(tabs.newTab().setText("인 앱 결제"))
|
||||
tabs.addTab(tabs.newTab().setText("PG"))
|
||||
|
||||
adapter.itemCount - 1 -> {
|
||||
outRect.top = 6.7f.dpToPx().toInt()
|
||||
outRect.bottom = 26.7f.dpToPx().toInt()
|
||||
}
|
||||
tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
|
||||
override fun onTabSelected(tab: TabLayout.Tab) {
|
||||
when (tab.position) {
|
||||
0 -> supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fl_container, CanChargeIapFragment()).commit()
|
||||
|
||||
else -> {
|
||||
outRect.top = 6.7f.dpToPx().toInt()
|
||||
outRect.bottom = 6.7f.dpToPx().toInt()
|
||||
1 -> supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fl_container, CanChargePgFragment()).commit()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
recyclerView.adapter = adapter
|
||||
override fun onTabUnselected(tab: TabLayout.Tab) {
|
||||
}
|
||||
|
||||
override fun onTabReselected(tab: TabLayout.Tab) {
|
||||
}
|
||||
})
|
||||
} else {
|
||||
binding.tabs.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun bindData() {
|
||||
viewModel.canChargeLiveData.observe(this) {
|
||||
adapter.items.addAll(it)
|
||||
adapter.notifyDataSetChanged()
|
||||
fun selectCan(model: CanResponse) {
|
||||
val intent = Intent(applicationContext, CanPaymentActivity::class.java)
|
||||
intent.putExtra(Constants.EXTRA_CAN, model)
|
||||
intent.putExtra(Constants.EXTRA_GO_TO_PREV_PAGE, gotoPrevPage)
|
||||
activityResultLauncher.launch(intent)
|
||||
}
|
||||
|
||||
fun successIapCharge() {
|
||||
if (gotoPrevPage) {
|
||||
setResult(RESULT_OK)
|
||||
} else {
|
||||
val intent = Intent(applicationContext, CanStatusActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
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()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,282 @@
|
|||
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)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
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
|
||||
)
|
|
@ -1,4 +1,4 @@
|
|||
package kr.co.vividnext.sodalive.mypage.can.charge
|
||||
package kr.co.vividnext.sodalive.mypage.can.charge.pg
|
||||
|
||||
import android.content.Context
|
||||
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.moneyFormat
|
||||
|
||||
class CanChargeAdapter(
|
||||
class CanChargePgAdapter(
|
||||
private val onClick: (CanResponse) -> Unit
|
||||
) : RecyclerView.Adapter<CanChargeAdapter.ViewHolder>() {
|
||||
) : RecyclerView.Adapter<CanChargePgAdapter.ViewHolder>() {
|
||||
|
||||
val items = mutableListOf<CanResponse>()
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package kr.co.vividnext.sodalive.mypage.can.charge
|
||||
package kr.co.vividnext.sodalive.mypage.can.charge.pg
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
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.mypage.can.CanRepository
|
||||
|
||||
class CanChargeViewModel(private val repository: CanRepository) : BaseViewModel() {
|
||||
class CanChargePgViewModel(private val repository: CanRepository) : BaseViewModel() {
|
||||
private val _canChargesLiveData = MutableLiveData<List<CanResponse>>()
|
||||
val canChargeLiveData: LiveData<List<CanResponse>>
|
||||
get() = _canChargesLiveData
|
|
@ -1,4 +1,4 @@
|
|||
package kr.co.vividnext.sodalive.mypage.can.charge
|
||||
package kr.co.vividnext.sodalive.mypage.can.charge.pg
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.google.gson.annotations.SerializedName
|
|
@ -1,4 +1,4 @@
|
|||
package kr.co.vividnext.sodalive.mypage.can.charge
|
||||
package kr.co.vividnext.sodalive.mypage.can.charge.pg
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kr.co.vividnext.sodalive.mypage.can.payment.PaymentGateway
|
|
@ -23,8 +23,8 @@ import kr.co.vividnext.sodalive.databinding.ActivityCanPaymentBinding
|
|||
import kr.co.vividnext.sodalive.extensions.fontSpan
|
||||
import kr.co.vividnext.sodalive.extensions.moneyFormat
|
||||
import kr.co.vividnext.sodalive.mypage.auth.BootpayResponse
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.CanResponse
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.VerifyRequest
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.pg.CanResponse
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.pg.VerifyRequest
|
||||
import kr.co.vividnext.sodalive.mypage.can.status.CanStatusActivity
|
||||
import org.koin.android.ext.android.inject
|
||||
|
||||
|
@ -140,8 +140,8 @@ class CanPaymentActivity : BaseActivity<ActivityCanPaymentBinding>(
|
|||
R.font.gmarket_sans_bold
|
||||
)
|
||||
|
||||
view.setTextColor(ContextCompat.getColor(applicationContext, R.color.color_9970ff))
|
||||
view.setBackgroundResource(R.drawable.bg_round_corner_10_4d9970ff_9970ff)
|
||||
view.setTextColor(ContextCompat.getColor(applicationContext, R.color.color_3bb9f1))
|
||||
view.setBackgroundResource(R.drawable.bg_round_corner_10_13181b_3bb9f1)
|
||||
}
|
||||
|
||||
private fun requestCharge() {
|
||||
|
@ -211,15 +211,13 @@ class CanPaymentActivity : BaseActivity<ActivityCanPaymentBinding>(
|
|||
request,
|
||||
onSuccess = {
|
||||
Toast.makeText(applicationContext, "캔이 충전되었습니다", Toast.LENGTH_LONG).show()
|
||||
SharedPreferenceManager.can += (canResponse!!.rewardCan + canResponse!!.can)
|
||||
if (gotoPrevPage) {
|
||||
setResult(RESULT_OK)
|
||||
} else {
|
||||
val intent = Intent(applicationContext, CanStatusActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
SharedPreferenceManager.can += (canResponse!!.rewardCan + canResponse!!.can)
|
||||
|
||||
finish()
|
||||
},
|
||||
onFailure = {
|
||||
|
|
|
@ -8,8 +8,8 @@ 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
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.ChargeRequest
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.VerifyRequest
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.pg.ChargeRequest
|
||||
import kr.co.vividnext.sodalive.mypage.can.charge.pg.VerifyRequest
|
||||
|
||||
class CanPaymentViewModel(private val repository: CanRepository) : BaseViewModel() {
|
||||
|
||||
|
|
|
@ -31,8 +31,6 @@ class OnBoardingActivity : BaseActivity<ActivityOnboardingBinding>(
|
|||
adapter.addFragment(OnBoardingFragment(R.drawable.img_guide_2))
|
||||
adapter.addFragment(OnBoardingFragment(R.drawable.img_guide_3))
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.net.Uri
|
|||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.os.bundleOf
|
||||
import com.google.firebase.dynamiclinks.PendingDynamicLinkData
|
||||
import com.google.firebase.dynamiclinks.ktx.dynamicLinks
|
||||
|
@ -32,6 +33,9 @@ class SplashActivity : BaseActivity<ActivitySplashBinding>(ActivitySplashBinding
|
|||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val lp = binding.ivText.layoutParams as ConstraintLayout.LayoutParams
|
||||
lp.topMargin = screenHeight * 787 / 2337
|
||||
binding.ivText.layoutParams = lp
|
||||
setupRemoteConfig()
|
||||
fetchAndroidLatestVersion()
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import io.reactivex.rxjava3.core.Single
|
|||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import kr.co.vividnext.sodalive.explorer.profile.MemberBlockRequest
|
||||
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.mypage.MyPageResponse
|
||||
import kr.co.vividnext.sodalive.mypage.profile.ProfileResponse
|
||||
|
@ -137,4 +138,10 @@ interface UserApi {
|
|||
@Part multipartFile: MultipartBody.Part,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<String>>
|
||||
|
||||
@PUT("/member/adid/update")
|
||||
fun updateGaid(
|
||||
@Body request: GaidUpdateRequest,
|
||||
@Header("Authorization") authHeader: String
|
||||
): Single<ApiResponse<Any>>
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import io.reactivex.rxjava3.core.Single
|
|||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import kr.co.vividnext.sodalive.explorer.profile.MemberBlockRequest
|
||||
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.mypage.MyPageResponse
|
||||
import kr.co.vividnext.sodalive.mypage.profile.ProfileResponse
|
||||
|
@ -108,4 +109,11 @@ class UserRepository(private val userApi: UserApi) {
|
|||
fun getProfile(token: String): Single<ApiResponse<ProfileResponse>> {
|
||||
return userApi.getMyProfile(authHeader = token)
|
||||
}
|
||||
|
||||
fun updateGaid(
|
||||
request: GaidUpdateRequest,
|
||||
token: String
|
||||
): Single<ApiResponse<Any>> {
|
||||
return userApi.updateGaid(request, authHeader = token)
|
||||
}
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 563 B |
Before Width: | Height: | Size: 824 B After Width: | Height: | Size: 825 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 775 KiB After Width: | Height: | Size: 2.2 MiB |
Before Width: | Height: | Size: 560 KiB After Width: | Height: | Size: 1.5 MiB |
Before Width: | Height: | Size: 320 KiB After Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 652 KiB After Width: | Height: | Size: 1.7 MiB |
Before Width: | Height: | Size: 699 KiB After Width: | Height: | Size: 1.7 MiB |
Before Width: | Height: | Size: 554 KiB |
Before Width: | Height: | Size: 252 KiB |
Before Width: | Height: | Size: 349 KiB |
After Width: | Height: | Size: 3.1 MiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 6.6 KiB |
|
@ -0,0 +1,4 @@
|
|||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="@color/color_b3333333" />
|
||||
</shape>
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/color_4d9970ff" />
|
||||
<corners android:radius="10dp" />
|
||||
<solid android:color="@color/color_111111" />
|
||||
<corners android:radius="13.3dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/color_9970ff" />
|
||||
android:color="@color/color_111111" />
|
||||
</shape>
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/color_881609" />
|
||||
<corners android:radius="10dp" />
|
||||
<solid android:color="@color/color_b3333333" />
|
||||
<corners android:radius="13.3dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/color_881609" />
|
||||
android:color="@color/color_b3333333" />
|
||||
</shape>
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/color_643bc8" />
|
||||
<corners android:radius="10dp" />
|
||||
<solid android:color="@color/color_dd4500" />
|
||||
<corners android:radius="13.3dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/color_643bc8" />
|
||||
android:color="@color/color_dd4500" />
|
||||
</shape>
|
|
@ -1,8 +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" />
|
||||
<solid android:color="@android:color/transparent" />
|
||||
<corners android:radius="3.3dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/color_ecf9ff" />
|
||||
android:color="@color/color_d2d2d2" />
|
||||
</shape>
|
|
@ -4,5 +4,5 @@
|
|||
<corners android:radius="33.3dp" />
|
||||
<stroke
|
||||
android:width="3dp"
|
||||
android:color="@color/color_9970ff" />
|
||||
android:color="@color/color_3bb9f1" />
|
||||
</shape>
|
|
@ -1,8 +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" />
|
||||
<solid android:color="@color/color_14262d" />
|
||||
<corners android:radius="5.3dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/color_ffecf7" />
|
||||
android:color="@color/color_14262d" />
|
||||
</shape>
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/color_4d9970ff" />
|
||||
<solid android:color="@color/color_777777" />
|
||||
<corners android:radius="6.7dp" />
|
||||
<stroke
|
||||
android:width="1.3dp"
|
||||
android:color="@color/color_9970ff" />
|
||||
android:width="1dp"
|
||||
android:color="@color/color_777777" />
|
||||
</shape>
|
|
@ -75,7 +75,7 @@
|
|||
android:layout_alignParentEnd="true"
|
||||
android:contentDescription="@null"
|
||||
android:visibility="gone"
|
||||
tools:src="@drawable/btn_notification_selected" />
|
||||
tools:src="@drawable/btn_following_big" />
|
||||
</RelativeLayout>
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
|
@ -122,13 +122,13 @@
|
|||
android:id="@+id/tv_preview_no"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:background="@drawable/bg_round_corner_46_7_333333"
|
||||
android:padding="13.3dp"
|
||||
android:visibility="gone"
|
||||
android:layout_centerInParent="true"
|
||||
android:text="해당 콘텐츠는 크리에이터의 요청으로\n미리듣기를 제공하지 않습니다"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textSize="16.7sp" />
|
||||
android:textSize="16.7sp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/sb_progress"
|
||||
|
@ -140,6 +140,24 @@
|
|||
android:paddingEnd="0dp"
|
||||
android:progressDrawable="@drawable/audio_content_player_seekbar"
|
||||
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>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
@ -364,6 +382,75 @@
|
|||
</LinearLayout>
|
||||
</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
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -422,6 +509,21 @@
|
|||
</RelativeLayout>
|
||||
</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
|
||||
android:id="@+id/ll_purchase"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -579,6 +681,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="11dp"
|
||||
android:ellipsize="end"
|
||||
android:lineSpacingExtra="8dp"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/color_bbbbbb"
|
||||
android:textSize="12sp"
|
||||
|
|
|
@ -1,20 +1,29 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
android:orientation="vertical">
|
||||
|
||||
<include
|
||||
android:id="@+id/toolbar"
|
||||
layout="@layout/detail_toolbar" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_charge_can"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/toolbar" />
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:visibility="gone"
|
||||
app:tabIndicatorColor="@color/color_80d8ff"
|
||||
app:tabIndicatorFullWidth="true"
|
||||
app:tabIndicatorHeight="1.3dp"
|
||||
app:tabSelectedTextColor="@color/color_eeeeee"
|
||||
app:tabTextAppearance="@style/tabText"
|
||||
app:tabTextColor="@color/color_777777" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<FrameLayout
|
||||
android:id="@+id/fl_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</LinearLayout>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/black"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include
|
||||
|
|
|
@ -11,12 +11,12 @@
|
|||
android:id="@+id/iv_cover"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:contentDescription="@null"
|
||||
android:scaleType="fitCenter"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/rl_input_chat"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/rl_input_chat"
|
||||
app:layout_constraintTop_toBottomOf="@id/ll_top" />
|
||||
|
||||
<View
|
||||
|
@ -104,6 +104,59 @@
|
|||
</ScrollView>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/ll_menu_pan"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginHorizontal="60dp"
|
||||
android:layout_marginBottom="13.3dp"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toTopOf="@+id/rl_input_chat"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/ll_top">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@null"
|
||||
android:src="@drawable/ic_notice_triangle" />
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:scrollbars="none">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/color_333333"
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_menu_pan_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/gmarket_sans_bold"
|
||||
android:text="[메뉴판] "
|
||||
android:textColor="@color/white"
|
||||
android:textSize="11.3sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_menu_pan_detail"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/gmarket_sans_light"
|
||||
android:lineSpacingExtra="4dp"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="11.3sp"
|
||||
tools:text="jkljkljkljkljkljkljkl" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/ll_top"
|
||||
android:layout_width="0dp"
|
||||
|
@ -143,6 +196,22 @@
|
|||
android:gravity="end"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_change_listener"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="@drawable/bg_round_corner_5_3_transparent_bbbbbb"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:gravity="center"
|
||||
android:paddingHorizontal="8dp"
|
||||
android:paddingVertical="4.7dp"
|
||||
android:text="리스너 변경"
|
||||
android:textColor="@color/color_eeeeee"
|
||||
android:textSize="12sp"
|
||||
android:visibility="gone"
|
||||
tools:ignore="SmallSp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_bg_switch"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -225,9 +294,9 @@
|
|||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginHorizontal="5.3dp"
|
||||
android:layout_toStartOf="@+id/iv_creator_follow"
|
||||
android:layout_toEndOf="@+id/rl_creator_profile"
|
||||
android:orientation="vertical">
|
||||
|
||||
|
@ -262,27 +331,33 @@
|
|||
tools:text="오늘 라이브 방송은ㅇㄹ너ㅏㅣㅇㄴ럴ㄴ아ㅣㄴㅇ러ㅏㅣ" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_creator_nickname"
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2.7dp"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:lines="1"
|
||||
android:textColor="@color/color_777777"
|
||||
android:textSize="12sp"
|
||||
tools:text="청령" />
|
||||
</LinearLayout>
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_creator_follow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:contentDescription="@null"
|
||||
android:src="@drawable/btn_plus_round" />
|
||||
<TextView
|
||||
android:id="@+id/tv_creator_nickname"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2.7dp"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:lines="1"
|
||||
android:textColor="@color/color_777777"
|
||||
android:textSize="12sp"
|
||||
tools:text="청령" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_creator_follow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5.3dp"
|
||||
android:contentDescription="@null"
|
||||
android:src="@drawable/btn_follow" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
|
@ -308,7 +383,6 @@
|
|||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:background="@drawable/bg_round_corner_5_3_transparent_bbbbbb"
|
||||
android:drawablePadding="2.7dp"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:paddingHorizontal="8dp"
|
||||
android:paddingVertical="5.3dp"
|
||||
|
@ -317,6 +391,23 @@
|
|||
android:textSize="12sp"
|
||||
tools:ignore="SmallSp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_menu_pan"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="5.3dp"
|
||||
android:layout_toEndOf="@+id/tv_notification"
|
||||
android:background="@drawable/bg_round_corner_5_3_transparent_bbbbbb"
|
||||
android:fontFamily="@font/gmarket_sans_medium"
|
||||
android:paddingHorizontal="8dp"
|
||||
android:paddingVertical="5.3dp"
|
||||
android:text="메뉴판"
|
||||
android:textColor="@color/color_bbbbbb"
|
||||
android:textSize="12sp"
|
||||
android:visibility="gone"
|
||||
tools:ignore="SmallSp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -357,7 +448,7 @@
|
|||
android:id="@+id/ll_view_users"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginStart="5.3dp"
|
||||
android:background="@drawable/bg_round_corner_5_3_transparent_bbbbbb"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="11dp"
|
||||
|
@ -571,4 +662,16 @@
|
|||
android:contentDescription="@null"
|
||||
android:src="@drawable/btn_message_send" />
|
||||
</RelativeLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_signature"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="200dp"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:layout_marginBottom="65dp"
|
||||
android:contentDescription="@null"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|