Compare commits

..

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

123 changed files with 680 additions and 3076 deletions

View File

@ -40,8 +40,8 @@ android {
applicationId "kr.co.vividnext.sodalive"
minSdk 23
targetSdk 33
versionCode 46
versionName "1.8.21"
versionCode 22
versionName "1.6.0"
}
buildTypes {
@ -149,7 +149,4 @@ 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"
}

View File

@ -2,7 +2,6 @@
<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" />
@ -85,9 +84,7 @@
<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"
android:configChanges="orientation|screenSize|keyboardHidden" />
<activity android:name=".mypage.can.charge.CanChargeActivity" />
<activity android:name=".mypage.can.payment.CanPaymentActivity" />
<activity android:name=".mypage.can.coupon.CanCouponActivity" />
<activity android:name=".live.room.create.LiveRoomCreateActivity" />

View File

@ -12,7 +12,6 @@ import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.audio_content.main.new_content.AudioContentMainNewContentThemeAdapter
import kr.co.vividnext.sodalive.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
@ -90,10 +89,8 @@ class AudioContentNewAllActivity : BaseActivity<ActivityAudioContentNewAllBindin
}
private fun setupNewContent() {
val spanCount = 3
val spacing = 40
newContentAdapter = AudioContentNewAllAdapter(
itemWidth = (screenWidth - (spacing * (spanCount + 1))) / 3,
itemWidth = (screenWidth - 42f.dpToPx().toInt()) / 3,
onClickItem = {
startActivity(
Intent(this, AudioContentDetailActivity::class.java).apply {
@ -110,8 +107,23 @@ class AudioContentNewAllActivity : BaseActivity<ActivityAudioContentNewAllBindin
}
)
binding.rvContent.layoutManager = GridLayoutManager(this, spanCount)
binding.rvContent.addItemDecoration(GridSpacingItemDecoration(spanCount, spacing, true))
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.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {

View File

@ -1,9 +1,6 @@
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
@ -11,12 +8,8 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.RecyclerView
import coil.load
import coil.transform.CircleCropTransformation
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 coil.transform.RoundedCornersTransformation
import com.orhanobut.logger.Logger
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentMainItem
import kr.co.vividnext.sodalive.databinding.ItemAudioContentNewAllBinding
@ -30,27 +23,24 @@ 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) {
Glide
.with(context)
.load(item.coverImageUrl)
.apply(
RequestOptions().transform(
CenterCrop(),
RoundedCorners(8)
)
)
.into(binding.ivAudioContentCoverImage)
Logger.e("item: $item")
binding.ivAudioContentCoverImage.load(item.coverImageUrl) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
transformations(RoundedCornersTransformation(2.7f.dpToPx()))
val layoutParams = binding.ivAudioContentCoverImage
.layoutParams as ConstraintLayout.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)
@ -82,7 +72,6 @@ class AudioContentNewAllAdapter(
parent: ViewGroup,
viewType: Int
) = ViewHolder(
parent.context,
ItemAudioContentNewAllBinding.inflate(
LayoutInflater.from(parent.context),
parent,

View File

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

View File

@ -17,7 +17,6 @@ import kr.co.vividnext.sodalive.audio_content.all.AudioContentNewAllAdapter
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.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
@ -55,10 +54,8 @@ class AudioContentCurationActivity : BaseActivity<ActivityAudioContentCurationBi
binding.toolbar.tvBack.text = title
binding.toolbar.tvBack.setOnClickListener { finish() }
val spanCount = 3
val spacing = 40
adapter = AudioContentNewAllAdapter(
itemWidth = (screenWidth - (spacing * (spanCount + 1))) / 3,
itemWidth = (screenWidth - 42f.dpToPx().toInt()) / 3,
onClickItem = {
startActivity(
Intent(this, AudioContentDetailActivity::class.java).apply {
@ -75,8 +72,23 @@ class AudioContentCurationActivity : BaseActivity<ActivityAudioContentCurationBi
}
)
binding.rvCuration.layoutManager = GridLayoutManager(this, spanCount)
binding.rvCuration.addItemDecoration(GridSpacingItemDecoration(spanCount, spacing, true))
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.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {

View File

@ -238,16 +238,10 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
}
})
val ivCoverLp = binding.ivCover.layoutParams as RelativeLayout.LayoutParams
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
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
binding.ivPlayLoop.setOnClickListener { viewModel.togglePlayLoop() }
binding.llDonation.setOnClickListener {
val dialog = LiveRoomDonationDialog(
@ -487,7 +481,6 @@ 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,
@ -501,14 +494,6 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
response.orderType == null &&
response.creator.creatorId != SharedPreferenceManager.userId
) {
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
@ -525,26 +510,10 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
}
binding.llPurchase.setOnClickListener {
if (
response.totalContentCount != null &&
response.remainingContentCount != null
) {
showOrderConfirmDialog(
audioContent = response,
isOnlyRental = false,
OrderType.KEEP
)
} else {
showOrderDialog(
audioContent = response,
isOnlyRental = response.isOnlyRental
)
}
}
showOrderDialog(audioContent = response, isOnlyRental = response.isOnlyRental)
}
} else {
binding.llPurchase.visibility = View.GONE
binding.tvPurchaseSoldOut.visibility = View.GONE
}
}
@ -558,8 +527,6 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
.apply(RequestOptions().override((screenWidth - 13.3f.dpToPx()).toInt()))
.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}"
@ -569,16 +536,9 @@ 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 ||
(response.isActivePreview && response.contentUrl.isNotBlank())
(isAlertPreview && response.isActivePreview)
) {
binding.ivPlayOrPause.visibility = View.VISIBLE
binding.ivPlayOrPause.setOnClickListener {
@ -612,7 +572,7 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
R.drawable.btn_audio_content_preview_play
}
)
} else if (response.releaseDate == null) {
} else {
binding.tvPreviewNo.visibility = View.VISIBLE
}
}
@ -697,33 +657,6 @@ 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) {
@ -769,7 +702,7 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
binding.ivFollow.visibility = View.VISIBLE
if (creator.isFollowing) {
binding.ivFollow.setImageResource(R.drawable.btn_following_big)
binding.ivFollow.setImageResource(R.drawable.btn_notification_selected)
binding.ivFollow.setOnClickListener {
viewModel.unRegisterNotification(
contentId = audioContentId,
@ -777,7 +710,7 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
)
}
} else {
binding.ivFollow.setImageResource(R.drawable.btn_follow_big)
binding.ivFollow.setImageResource(R.drawable.btn_notification)
binding.ivFollow.setOnClickListener {
viewModel.registerNotification(
contentId = audioContentId,

View File

@ -15,9 +15,6 @@ 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,

View File

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

View File

@ -53,7 +53,6 @@ import kr.co.vividnext.sodalive.live.room.LiveRoomViewModel
import kr.co.vividnext.sodalive.live.room.create.LiveRoomCreateViewModel
import kr.co.vividnext.sodalive.live.room.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
@ -74,8 +73,7 @@ import kr.co.vividnext.sodalive.mypage.auth.AuthApi
import kr.co.vividnext.sodalive.mypage.auth.AuthRepository
import kr.co.vividnext.sodalive.mypage.can.CanApi
import kr.co.vividnext.sodalive.mypage.can.CanRepository
import kr.co.vividnext.sodalive.mypage.can.charge.iap.CanChargeIapViewModel
import kr.co.vividnext.sodalive.mypage.can.charge.pg.CanChargePgViewModel
import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeViewModel
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
@ -153,7 +151,6 @@ 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) }
@ -179,7 +176,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
viewModel { LiveViewModel(get(), get(), get(), get()) }
viewModel { MyPageViewModel(get(), get()) }
viewModel { CanStatusViewModel(get()) }
viewModel { CanChargePgViewModel(get()) }
viewModel { CanChargeViewModel(get()) }
viewModel { CanPaymentViewModel(get()) }
viewModel { LiveRoomDetailViewModel(get()) }
viewModel { LiveRoomCreateViewModel(get()) }
@ -232,13 +229,12 @@ 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(), get()) }
factory { LiveRepository(get(), get()) }
factory { EventRepository(get()) }
factory { LiveRecommendRepository(get()) }
factory { AuthRepository(get()) }

View File

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

View File

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

View File

@ -32,7 +32,7 @@ class FollowingCreatorAdapter(
binding.ivNotification.visibility = View.VISIBLE
if (item.isFollow) {
binding.ivNotification.setImageResource(R.drawable.btn_following_big)
binding.ivNotification.setImageResource(R.drawable.btn_notification_selected)
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_follow_big)
binding.ivNotification.setImageResource(R.drawable.btn_notification)
binding.ivNotification.setOnClickListener {
val index = items.indexOf(item)
val copyItem = item.copy(isFollow = true)

View File

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

View File

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

View File

@ -1,21 +1,12 @@
package kr.co.vividnext.sodalive.live.now
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 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 coil.transform.RoundedCornersTransformation
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.databinding.ItemLiveNowBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
@ -29,28 +20,15 @@ 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) {
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.ivCover.loadUrl(item.coverImageUrl) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
transformations(RoundedCornersTransformation(4.7f.dpToPx()))
}
binding.ivLock.visibility = if (item.isPrivateRoom) {
View.VISIBLE
} else {
@ -58,18 +36,11 @@ class LiveNowAdapter(
}
if (item.price > 0) {
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)
binding.tvPrice.text = "유료"
binding.tvPrice.setBackgroundResource(R.drawable.bg_round_corner_10_881609)
} else {
binding.tvPrice.text = "무료"
binding.tvPrice.setCompoundDrawables(null, null, null, null)
binding.tvPrice.setBackgroundResource(R.drawable.bg_round_corner_13_3_111111)
binding.tvPrice.setBackgroundResource(R.drawable.bg_round_corner_10_643bc8)
}
if (item.tags.isNotEmpty()) {
@ -92,7 +63,6 @@ class LiveNowAdapter(
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
parent.context,
ItemLiveNowBinding.inflate(
LayoutInflater.from(parent.context),
parent,

View File

@ -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,10 +46,8 @@ 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(itemWidth = (screenWidth - (spacing * (spanCount + 1))) / 3) {
adapter = LiveNowAllAdapter {
val detailFragment = LiveRoomDetailFragment(
it.roomId,
onClickParticipant = { enterLiveRoom(it.roomId) },
@ -65,8 +63,41 @@ class LiveNowAllActivity : BaseActivity<ActivityLiveNowAllBinding>(
)
}
recyclerView.layoutManager = GridLayoutManager(this, spanCount)
recyclerView.addItemDecoration(GridSpacingItemDecoration(spanCount, spacing, false))
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.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
@ -82,8 +113,6 @@ class LiveNowAllActivity : BaseActivity<ActivityLiveNowAllBinding>(
}
})
recyclerView.adapter = adapter
binding.swipeRefreshLayout.setOnRefreshListener {
adapter.clear()
viewModel.page = 1

View File

@ -2,29 +2,20 @@ 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.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import coil.transform.CircleCropTransformation
import coil.load
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.loadUrl
import kr.co.vividnext.sodalive.extensions.moneyFormat
import kr.co.vividnext.sodalive.live.GetRoomListResponse
class LiveNowAllAdapter(
private val itemWidth: Int,
private val onClick: (GetRoomListResponse) -> Unit
) : RecyclerView.Adapter<LiveNowAllAdapter.ViewHolder>() {
@ -36,72 +27,48 @@ class LiveNowAllAdapter(
) : RecyclerView.ViewHolder(binding.root) {
@SuppressLint("SetTextI18n")
fun bind(item: GetRoomListResponse) {
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.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
binding.ivLock.visibility = if (item.isPrivateRoom) {
View.VISIBLE
} else {
View.GONE
}
if (item.price > 0) {
binding.tvPrice.text = "${item.price}"
binding.tvPrice.setCompoundDrawablesWithIntrinsicBounds(
R.drawable.ic_can_white,
0,
0,
0
if (item.numberOfPeople > item.numberOfParticipate) {
binding.tvAvailableParticipate.text = "참여가능"
binding.tvAvailableParticipate.setTextColor(
ContextCompat.getColor(
context,
R.color.color_3bb9f1
)
)
binding.tvPrice.setBackgroundResource(R.drawable.bg_round_corner_13_3_dd4500)
} 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)
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.tvPrice.text = item.price.moneyFormat()
binding.tvPrice.setCompoundDrawablesWithIntrinsicBounds(
0,
0,
R.drawable.ic_can,
0
)
}
binding.root.setOnClickListener { onClick(item) }

View File

@ -26,11 +26,12 @@ 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
@ -114,18 +115,6 @@ 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
@ -465,7 +454,6 @@ 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 {
@ -708,25 +696,21 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
if (response.creatorId == SharedPreferenceManager.userId) {
binding.ivEdit.setOnClickListener {
viewModel.getAllMenuPreset {
roomInfoEditDialog.setRoomInfo(response.title, response.notice)
roomInfoEditDialog.setCoverImageUrl(response.coverImageUrl)
roomInfoEditDialog.setMenuPreset(it)
roomInfoEditDialog.setConfirmAction { newTitle, newContent, newCoverImageUri, isActivateMenu, menuId, menu ->
roomInfoEditDialog.setConfirmAction { newTitle, newContent, newCoverImageUri ->
viewModel.editLiveRoomInfo(
response.roomId,
newTitle,
newContent,
newCoverImageUri,
isActivateMenu,
menuId,
menu,
onSuccess = {
Toast.makeText(
applicationContext,
"라이브 정보가 수정되었습니다.",
Toast.LENGTH_LONG
).show()
binding.tvTitle.text = newTitle
setNoticeAndClickableUrl(binding.tvNotice, newContent)
if (newCoverImageUri != null) {
binding.ivCover.load(newCoverImageUri)
}
agora.sendRawMessageToGroup(
rawMessage = Gson().toJson(
@ -742,11 +726,8 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
)
}
handler.post {
roomInfoEditDialog.show(screenWidth)
}
}
}
binding.ivEdit.visibility = View.VISIBLE
binding.tvQuit.text = "라이브 종료"
@ -800,7 +781,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_following)
binding.ivCreatorFollow.setImageResource(R.drawable.btn_select_checked)
binding.ivCreatorFollow.setOnClickListener {
viewModel.creatorUnFollow(
creatorId = response.creatorId,
@ -808,7 +789,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
)
}
} else {
binding.ivCreatorFollow.setImageResource(R.drawable.btn_follow)
binding.ivCreatorFollow.setImageResource(R.drawable.btn_plus_round)
binding.ivCreatorFollow.setOnClickListener {
viewModel.creatorFollow(
creatorId = response.creatorId,
@ -826,15 +807,6 @@ 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)
}
@ -868,34 +840,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
}
}
viewModel.isShowMenuPan.observe(this) {
if (it) {
binding.tvMenuPan.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_3bb9f1
)
)
binding.tvMenuPan.setBackgroundResource(
R.drawable.bg_round_corner_5_3_transparent_3bb9f1
)
binding.llMenuPan.visibility = View.VISIBLE
} else {
binding.tvMenuPan.setTextColor(
ContextCompat.getColor(
applicationContext,
R.color.color_bbbbbb
)
)
binding.tvMenuPan.setBackgroundResource(
R.drawable.bg_round_corner_5_3_transparent_bbbbbb
)
binding.llMenuPan.visibility = View.GONE
}
}
viewModel.totalDonationCan.observe(this) {
binding.tvTotalCan.text = it.moneyFormat()
}
@ -1082,8 +1026,6 @@ 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
@ -1269,18 +1211,16 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
private fun donation(can: Int, message: String) {
val rawMessage = "${can}캔을 후원하셨습니다.\uD83D\uDCB0\uD83E\uDE99"
viewModel.donation(roomId, can, message) { signatureImage ->
val donationRawMessage = Gson().toJson(
LiveRoomChatRawMessage(
type = LiveRoomChatRawMessageType.DONATION,
message = rawMessage,
can = can,
signatureImageUrl = signatureImage,
donationMessage = message
)
)
viewModel.donation(roomId, can, message) {
agora.sendRawMessageToGroup(
rawMessage = donationRawMessage.toByteArray(),
onSuccess = {
@ -1300,7 +1240,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
)
invalidateChat()
viewModel.addDonationCan(can)
addSignatureImage(signatureImage)
}
},
onFailure = {
@ -1407,9 +1346,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
)
invalidateChat()
viewModel.addDonationCan(rawMessage.can)
addSignatureImage(
imageUrl = rawMessage.signatureImageUrl ?: ""
)
}
}
@ -1686,18 +1622,6 @@ 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)
}
}
}
}
}
}
@ -1751,38 +1675,6 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
}
}
private fun addSignatureImage(imageUrl: String) {
if (imageUrl.isNotBlank()) {
if (!isShowSignatureImage) {
isShowSignatureImage = true
signatureImageUrl = imageUrl
} else {
signatureImageUrlList.add(imageUrl)
}
}
}
private fun showSignatureImage() {
if (signatureImageUrl.isNotBlank()) {
Glide
.with(this)
.load(signatureImageUrl)
.into(binding.ivSignature)
binding.ivSignature.visibility = View.VISIBLE
handler.postDelayed({
if (signatureImageUrlList.isNotEmpty()) {
signatureImageUrl = signatureImageUrlList.removeAt(0)
} else {
signatureImageUrl = ""
isShowSignatureImage = false
binding.ivSignature.setImageDrawable(null)
binding.ivSignature.visibility = View.GONE
}
}, 7000)
}
}
companion object {
private const val noChattingTime = 180L
}

View File

@ -18,7 +18,6 @@ import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.live.LiveRepository
import kr.co.vividnext.sodalive.live.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
@ -55,10 +54,6 @@ 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
@ -360,20 +355,9 @@ 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!!
}
@ -383,9 +367,6 @@ class LiveRoomViewModel(
newTitle: String,
newContent: String,
newCoverImageUri: Uri? = null,
isActivateMenu: Boolean?,
menuId: Long,
menu: String,
onSuccess: () -> Unit
) {
val request = EditLiveRoomInfoRequest(
@ -401,25 +382,17 @@ class LiveRoomViewModel(
},
numberOfPeople = null,
beginDateTimeString = null,
timezone = null,
menuPanId = if (isActivateMenu == true) menuId else 0,
menuPan = if (isActivateMenu == true) menu else "",
isActiveMenuPan = isActivateMenu
timezone = null
)
val requestJson = if (
request.title != null ||
request.notice != null ||
request.isActiveMenuPan != null ||
request.menuPan.isNotBlank()
) {
val requestJson = if (request.title != null || request.notice != null) {
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,
@ -508,7 +481,7 @@ class LiveRoomViewModel(
)
}
fun donation(roomId: Long, can: Int, message: String, onSuccess: (String) -> Unit) {
fun donation(roomId: Long, can: Int, message: String, onSuccess: () -> Unit) {
_isLoading.postValue(true)
compositeDisposable.add(
repository.donation(roomId, can, message, "Bearer ${SharedPreferenceManager.token}")
@ -519,7 +492,7 @@ class LiveRoomViewModel(
_isLoading.value = false
if (it.success) {
SharedPreferenceManager.can -= can
onSuccess(it.data ?: "")
onSuccess()
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
@ -927,40 +900,6 @@ class LiveRoomViewModel(
)
}
fun getAllMenuPreset(onSuccess: (List<GetMenuPresetResponse>) -> Unit) {
_isLoading.value = true
compositeDisposable.add(
repository.getAllMenu(
creatorId = SharedPreferenceManager.userId,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success) {
onSuccess(it.data ?: listOf())
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
_isLoading.value = false
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
private fun randomSelectRouletteItem(
can: Int,
items: List<RouletteItem>,

View File

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

View File

@ -6,7 +6,6 @@ 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
)

View File

@ -14,8 +14,5 @@ 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("menuPanId") val menuPanId: Long = 0,
@SerializedName("menuPan") val menuPan: String = "",
@SerializedName("isActiveMenuPan") val isActiveMenuPan: Boolean = false
@SerializedName("password") val password: String? = null
)

View File

@ -9,7 +9,6 @@ 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
@ -128,8 +127,6 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
viewModel.setTimeNow(
intent.getBooleanExtra(Constants.EXTRA_LIVE_TIME_NOW, true)
)
viewModel.getAllMenuPreset()
}
@SuppressLint("SetTextI18n", "ClickableViewAccessibility")
@ -298,30 +295,6 @@ 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")
@ -375,15 +348,6 @@ class LiveRoomCreateActivity : BaseActivity<ActivityLiveRoomCreateBinding>(
}
)
compositeDisposable.add(
binding.etMenu.textChanges().skip(1)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
viewModel.menu = it.toString()
}
)
viewModel.toastLiveData.observe(this) {
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
}
@ -609,44 +573,6 @@ 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)
}
}
}
@ -687,69 +613,4 @@ 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
)
)
}
}

View File

@ -11,7 +11,6 @@ import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.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
@ -23,10 +22,6 @@ 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
@ -63,18 +58,6 @@ 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 = ""
@ -87,10 +70,6 @@ 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)
@ -136,18 +115,7 @@ 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)
@ -239,11 +207,6 @@ class LiveRoomCreateViewModel(
return false
}
if (_isActivateMenuLiveData.value!! && menu.isBlank()) {
_toastLiveData.postValue("메뉴판은 빈칸일 수 없습니다.")
return false
}
return true
}
@ -289,80 +252,4 @@ class LiveRoomCreateViewModel(
)
)
}
fun selectMenuPreset(selectedMenuPreset: SelectedMenu) {
if (
menuList.isEmpty() &&
(
selectedMenuPreset == SelectedMenu.MENU_2 ||
selectedMenuPreset == SelectedMenu.MENU_3
)
) {
_toastLiveData.value = "메뉴 1을 먼저 설정하세요"
return
}
if (menuList.size == 1 && selectedMenuPreset == SelectedMenu.MENU_3) {
_toastLiveData.value = "메뉴 1과 메뉴 2를 먼저 설정하세요"
return
}
if (_selectedMenuLiveData.value != selectedMenuPreset) {
_selectedMenuLiveData.value = selectedMenuPreset
if (menuList.size > selectedMenuPreset.ordinal) {
val menuPreset = menuList[selectedMenuPreset.ordinal]
_menuLiveData.value = menuPreset.menu
menu = menuPreset.menu
menuId = menuPreset.id
} else {
_menuLiveData.value = ""
menu = ""
menuId = 0
}
}
}
fun toggleIsActivateMenu() {
_isActivateMenuLiveData.value = !_isActivateMenuLiveData.value!!
}
fun getAllMenuPreset() {
_isLoading.value = true
compositeDisposable.add(
repository.getAllMenu(
creatorId = SharedPreferenceManager.userId,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success) {
val data = it.data
menuList.clear()
menuList.addAll(data ?: listOf())
selectMenuPreset(SelectedMenu.MENU_1)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
)
}
}
_isLoading.value = false
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}

View File

@ -21,7 +21,6 @@ data class GetRoomInfoResponse(
@SerializedName("listenerList") val listenerList: List<LiveRoomMember>,
@SerializedName("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

View File

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

View File

@ -1,15 +0,0 @@
package kr.co.vividnext.sodalive.live.room.menu
import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.Query
interface MenuApi {
@GET("/live/room/menu/all")
fun getAllMenu(
@Query("creatorId") creatorId: Long,
@Header("Authorization") authHeader: String
): Single<ApiResponse<List<GetMenuPresetResponse>>>
}

View File

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

View File

@ -1,32 +1,20 @@
package kr.co.vividnext.sodalive.live.room.update
import android.annotation.SuppressLint
import android.app.Activity
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(
private val activity: AppCompatActivity,
activity: Activity,
layoutInflater: LayoutInflater,
onClickImagePicker: () -> Unit
) {
@ -36,17 +24,6 @@ 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)
@ -58,77 +35,6 @@ 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(
@ -136,7 +42,7 @@ class LiveRoomInfoEditDialog(
currentContent: String,
) {
dialogView.etTitle.setText(currentTitle)
dialogView.etNotice.setText(currentContent)
dialogView.etContent.setText(currentContent)
}
fun setCoverImageUri(coverImageUri: Uri) {
@ -157,53 +63,13 @@ class LiveRoomInfoEditDialog(
}
}
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) {
fun setConfirmAction(confirmAction: (String, String, Uri?) -> Unit) {
dialogView.tvConfirm.setOnClickListener {
alertDialog.dismiss()
val newTitle = dialogView.etTitle.text.toString()
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
)
val newContent = dialogView.etContent.text.toString()
confirmAction(newTitle, newContent, coverImageUri)
coverImageUri = null
coverImageUrl = null
}
@ -219,103 +85,4 @@ 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("")
}
}
}
}

View File

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

View File

@ -2,32 +2,18 @@ package kr.co.vividnext.sodalive.live.roulette
import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.live.roulette.config.CreateRouletteRequest
import kr.co.vividnext.sodalive.live.roulette.config.CreateOrUpdateRouletteRequest
import kr.co.vividnext.sodalive.live.roulette.config.RouletteApi
import kr.co.vividnext.sodalive.live.roulette.config.UpdateRouletteRequest
class RouletteRepository(private val api: RouletteApi) {
fun createRoulette(
request: CreateRouletteRequest,
fun createOrUpdateRoulette(
request: CreateOrUpdateRouletteRequest,
token: String
) = api.createRoulette(
) = api.createOrUpdateRoulette(
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

View File

@ -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 CreateRouletteRequest(
data class CreateOrUpdateRouletteRequest(
@SerializedName("can") val can: Int,
@SerializedName("isActive") val isActive: Boolean,
@SerializedName("items") val items: List<RouletteItem>

View File

@ -2,49 +2,35 @@ package kr.co.vividnext.sodalive.live.roulette.config
import io.reactivex.rxjava3.core.Single
import 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("/new-roulette")
fun createRoulette(
@Body request: CreateRouletteRequest,
@POST("/roulette")
fun createOrUpdateRoulette(
@Body request: CreateOrUpdateRouletteRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
@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")
@GET("/roulette")
fun getRoulette(
@Query("creatorId") creatorId: Long,
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetRouletteResponse>>
@POST("/new-roulette/spin")
@POST("/roulette/spin")
fun spinRoulette(
@Body request: SpinRouletteRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetRouletteResponse>>
@POST("/new-roulette/refund/{id}")
@POST("/roulette/refund/{id}")
fun refundRouletteDonation(
@Path("id") id: Long,
@Header("Authorization") authHeader: String

View File

@ -13,9 +13,7 @@ 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
@ -43,7 +41,7 @@ class RouletteSettingsFragment : BaseFragment<FragmentRouletteSettingsBinding>(
setupView()
bindData()
viewModel.getAllRoulette()
viewModel.getRoulette()
}
private fun setupView() {
@ -76,57 +74,15 @@ 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
@ -229,69 +185,4 @@ 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
)
)
}
}

View File

@ -7,7 +7,6 @@ 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
@ -16,10 +15,6 @@ 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
@ -44,16 +39,9 @@ class RouletteSettingsViewModel(private val repository: RouletteRepository) : Ba
val roulettePreviewLiveData: LiveData<RoulettePreview>
get() = _roulettePreviewLiveData
private val _selectedRouletteLiveData = MutableLiveData<SelectedRoulette>()
val selectedRouletteLiveData: LiveData<SelectedRoulette>
get() = _selectedRouletteLiveData
private val options = mutableListOf<RouletteOption>()
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]
@ -125,15 +113,6 @@ class RouletteSettingsViewModel(private val repository: RouletteRepository) : Ba
if (!_isLoading.value!!) {
_isLoading.value = true
if (rouletteId > 0) {
updateRoulette(onSuccess)
} else {
createRoulette(onSuccess)
}
}
}
private fun updateRoulette(onSuccess: (Boolean) -> Unit) {
val items = mutableListOf<RouletteItem>()
for (option in options) {
if (option.title.trim().isEmpty()) {
@ -145,52 +124,14 @@ class RouletteSettingsViewModel(private val repository: RouletteRepository) : Ba
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,
val request = CreateOrUpdateRouletteRequest(
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(
repository.createOrUpdateRoulette(
request = request,
token = "Bearer ${SharedPreferenceManager.token}"
)
@ -200,7 +141,12 @@ class RouletteSettingsViewModel(private val repository: RouletteRepository) : Ba
{
_isLoading.value = false
if (it.success && it.data != null && it.data is Boolean) {
_toastLiveData.postValue(successMessage)
val message = if (it.data) {
"룰렛을 활성화 했습니다."
} else {
"룰렛을 비활성화 했습니다."
}
_toastLiveData.postValue(message)
onSuccess(it.data)
} else {
if (it.message != null) {
@ -220,71 +166,13 @@ class RouletteSettingsViewModel(private val repository: RouletteRepository) : Ba
)
)
}
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() {
fun getRoulette() {
if (!_isLoading.value!!) {
_isLoading.value = true
compositeDisposable.add(
repository.getAllRoulette(
repository.getRoulette(
creatorId = SharedPreferenceManager.userId,
token = "Bearer ${SharedPreferenceManager.token}"
)
@ -294,9 +182,30 @@ class RouletteSettingsViewModel(private val repository: RouletteRepository) : Ba
{
if (it.success) {
val data = it.data
rouletteList.clear()
rouletteList.addAll(data ?: listOf())
selectRoulette(SelectedRoulette.ROULETTE_1)
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)
}
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
@ -318,56 +227,6 @@ class RouletteSettingsViewModel(private val repository: RouletteRepository) : Ba
}
}
fun selectRoulette(selectedRoulette: SelectedRoulette) {
if (
rouletteList.isEmpty() &&
(
selectedRoulette == SelectedRoulette.ROULETTE_2 ||
selectedRoulette == SelectedRoulette.ROULETTE_3
)
) {
_toastLiveData.value = "룰렛 1을 먼저 설정하세요"
return
}
if (rouletteList.size == 1 && selectedRoulette == SelectedRoulette.ROULETTE_3) {
_toastLiveData.value = "룰렛 1과 룰렛 2를 먼저 설정하세요"
return
}
if (_selectedRouletteLiveData.value != selectedRoulette) {
_selectedRouletteLiveData.value = selectedRoulette
if (rouletteList.size > selectedRoulette.ordinal) {
val roulette = rouletteList[selectedRoulette.ordinal]
_canLiveData.value = roulette.can
_isActiveLiveData.value = roulette.isActive
can = roulette.can
rouletteId = roulette.id
isActive = roulette.isActive
val options = roulette.items.asSequence().map { item ->
RouletteOption(title = item.title, weight = item.weight)
}.toList()
removeAllAndAddOptions(options = options)
recalculatePercentages(options)
} else {
_canLiveData.value = 0
_isActiveLiveData.value = false
can = 0
rouletteId = 0
isActive = false
options.clear()
options.add(RouletteOption(title = "", weight = 1))
options.add(RouletteOption(title = "", weight = 1))
recalculatePercentages(options)
}
}
}
private fun removeAllAndAddOptions(options: List<RouletteOption>) {
this.options.clear()
this.options.addAll(options)

View File

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

View File

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

View File

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

View File

@ -1,9 +1,7 @@
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
@ -17,7 +15,6 @@ import kr.co.vividnext.sodalive.settings.event.EventItem
import kr.co.vividnext.sodalive.settings.event.EventRepository
import kr.co.vividnext.sodalive.settings.notification.UpdateNotificationSettingRequest
import kr.co.vividnext.sodalive.user.UserRepository
import java.util.concurrent.Executors
class MainViewModel(
private val userRepository: UserRepository,
@ -156,26 +153,4 @@ class MainViewModel(
)
)
}
fun fetchAndUpdateGaid(context: Context) {
Executors.newSingleThreadExecutor().execute {
try {
val adInfo = AdvertisingIdClient.getAdvertisingIdInfo(context)
adInfo.id?.let { gaid ->
val request = GaidUpdateRequest(adid = gaid)
compositeDisposable.add(
userRepository.updateGaid(
request,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({}, {})
)
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}

View File

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

View File

@ -2,11 +2,10 @@ package kr.co.vividnext.sodalive.mypage.can
import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse
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.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.coupon.UseCanCouponRequest
import kr.co.vividnext.sodalive.mypage.can.status.GetCanStatusResponse
import kr.co.vividnext.sodalive.mypage.can.status.charge.GetCanChargeStatusResponseItem
@ -18,12 +17,6 @@ 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,

View File

@ -1,17 +1,11 @@
package kr.co.vividnext.sodalive.mypage.can
import kr.co.vividnext.sodalive.mypage.can.charge.iap.GoogleChargeRequest
import kr.co.vividnext.sodalive.mypage.can.charge.pg.ChargeRequest
import kr.co.vividnext.sodalive.mypage.can.charge.pg.VerifyRequest
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.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

View File

@ -1,28 +1,33 @@
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 com.google.android.material.tabs.TabLayout
import kr.co.vividnext.sodalive.R
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.ActivityCanChargeBinding
import kr.co.vividnext.sodalive.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.extensions.dpToPx
import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentActivity
import kr.co.vividnext.sodalive.mypage.can.status.CanStatusActivity
import org.koin.android.ext.android.inject
class CanChargeActivity : BaseActivity<ActivityCanChargeBinding>(
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?) {
@ -35,71 +40,87 @@ 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
)
supportFragmentManager.beginTransaction()
.replace(R.id.fl_container, CanChargeIapFragment()).commit()
}
override fun setupView() {
setupToolbar()
setupTabs()
}
private fun setupToolbar() {
binding.toolbar.tvBack.text = "충전하기"
binding.toolbar.tvBack.setOnClickListener { finish() }
}
private fun setupTabs() {
if (SharedPreferenceManager.isAuth) {
val tabs = binding.tabs
tabs.visibility = View.VISIBLE
tabs.addTab(tabs.newTab().setText("인 앱 결제"))
tabs.addTab(tabs.newTab().setText("PG"))
tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {
when (tab.position) {
0 -> supportFragmentManager.beginTransaction()
.replace(R.id.fl_container, CanChargeIapFragment()).commit()
1 -> supportFragmentManager.beginTransaction()
.replace(R.id.fl_container, CanChargePgFragment()).commit()
}
}
override fun onTabUnselected(tab: TabLayout.Tab) {
}
override fun onTabReselected(tab: TabLayout.Tab) {
}
})
} else {
binding.tabs.visibility = View.GONE
}
}
fun selectCan(model: CanResponse) {
val recyclerView = binding.rvChargeCan
adapter = CanChargeAdapter {
val intent = Intent(applicationContext, CanPaymentActivity::class.java)
intent.putExtra(Constants.EXTRA_CAN, model)
intent.putExtra(Constants.EXTRA_CAN, it)
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)
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)
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()
}
finish()
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(this) {
adapter.items.addAll(it)
adapter.notifyDataSetChanged()
}
viewModel.toastLiveData.observe(this) {
it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
}
viewModel.isLoading.observe(this) {
if (it) {
loadingDialog.show(screenWidth)
} else {
loadingDialog.dismiss()
}
}
}
}

View File

@ -1,4 +1,4 @@
package kr.co.vividnext.sodalive.mypage.can.charge.pg
package kr.co.vividnext.sodalive.mypage.can.charge
import android.content.Context
import android.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 CanChargePgAdapter(
class CanChargeAdapter(
private val onClick: (CanResponse) -> Unit
) : RecyclerView.Adapter<CanChargePgAdapter.ViewHolder>() {
) : RecyclerView.Adapter<CanChargeAdapter.ViewHolder>() {
val items = mutableListOf<CanResponse>()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -23,8 +23,8 @@ import kr.co.vividnext.sodalive.databinding.ActivityCanPaymentBinding
import kr.co.vividnext.sodalive.extensions.fontSpan
import kr.co.vividnext.sodalive.extensions.moneyFormat
import kr.co.vividnext.sodalive.mypage.auth.BootpayResponse
import kr.co.vividnext.sodalive.mypage.can.charge.pg.CanResponse
import kr.co.vividnext.sodalive.mypage.can.charge.pg.VerifyRequest
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.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_3bb9f1))
view.setBackgroundResource(R.drawable.bg_round_corner_10_13181b_3bb9f1)
view.setTextColor(ContextCompat.getColor(applicationContext, R.color.color_9970ff))
view.setBackgroundResource(R.drawable.bg_round_corner_10_4d9970ff_9970ff)
}
private fun requestCharge() {
@ -211,13 +211,15 @@ 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 = {

View File

@ -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.pg.ChargeRequest
import kr.co.vividnext.sodalive.mypage.can.charge.pg.VerifyRequest
import kr.co.vividnext.sodalive.mypage.can.charge.ChargeRequest
import kr.co.vividnext.sodalive.mypage.can.charge.VerifyRequest
class CanPaymentViewModel(private val repository: CanRepository) : BaseViewModel() {

View File

@ -31,6 +31,8 @@ class OnBoardingActivity : BaseActivity<ActivityOnboardingBinding>(
adapter.addFragment(OnBoardingFragment(R.drawable.img_guide_2))
adapter.addFragment(OnBoardingFragment(R.drawable.img_guide_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
}

View File

@ -6,7 +6,6 @@ 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
@ -33,9 +32,6 @@ 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()
}

View File

@ -4,7 +4,6 @@ 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
@ -138,10 +137,4 @@ 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>>
}

View File

@ -4,7 +4,6 @@ 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
@ -109,11 +108,4 @@ 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)
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 825 B

After

Width:  |  Height:  |  Size: 824 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

After

Width:  |  Height:  |  Size: 775 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 560 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 320 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 652 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 699 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

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

View File

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

View File

@ -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_14262d" />
<corners android:radius="5.3dp" />
<solid android:color="@color/color_643bc8" />
<corners android:radius="10dp" />
<stroke
android:width="1dp"
android:color="@color/color_14262d" />
android:color="@color/color_643bc8" />
</shape>

View File

@ -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_dd4500" />
<corners android:radius="13.3dp" />
<solid android:color="@color/color_881609" />
<corners android:radius="10dp" />
<stroke
android:width="1dp"
android:color="@color/color_dd4500" />
android:color="@color/color_881609" />
</shape>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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_777777" />
<solid android:color="@color/color_4d9970ff" />
<corners android:radius="6.7dp" />
<stroke
android:width="1dp"
android:color="@color/color_777777" />
android:width="1.3dp"
android:color="@color/color_9970ff" />
</shape>

View File

@ -75,7 +75,7 @@
android:layout_alignParentEnd="true"
android:contentDescription="@null"
android:visibility="gone"
tools:src="@drawable/btn_following_big" />
tools:src="@drawable/btn_notification_selected" />
</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:visibility="gone" />
android:textSize="16.7sp" />
<SeekBar
android:id="@+id/sb_progress"
@ -140,24 +140,6 @@
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
@ -382,75 +364,6 @@
</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"
@ -509,21 +422,6 @@
</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"
@ -681,7 +579,6 @@
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"

View File

@ -1,29 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
xmlns:app="http://schemas.android.com/apk/res-auto">
<include
android:id="@+id/toolbar"
layout="@layout/detail_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.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" />
<FrameLayout
android:id="@+id/fl_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

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