콘텐츠 상세, 콘텐츠 구매

- pg 테스트 계정의 경우 캔이 아닌 원으로 표시되도록 하고 콘텐츠 구매시 바로 결제 후 구매 되도록 수정
This commit is contained in:
klaus 2024-05-20 15:37:46 +09:00
parent ba4d707d45
commit b220b8bce4
13 changed files with 523 additions and 35 deletions

View File

@ -89,6 +89,7 @@
android:name=".mypage.can.charge.CanChargeActivity"
android:configChanges="orientation|screenSize|keyboardHidden" />
<activity android:name=".mypage.can.payment.CanPaymentActivity" />
<activity android:name=".mypage.can.payment.CanPaymentTempActivity" />
<activity android:name=".mypage.can.coupon.CanCouponActivity" />
<activity android:name=".live.room.create.LiveRoomCreateActivity" />
<activity android:name=".live.room.update.LiveRoomEditActivity" />

View File

@ -1,6 +1,7 @@
package kr.co.vividnext.sodalive.audio_content.detail
import android.annotation.SuppressLint
import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
@ -13,6 +14,8 @@ import android.view.View
import android.widget.RelativeLayout
import android.widget.SeekBar
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
@ -37,13 +40,16 @@ import kr.co.vividnext.sodalive.common.Utils
import kr.co.vividnext.sodalive.databinding.ActivityAudioContentDetailBinding
import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.moneyFormat
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationDialog
import kr.co.vividnext.sodalive.mypage.auth.Auth
import kr.co.vividnext.sodalive.mypage.auth.AuthVerifyRequest
import kr.co.vividnext.sodalive.mypage.auth.BootpayResponse
import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeActivity
import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentTempActivity
import kr.co.vividnext.sodalive.report.ReportType
import org.koin.android.ext.android.inject
import kotlin.math.ceil
class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBinding>(
ActivityAudioContentDetailBinding::inflate
@ -63,6 +69,10 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
private var refresh = false
private var title = ""
private lateinit var activityResultLauncher: ActivityResultLauncher<Intent>
private lateinit var audioContent: GetAudioContentDetailResponse
private lateinit var orderType: OrderType
@SuppressLint("SetTextI18n")
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
@ -87,6 +97,14 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
finish()
}
activityResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
if (it.resultCode == Activity.RESULT_OK) {
contentOrder(audioContent, orderType)
}
}
bindData()
viewModel.getAudioContentDetail(audioContentId = audioContentId) { finish() }
}
@ -516,12 +534,29 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
binding.tvReleaseDate.visibility = View.GONE
binding.llPurchase.visibility = View.VISIBLE
binding.llPurchasePrice.visibility = View.VISIBLE
binding.tvPrice.text = response.price.toString()
binding.llPurchase.background = ContextCompat.getDrawable(
applicationContext,
R.drawable.bg_round_corner_5_3_3bb9f1
)
binding.ivCan.visibility = if (SharedPreferenceManager.userId == 17958L) {
View.GONE
} else {
View.VISIBLE
}
binding.tvPrice.text = if (SharedPreferenceManager.userId == 17958L) {
(response.price * 110).moneyFormat()
} else {
response.price.moneyFormat()
}
binding.tvUnit.text = if (SharedPreferenceManager.userId == 17958L) {
"원으로"
} else {
"캔으로"
}
binding.tvStrPurchaseOrRental.text = if (response.isOnlyRental) {
" 대여하기"
} else {
@ -847,18 +882,43 @@ class AudioContentDetailActivity : BaseActivity<ActivityAudioContentDetailBindin
binding.rlPreviewAlert.visibility = View.GONE
viewModel.order(
contentId = audioContent.contentId,
orderType = orderType
) {
val intent = Intent(applicationContext, CanChargeActivity::class.java)
intent.putExtra(Constants.EXTRA_GO_TO_PREV_PAGE, true)
startActivity(intent)
if (SharedPreferenceManager.userId == 17958L) {
this@AudioContentDetailActivity.audioContent = audioContent
this@AudioContentDetailActivity.orderType = orderType
activityResultLauncher.launch(
Intent(applicationContext, CanPaymentTempActivity::class.java).apply {
putExtra("title", audioContent.title)
putExtra(
"can",
if (orderType == OrderType.RENTAL) {
ceil(audioContent.price * 0.6).toInt()
} else {
audioContent.price
}
)
}
)
} else {
contentOrder(audioContent, orderType)
}
},
).show(screenWidth)
}
private fun contentOrder(
audioContent: GetAudioContentDetailResponse,
orderType: OrderType
) {
viewModel.order(
contentId = audioContent.contentId,
orderType = orderType
) {
val intent = Intent(applicationContext, CanChargeActivity::class.java)
intent.putExtra(Constants.EXTRA_GO_TO_PREV_PAGE, true)
startActivity(intent)
}
}
inner class AudioContentReceiver : BroadcastReceiver() {
@SuppressLint("SetTextI18n")
override fun onReceive(context: Context?, intent: Intent?) {

View File

@ -4,14 +4,17 @@ import android.app.Activity
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
import androidx.appcompat.app.AlertDialog
import coil.load
import coil.transform.CircleCropTransformation
import coil.transform.RoundedCornersTransformation
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.DialogAudioContentOrderConfirmBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.moneyFormat
import kotlin.math.ceil
class AudioContentOrderConfirmDialog(
@ -58,16 +61,35 @@ class AudioContentOrderConfirmDialog(
}
dialogView.tvDuration.text = duration
dialogView.tvPrice.text = if (orderType == OrderType.RENTAL && !isOnlyRental) {
"${ceil(price * 0.6).toInt()}"
if (SharedPreferenceManager.userId == 17958L) {
dialogView.ivCan.visibility = View.GONE
dialogView.tvPrice.text = if (orderType == OrderType.RENTAL && !isOnlyRental) {
"${(ceil(price * 0.6).toInt() * 110).moneyFormat()}"
} else {
"${(price * 110).moneyFormat()}"
}
} else {
"$price"
dialogView.ivCan.visibility = View.VISIBLE
dialogView.tvPrice.text = if (orderType == OrderType.RENTAL && !isOnlyRental) {
ceil(price * 0.6).toInt().moneyFormat()
} else {
price.moneyFormat()
}
}
dialogView.tvNotice.text = if (orderType == OrderType.RENTAL) {
"콘텐츠를 대여하시겠습니까?\n아래 캔이 차감됩니다."
if (SharedPreferenceManager.userId == 17958L) {
dialogView.tvNotice.text = if (orderType == OrderType.RENTAL) {
"콘텐츠를 대여하시겠습니까?"
} else {
"콘텐츠를 소장하시겠습니까?"
}
} else {
"콘텐츠를 소장하시겠습니까?\n아래 캔이 차감됩니다."
dialogView.tvNotice.text = if (orderType == OrderType.RENTAL) {
"콘텐츠를 대여하시겠습니까?\n아래 캔이 차감됩니다."
} else {
"콘텐츠를 소장하시겠습니까?\n아래 캔이 차감됩니다."
}
}
dialogView.tvCancel.setOnClickListener {

View File

@ -1,11 +1,14 @@
package kr.co.vividnext.sodalive.audio_content.order
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.FragmentAudioContentOrderBinding
import kr.co.vividnext.sodalive.extensions.moneyFormat
import kotlin.math.ceil
class AudioContentOrderFragment(
@ -26,15 +29,33 @@ class AudioContentOrderFragment(
return binding.root
}
@SuppressLint("SetTextI18n")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (SharedPreferenceManager.userId == 17958L) {
binding.ivKeepCan.visibility = View.GONE
binding.ivRentalCan.visibility = View.GONE
} else {
binding.ivKeepCan.visibility = View.VISIBLE
binding.ivRentalCan.visibility = View.VISIBLE
}
if (isOnlyRental) {
binding.tvRental.text = "$price"
if (SharedPreferenceManager.userId == 17958L) {
binding.tvRental.text = "${(price * 110).moneyFormat()}"
} else {
binding.tvRental.text = price.moneyFormat()
}
binding.rlKeep.visibility = View.GONE
} else {
binding.tvKeep.text = "$price"
binding.tvRental.text = "${ceil(price * 0.6).toInt()}"
if (SharedPreferenceManager.userId == 17958L) {
binding.tvKeep.text = "${(price * 110).moneyFormat()}"
binding.tvRental.text = "${(ceil(price * 0.6).toInt() * 110).moneyFormat()}"
} else {
binding.tvKeep.text = price.moneyFormat()
binding.tvRental.text = ceil(price * 0.6).toInt().moneyFormat()
}
binding.rlKeep.visibility = View.VISIBLE
binding.llKeep.setOnClickListener {

View File

@ -82,7 +82,10 @@ 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.coupon.CanCouponViewModel
import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentTempRepository
import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentTempViewModel
import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentViewModel
import kr.co.vividnext.sodalive.mypage.can.payment.CanTempApi
import kr.co.vividnext.sodalive.mypage.can.status.CanStatusViewModel
import kr.co.vividnext.sodalive.mypage.profile.ProfileUpdateViewModel
import kr.co.vividnext.sodalive.mypage.profile.nickname.NicknameUpdateViewModel
@ -160,6 +163,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
}
single { ApiBuilder().build(get(), CanApi::class.java) }
single { ApiBuilder().build(get(), CanTempApi::class.java) }
single { ApiBuilder().build(get(), AuthApi::class.java) }
single { ApiBuilder().build(get(), UserApi::class.java) }
single { ApiBuilder().build(get(), MenuApi::class.java) }
@ -191,6 +195,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
viewModel { CanStatusViewModel(get()) }
viewModel { CanChargePgViewModel(get()) }
viewModel { CanPaymentViewModel(get()) }
viewModel { CanPaymentTempViewModel(get()) }
viewModel { LiveRoomDetailViewModel(get()) }
viewModel { LiveRoomCreateViewModel(get()) }
viewModel { LiveTagViewModel(get()) }
@ -257,6 +262,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
factory { LiveRecommendRepository(get()) }
factory { AuthRepository(get()) }
factory { CanRepository(get()) }
factory { CanPaymentTempRepository(get()) }
factory { LiveTagRepository(get()) }
factory { ReportRepository(get()) }
factory { ExplorerRepository(get()) }

View File

@ -0,0 +1,237 @@
package kr.co.vividnext.sodalive.mypage.can.payment
import android.annotation.SuppressLint
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.View
import android.widget.TextView
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import com.google.gson.Gson
import com.orhanobut.logger.Logger
import kr.co.bootpay.android.Bootpay
import kr.co.bootpay.android.events.BootpayEventListener
import kr.co.bootpay.android.models.BootUser
import kr.co.bootpay.android.models.Payload
import kr.co.vividnext.sodalive.BuildConfig
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
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.VerifyRequest
import org.koin.android.ext.android.inject
class CanPaymentTempActivity : BaseActivity<ActivityCanPaymentBinding>(
ActivityCanPaymentBinding::inflate
) {
enum class PaymentMethod(val method: String) {
CARD("카드"), BANK("계좌이체"), PHONE("휴대폰")
}
private val viewModel: CanPaymentTempViewModel by inject()
private val handler = Handler(Looper.getMainLooper())
private lateinit var loadingDialog: LoadingDialog
private lateinit var title: String
private var can: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
bindData()
}
@SuppressLint("SetTextI18n")
override fun setupView() {
title = intent.getStringExtra("title") ?: ""
can = intent.getIntExtra("can", 0)
loadingDialog = LoadingDialog(this, layoutInflater)
if (title.isBlank() || can <= 0) {
Toast.makeText(
applicationContext,
"다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다.",
Toast.LENGTH_LONG
).show()
finish()
}
binding.toolbar.tvBack.text = "결제하기"
binding.toolbar.tvBack.setOnClickListener { finish() }
binding.ivCan.visibility = View.GONE
binding.tvAlert.visibility = View.GONE
binding.tvChargeCanTitle.text = title
binding.tvPrice.text = (can * 110).moneyFormat()
binding.tvPaymentPrice.text = "${(can * 110).moneyFormat()}".fontSpan(
ResourcesCompat.getFont(applicationContext, R.font.gmarket_sans_light),
""
)
binding.tvAgree.setOnClickListener {
binding.tvAgree.isSelected = !binding.tvAgree.isSelected
}
binding.tvPayment.setOnClickListener {
if (viewModel.paymentMethodLiveData.value == null) {
Toast.makeText(
applicationContext,
"결제수단을 선택해 주세요.",
Toast.LENGTH_LONG
).show()
return@setOnClickListener
}
if (!binding.tvAgree.isSelected) {
Toast.makeText(
applicationContext,
"결제 진행에 동의하셔야 결제가 가능합니다.",
Toast.LENGTH_LONG
).show()
return@setOnClickListener
}
requestCharge()
}
binding.tvMethodCard.setOnClickListener { viewModel.setPaymentMethod(PaymentMethod.CARD) }
binding.tvMethodBank.setOnClickListener { viewModel.setPaymentMethod(PaymentMethod.BANK) }
binding.tvMethodPhone.setOnClickListener { viewModel.setPaymentMethod(PaymentMethod.PHONE) }
}
private fun allPaymentMethodSelectFalse() {
paymentMethodSelectFalse(binding.tvMethodBank)
paymentMethodSelectFalse(binding.tvMethodCard)
paymentMethodSelectFalse(binding.tvMethodPhone)
}
private fun paymentMethodSelectFalse(view: TextView) {
view.typeface = ResourcesCompat.getFont(
applicationContext,
R.font.gmarket_sans_medium
)
view.setTextColor(ContextCompat.getColor(applicationContext, R.color.color_eeeeee))
view.setBackgroundResource(R.drawable.bg_round_corner_10_232323_777777)
}
private fun paymentMethodSelect(view: TextView) {
view.typeface = ResourcesCompat.getFont(
applicationContext,
R.font.gmarket_sans_bold
)
view.setTextColor(ContextCompat.getColor(applicationContext, R.color.color_3bb9f1))
view.setBackgroundResource(R.drawable.bg_round_corner_10_13181b_3bb9f1)
}
private fun requestCharge() {
viewModel.chargeCan(
can = can,
paymentGateway = PaymentGateway.PG,
onSuccess = {
requestPayment(chargeId = it)
},
onFailure = {
Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show()
}
)
}
private fun requestPayment(chargeId: Long) {
val user = BootUser()
.setId("${SharedPreferenceManager.userId}")
.setUsername(SharedPreferenceManager.nickname)
val payload = Payload()
.setApplicationId(BuildConfig.BOOTPAY_APP_ID)
.setOrderId("$chargeId")
.setOrderName(title)
.setPrice((can * 110).toDouble())
.setTaxFree(0.toDouble())
.setPg("세틀뱅크")
.setMethod(viewModel.paymentMethodLiveData.value!!.method)
.setUser(user)
Bootpay.init(this, this)
.setPayload(payload)
.setEventListener(object : BootpayEventListener {
override fun onCancel(data: String) {
Logger.e("onCancel: $data")
}
override fun onError(data: String) {
Logger.e("onError: $data")
Toast.makeText(applicationContext, data, Toast.LENGTH_LONG).show()
}
override fun onClose() {
Logger.e("onClose")
Bootpay.removePaymentWindow()
}
override fun onIssued(data: String) {
Logger.e("onIssued: $data")
}
override fun onConfirm(data: String): Boolean {
Logger.e("onConfirm: $data")
return true
}
override fun onDone(data: String) {
Logger.e("onDone: $data")
handler.post {
verifyPayment(data)
Bootpay.removePaymentWindow()
}
}
}).requestPayment()
}
private fun verifyPayment(data: String) {
val bootpayResponse = Gson().fromJson(data, BootpayResponse::class.java)
val request = VerifyRequest(bootpayResponse.data.receiptId, bootpayResponse.data.orderId)
viewModel.verify(
request,
onSuccess = {
SharedPreferenceManager.can += can
setResult(RESULT_OK)
finish()
},
onFailure = {
Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show()
}
)
}
private fun bindData() {
viewModel.paymentMethodLiveData.observe(this) {
allPaymentMethodSelectFalse()
if (it != null) {
when (it) {
PaymentMethod.CARD -> paymentMethodSelect(binding.tvMethodCard)
PaymentMethod.BANK -> paymentMethodSelect(binding.tvMethodBank)
PaymentMethod.PHONE -> paymentMethodSelect(binding.tvMethodPhone)
}
}
}
viewModel.isLoading.observe(this) {
if (it) {
loadingDialog.show(screenWidth, "")
} else {
loadingDialog.dismiss()
}
}
}
}

View File

@ -0,0 +1,15 @@
package kr.co.vividnext.sodalive.mypage.can.payment
import kr.co.vividnext.sodalive.mypage.can.charge.pg.VerifyRequest
class CanPaymentTempRepository(private val api: CanTempApi) {
fun chargeCan(
request: ChargeTempRequest,
token: String
) = api.chargeCan(request, authHeader = token)
fun verify(
request: VerifyRequest,
token: String
) = api.verifyCharge(request, authHeader = token)
}

View File

@ -0,0 +1,88 @@
package kr.co.vividnext.sodalive.mypage.can.payment
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.mypage.can.charge.pg.VerifyRequest
class CanPaymentTempViewModel(private val repository: CanPaymentTempRepository) : BaseViewModel() {
private val _paymentMethodLiveData = MutableLiveData<CanPaymentTempActivity.PaymentMethod?>()
val paymentMethodLiveData: LiveData<CanPaymentTempActivity.PaymentMethod?>
get() = _paymentMethodLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
fun chargeCan(
can: Int,
paymentGateway: PaymentGateway,
onSuccess: (Long) -> Unit,
onFailure: (String) -> Unit
) {
_isLoading.value = true
val request = ChargeTempRequest(can, can * 110, paymentGateway)
compositeDisposable.add(
repository.chargeCan(request, "Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success && it.data != null) {
onSuccess(it.data.chargeId)
} else {
if (it.message != null) {
onFailure(it.message)
} else {
onFailure("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
onFailure("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun verify(request: VerifyRequest, onSuccess: () -> Unit, onFailure: (String) -> Unit) {
_isLoading.value = true
compositeDisposable.add(
repository.verify(
request = request,
"Bearer ${SharedPreferenceManager.token}"
).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success) {
onSuccess()
} else {
if (it.message != null) {
onFailure(it.message)
} else {
onFailure("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
}
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
onFailure("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
fun setPaymentMethod(paymentMethod: CanPaymentTempActivity.PaymentMethod) {
_paymentMethodLiveData.value = paymentMethod
}
}

View File

@ -0,0 +1,30 @@
package kr.co.vividnext.sodalive.mypage.can.payment
import com.google.gson.annotations.SerializedName
import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.mypage.can.charge.pg.ChargeResponse
import kr.co.vividnext.sodalive.mypage.can.charge.pg.VerifyRequest
import retrofit2.http.Body
import retrofit2.http.Header
import retrofit2.http.POST
interface CanTempApi {
@POST("/charge/temp")
fun chargeCan(
@Body chargeRequest: ChargeTempRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<ChargeResponse>>
@POST("/charge/temp/verify")
fun verifyCharge(
@Body request: VerifyRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
}
data class ChargeTempRequest(
@SerializedName("can") val can: Int,
@SerializedName("price") val price: Int,
@SerializedName("paymentGateway") val paymentGateway: PaymentGateway
)

View File

@ -546,6 +546,7 @@
android:visibility="gone">
<ImageView
android:id="@+id/iv_can"
android:layout_width="16.7dp"
android:layout_height="16.7dp"
android:contentDescription="@null"

View File

@ -33,29 +33,32 @@
android:paddingHorizontal="13.3dp"
android:paddingVertical="23.3dp">
<ImageView
android:id="@+id/ic_can"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_can"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/tv_charge_can_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:textColor="@color/color_eeeeee"
android:textSize="15.3sp"
app:layout_constraintBottom_toBottomOf="@+id/ic_can"
app:layout_constraintStart_toEndOf="@+id/ic_can"
app:layout_constraintTop_toTopOf="@+id/ic_can"
tools:text="5000 캔 + 1000 캔" />
<ImageView
android:id="@+id/iv_can"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="13.3dp"
android:contentDescription="@null"
android:src="@drawable/ic_can" />
<TextView
android:id="@+id/tv_charge_can_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:textColor="@color/color_eeeeee"
android:textSize="15.3sp"
tools:text="5000 캔 + 1000 캔" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_price"
@ -171,6 +174,7 @@
app:drawableStartCompat="@drawable/ic_select" />
<TextView
android:id="@+id/tv_alert"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="26.7dp"

View File

@ -130,6 +130,7 @@
android:paddingVertical="13.3dp">
<ImageView
android:id="@+id/iv_can"
android:layout_width="16.7dp"
android:layout_height="16.7dp"
android:layout_marginEnd="2.7dp"

View File

@ -46,6 +46,7 @@
tools:ignore="RelativeOverlap">
<ImageView
android:id="@+id/iv_rental_can"
android:layout_width="16.7dp"
android:layout_height="16.7dp"
android:layout_marginEnd="2.7dp"
@ -105,6 +106,7 @@
tools:ignore="RelativeOverlap">
<ImageView
android:id="@+id/iv_keep_can"
android:layout_width="16.7dp"
android:layout_height="16.7dp"
android:layout_marginEnd="2.7dp"