코인 충전, 코인 내역

This commit is contained in:
klaus 2023-07-30 16:20:58 +09:00
parent 7fb43b3f91
commit 79127801c6
34 changed files with 1086 additions and 63 deletions

View File

@ -34,6 +34,8 @@
<activity android:name=".settings.terms.TermsActivity" />
<activity android:name=".user.find_password.FindPasswordActivity" />
<activity android:name=".mypage.can.status.CanStatusActivity" />
<activity android:name=".mypage.can.charge.CanChargeActivity" />
<activity android:name=".mypage.can.payment.CanPaymentActivity" />
<activity
android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity"

View File

@ -12,11 +12,13 @@ object Constants {
const val PREF_PROFILE_IMAGE = "pref_profile_image"
const val PREF_IS_FOLLOWED_CREATOR_LIVE = "pref_is_followed_creator_live"
const val EXTRA_CAN = "extra_can"
const val EXTRA_DATA = "extra_data"
const val EXTRA_TERMS = "extra_terms"
const val EXTRA_ROOM_ID = "extra_room_id"
const val EXTRA_USER_ID = "extra_user_id"
const val EXTRA_MESSAGE_ID = "extra_message_id"
const val EXTRA_PREV_LIVE_ROOM = "extra_prev_live_room"
const val EXTRA_CONTENT_ID = "extra_content_id"
}

View File

@ -0,0 +1,15 @@
package kr.co.vividnext.sodalive.common
import android.graphics.Typeface
import android.text.TextPaint
import android.text.style.MetricAffectingSpan
class CustomTypefaceSpan(private val typeface: Typeface?) : MetricAffectingSpan() {
override fun updateDrawState(tp: TextPaint) {
tp.typeface = typeface
}
override fun updateMeasureState(textPaint: TextPaint) {
textPaint.typeface = typeface
}
}

View File

@ -15,6 +15,8 @@ import kr.co.vividnext.sodalive.mypage.auth.AuthApi
import kr.co.vividnext.sodalive.mypage.auth.AuthRepository
import kr.co.vividnext.sodalive.mypage.can.CanApi
import kr.co.vividnext.sodalive.mypage.can.CanRepository
import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeViewModel
import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentViewModel
import kr.co.vividnext.sodalive.mypage.can.status.CanStatusViewModel
import kr.co.vividnext.sodalive.network.TokenAuthenticator
import kr.co.vividnext.sodalive.settings.event.EventApi
@ -87,6 +89,8 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
viewModel { LiveViewModel(get(), get(), get()) }
viewModel { MyPageViewModel(get(), get()) }
viewModel { CanStatusViewModel(get()) }
viewModel { CanChargeViewModel(get()) }
viewModel { CanPaymentViewModel(get()) }
}
private val repositoryModule = module {

View File

@ -0,0 +1,48 @@
package kr.co.vividnext.sodalive.extensions
import android.graphics.Typeface
import android.text.SpannableString
import kr.co.vividnext.sodalive.common.CustomTypefaceSpan
import java.text.SimpleDateFormat
import java.util.Locale
fun String.convertDateFormat(
from: String,
to: String,
inputLocale: Locale = Locale.KOREAN,
outputLocale: Locale = Locale.KOREAN
): String {
val fromDateFormat = SimpleDateFormat(from, inputLocale)
val toDateFormat = SimpleDateFormat(to, outputLocale)
var outputDateString = ""
val date = fromDateFormat.parse(this)
if (date != null) {
outputDateString = toDateFormat.format(date)
}
return outputDateString
}
fun String.fontSpan(typeface: Typeface?, text: String): SpannableString {
val typefaceSpan = CustomTypefaceSpan(typeface)
val spannableString = SpannableString(this)
var startIndex = this.indexOf(text)
while (startIndex != -1) {
val endIndex = startIndex + text.length
spannableString.setSpan(
typefaceSpan,
startIndex,
endIndex,
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE
)
startIndex = this.indexOf(text, endIndex)
}
return spannableString
}

View File

@ -19,6 +19,7 @@ import kr.co.vividnext.sodalive.extensions.moneyFormat
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.status.CanStatusActivity
import kr.co.vividnext.sodalive.settings.notification.MemberRole
import org.koin.android.ext.android.inject
@ -47,7 +48,7 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
binding.ivEdit.setOnClickListener {}
binding.llTotalCoin.setOnClickListener {
binding.llTotalCan.setOnClickListener {
startActivity(
Intent(
requireActivity(),
@ -56,7 +57,14 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
)
}
binding.tvChargeCoin.setOnClickListener {}
binding.tvChargeCan.setOnClickListener {
startActivity(
Intent(
requireActivity(),
CanChargeActivity::class.java
)
)
}
binding.llReservationSuda.setOnClickListener {}
@ -144,8 +152,8 @@ class MyPageFragment : BaseFragment<FragmentMyBinding>(FragmentMyBinding::inflat
}
}
binding.tvTotalCoin.text = (it.chargeCoin + it.rewardCoin).moneyFormat()
binding.tvReservationSuda.text = "${it.sudaReservationCount}"
binding.tvTotalCan.text = (it.chargeCan + it.rewardCan).moneyFormat()
binding.tvReservationSuda.text = "${it.liveReservationCount}"
}
}
}

View File

@ -5,15 +5,13 @@ import com.google.gson.annotations.SerializedName
data class MyPageResponse(
@SerializedName("nickname") val nickname: String,
@SerializedName("profileUrl") val profileUrl: String,
@SerializedName("chargeCoin") val chargeCoin: Int,
@SerializedName("rewardCoin") val rewardCoin: Int,
@SerializedName("chargeCan") val chargeCan: Int,
@SerializedName("rewardCan") val rewardCan: Int,
@SerializedName("youtubeUrl") val youtubeUrl: String?,
@SerializedName("instagramUrl") val instagramUrl: String?,
@SerializedName("websiteUrl") val websiteUrl: String?,
@SerializedName("blogUrl") val blogUrl: String?,
@SerializedName("sudaReservationCount") val sudaReservationCount: Int,
@SerializedName("counselingReservationCount") val counselingReservationCount: Int,
@SerializedName("liveReservationCount") val liveReservationCount: Int,
@SerializedName("likeCount") val likeCount: Int,
@SerializedName("reviewCount") val reviewCount: Int,
@SerializedName("isAuth") val isAuth: Boolean,
)

View File

@ -2,14 +2,37 @@ package kr.co.vividnext.sodalive.mypage.can
import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.mypage.can.charge.CanResponse
import kr.co.vividnext.sodalive.mypage.can.charge.ChargeRequest
import kr.co.vividnext.sodalive.mypage.can.charge.ChargeResponse
import kr.co.vividnext.sodalive.mypage.can.charge.VerifyRequest
import kr.co.vividnext.sodalive.mypage.can.status.GetCanStatusResponse
import kr.co.vividnext.sodalive.mypage.can.status.charge.GetCanChargeStatusResponseItem
import kr.co.vividnext.sodalive.mypage.can.status.use.GetCanUseStatusResponseItem
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.POST
import retrofit2.http.Query
interface CanApi {
@POST("/charge")
fun chargeCan(
@Body chargeRequest: ChargeRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<ChargeResponse>>
@POST("/charge/verify")
fun verifyCharge(
@Body request: VerifyRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
@GET("/can")
fun getCans(
@Header("Authorization") authHeader: String
): Single<ApiResponse<List<CanResponse>>>
@GET("/can/status")
fun getCanStatus(
@Query("container") container: String = "aos",

View File

@ -1,8 +1,22 @@
package kr.co.vividnext.sodalive.mypage.can
import kr.co.vividnext.sodalive.mypage.can.charge.ChargeRequest
import kr.co.vividnext.sodalive.mypage.can.charge.VerifyRequest
import java.util.TimeZone
class CanRepository(private val api: CanApi) {
fun chargeCan(
chargeRequest: ChargeRequest,
token: String
) = api.chargeCan(chargeRequest, authHeader = token)
fun verify(
request: VerifyRequest,
token: String
) = api.verifyCharge(request, authHeader = token)
fun getCans(token: String) = api.getCans(authHeader = token)
fun getCanStatus(token: String) = api.getCanStatus(authHeader = token)
fun getCanUseStatus(

View File

@ -0,0 +1,126 @@
package kr.co.vividnext.sodalive.mypage.can.charge
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.graphics.Rect
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.ActivityCanChargeBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.mypage.can.payment.CanPaymentActivity
import org.koin.android.ext.android.inject
class CanChargeActivity : BaseActivity<ActivityCanChargeBinding>(
ActivityCanChargeBinding::inflate
) {
private val viewModel: CanChargeViewModel by inject()
private var prevLiveRoom: Boolean = false
private lateinit var adapter: CanChargeAdapter
private lateinit var loadingDialog: LoadingDialog
private lateinit var activityResultLauncher: ActivityResultLauncher<Intent>
override fun onCreate(savedInstanceState: Bundle?) {
activityResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
if (it.resultCode == Activity.RESULT_OK) {
finish()
}
}
super.onCreate(savedInstanceState)
bindData()
viewModel.getCanCharges()
}
override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater)
binding.toolbar.tvBack.text = "충전하기"
binding.toolbar.tvBack.setOnClickListener { finish() }
prevLiveRoom = intent.getBooleanExtra(
Constants.EXTRA_PREV_LIVE_ROOM,
false
)
val recyclerView = binding.rvChargeCan
adapter = CanChargeAdapter {
val intent = Intent(applicationContext, CanPaymentActivity::class.java)
intent.putExtra(Constants.EXTRA_CAN, it)
intent.putExtra(Constants.EXTRA_PREV_LIVE_ROOM, prevLiveRoom)
activityResultLauncher.launch(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()
}
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

@ -0,0 +1,52 @@
package kr.co.vividnext.sodalive.mypage.can.charge
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.content.res.ResourcesCompat
import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.databinding.ItemCanChargeBinding
import kr.co.vividnext.sodalive.extensions.fontSpan
import kr.co.vividnext.sodalive.extensions.moneyFormat
class CanChargeAdapter(
private val onClick: (CanResponse) -> Unit
) : RecyclerView.Adapter<CanChargeAdapter.ViewHolder>() {
val items = mutableListOf<CanResponse>()
inner class ViewHolder(
private val context: Context,
private val binding: ItemCanChargeBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(canCharge: CanResponse) {
binding.tvPrice.text = canCharge.price.moneyFormat()
val typeface = ResourcesCompat.getFont(context, R.font.gmarket_sans_medium)
binding.tvTitle.text = canCharge.title.fontSpan(
typeface,
""
)
binding.root.setOnClickListener { onClick(canCharge) }
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return 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()
}

View File

@ -0,0 +1,54 @@
package kr.co.vividnext.sodalive.mypage.can.charge
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.CanRepository
class CanChargeViewModel(private val repository: CanRepository) : BaseViewModel() {
private val _canChargesLiveData = MutableLiveData<List<CanResponse>>()
val canChargeLiveData: LiveData<List<CanResponse>>
get() = _canChargesLiveData
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
fun getCanCharges() {
_isLoading.value = true
compositeDisposable.add(
repository.getCans("Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
_isLoading.value = false
if (it.success && it.data != null) {
_canChargesLiveData.postValue(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("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}

View File

@ -0,0 +1,19 @@
package kr.co.vividnext.sodalive.mypage.can.charge
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class CanResponse(
@SerializedName("id")
val id: Long,
@SerializedName("title")
val title: String,
@SerializedName("can")
val can: Int,
@SerializedName("rewardCan")
val rewardCan: Int,
@SerializedName("price")
val price: Int
) : Parcelable

View File

@ -0,0 +1,23 @@
package kr.co.vividnext.sodalive.mypage.can.charge
import com.google.gson.annotations.SerializedName
import kr.co.vividnext.sodalive.mypage.can.payment.PaymentGateway
data class ChargeRequest(
@SerializedName("canId")
val canId: Long,
@SerializedName("paymentGateway")
val paymentGateway: PaymentGateway
)
data class ChargeResponse(
@SerializedName("chargeId")
val chargeId: Long
)
data class VerifyRequest(
@SerializedName("receipt_id")
val receiptId: String,
@SerializedName("order_id")
val orderId: String
)

View File

@ -0,0 +1,227 @@
package kr.co.vividnext.sodalive.mypage.can.payment
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Handler
import android.os.Looper
import android.widget.TextView
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.content.IntentCompat
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.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.Constants
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.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
class CanPaymentActivity : BaseActivity<ActivityCanPaymentBinding>(
ActivityCanPaymentBinding::inflate
) {
enum class PaymentMethod(val method: String) {
CARD("디지털카드"), BANK("디지털계좌이체")
}
private val viewModel: CanPaymentViewModel by inject()
private var canResponse: CanResponse? = null
private var prevLiveRoom: Boolean = false
private val handler = Handler(Looper.getMainLooper())
@SuppressLint("SetTextI18n")
override fun setupView() {
canResponse = IntentCompat.getParcelableExtra(
intent,
Constants.EXTRA_CAN,
CanResponse::class.java
)
if (canResponse == null) {
Toast.makeText(
applicationContext,
"다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다.",
Toast.LENGTH_LONG
).show()
finish()
}
prevLiveRoom = intent.getBooleanExtra(
Constants.EXTRA_PREV_LIVE_ROOM,
false
)
binding.toolbar.tvBack.text = "결제하기"
binding.toolbar.tvBack.setOnClickListener { finish() }
binding.tvChargeCanTitle.text = canResponse!!.title
binding.tvPrice.text = canResponse!!.price.moneyFormat()
binding.tvPaymentPrice.text = "${canResponse!!.price.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) }
viewModel.paymentMethodLiveData.observe(this) {
allPaymentMethodSelectFalse()
if (it != null) {
when (it) {
PaymentMethod.CARD -> paymentMethodSelect(binding.tvMethodCard)
PaymentMethod.BANK -> paymentMethodSelect(binding.tvMethodBank)
}
}
}
}
private fun allPaymentMethodSelectFalse() {
paymentMethodSelectFalse(binding.tvMethodBank)
paymentMethodSelectFalse(binding.tvMethodCard)
}
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_9970ff))
view.setBackgroundResource(R.drawable.bg_round_corner_10_4d9970ff_9970ff)
}
private fun requestCharge() {
viewModel.chargeCan(
canId = canResponse!!.id,
paymentGateway = PaymentGateway.PG,
onSuccess = {
requestPayment(chargeId = it)
},
onFailure = {
Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show()
}
)
}
private fun requestPayment(chargeId: Long) {
val payload = Payload()
.setApplicationId(BuildConfig.BOOTPAY_APP_ID)
.setOrderId("$chargeId")
.setOrderName(canResponse!!.title)
.setPrice(canResponse!!.price.toDouble())
.setTaxFree(0.toDouble())
.setPg("웰컴페이먼츠")
.setMethod(viewModel.paymentMethodLiveData.value!!.method)
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 = {
Toast.makeText(applicationContext, "코인이 충전되었습니다", Toast.LENGTH_LONG).show()
if (prevLiveRoom) {
setResult(RESULT_OK)
} else {
val intent = Intent(applicationContext, CanStatusActivity::class.java)
startActivity(intent)
}
SharedPreferenceManager.can += (canResponse!!.rewardCan + canResponse!!.can)
finish()
},
onFailure = {
Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show()
}
)
}
}

View File

@ -0,0 +1,93 @@
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.CanRepository
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() {
private val _paymentMethodLiveData = MutableLiveData<CanPaymentActivity.PaymentMethod?>()
val paymentMethodLiveData: LiveData<CanPaymentActivity.PaymentMethod?>
get() = _paymentMethodLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
fun chargeCan(
canId: Long,
paymentGateway: PaymentGateway,
onSuccess: (Long) -> Unit,
onFailure: (String) -> Unit
) {
_isLoading.value = true
val request = ChargeRequest(canId = canId, paymentGateway = paymentGateway)
compositeDisposable.add(
repository.chargeCan(
chargeRequest = 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: CanPaymentActivity.PaymentMethod) {
_paymentMethodLiveData.value = paymentMethod
}
}

View File

@ -0,0 +1,8 @@
package kr.co.vividnext.sodalive.mypage.can.payment
import com.google.gson.annotations.SerializedName
enum class PaymentGateway {
@SerializedName("PG") PG,
@SerializedName("GOOGLE_IAP") GOOGLE_IAP
}

View File

@ -12,6 +12,7 @@ import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.ActivityCanStatusBinding
import kr.co.vividnext.sodalive.extensions.moneyFormat
import kr.co.vividnext.sodalive.main.MainActivity
import kr.co.vividnext.sodalive.mypage.can.charge.CanChargeActivity
import kr.co.vividnext.sodalive.mypage.can.status.charge.CanChargeStatusFragment
import kr.co.vividnext.sodalive.mypage.can.status.use.CanUseStatusFragment
import org.koin.android.ext.android.inject
@ -40,7 +41,14 @@ class CanStatusActivity : BaseActivity<ActivityCanStatusBinding>(
override fun setupView() {
binding.toolbar.tvBack.text = "코인내역"
binding.toolbar.tvBack.setOnClickListener { onClickBackButton() }
binding.llChargeCoin.setOnClickListener {}
binding.llChargeCan.setOnClickListener {
startActivity(
Intent(
applicationContext,
CanChargeActivity::class.java
)
)
}
loadingDialog = LoadingDialog(this, layoutInflater)
@ -93,16 +101,16 @@ class CanStatusActivity : BaseActivity<ActivityCanStatusBinding>(
@SuppressLint("SetTextI18n")
private fun bindData() {
viewModel.totalCoinLiveData.observe(this) {
binding.tvTotalCoin.text = it.moneyFormat()
viewModel.totalCanLiveData.observe(this) {
binding.tvTotalCan.text = it.moneyFormat()
}
viewModel.paidCoinLiveData.observe(this) {
binding.tvPaidCoin.text = it.moneyFormat()
viewModel.paidCanLiveData.observe(this) {
binding.tvPaidCan.text = it.moneyFormat()
}
viewModel.rewardCoinLiveData.observe(this) {
binding.tvRewardCoin.text = it.moneyFormat()
viewModel.rewardCanLiveData.observe(this) {
binding.tvRewardCan.text = it.moneyFormat()
}
viewModel.toastLiveData.observe(this) {

View File

@ -12,25 +12,25 @@ import kr.co.vividnext.sodalive.mypage.can.status.charge.GetCanChargeStatusRespo
import kr.co.vividnext.sodalive.mypage.can.status.use.GetCanUseStatusResponseItem
class CanStatusViewModel(private val repository: CanRepository) : BaseViewModel() {
private val _totalCoinLiveData = MutableLiveData<Int>()
val totalCoinLiveData: LiveData<Int>
get() = _totalCoinLiveData
private val _totalCanLiveData = MutableLiveData<Int>()
val totalCanLiveData: LiveData<Int>
get() = _totalCanLiveData
private val _paidCoinLiveData = MutableLiveData<Int>()
val paidCoinLiveData: LiveData<Int>
get() = _paidCoinLiveData
private val _paidCanLiveData = MutableLiveData<Int>()
val paidCanLiveData: LiveData<Int>
get() = _paidCanLiveData
private val _rewardCoinLiveData = MutableLiveData<Int>()
val rewardCoinLiveData: LiveData<Int>
get() = _rewardCoinLiveData
private val _rewardCanLiveData = MutableLiveData<Int>()
val rewardCanLiveData: LiveData<Int>
get() = _rewardCanLiveData
private val _coinUseStatusLiveData = MutableLiveData<List<GetCanUseStatusResponseItem>>()
val coinUseStatusLiveData: LiveData<List<GetCanUseStatusResponseItem>>
get() = _coinUseStatusLiveData
private val _canUseStatusLiveData = MutableLiveData<List<GetCanUseStatusResponseItem>>()
val canUseStatusLiveData: LiveData<List<GetCanUseStatusResponseItem>>
get() = _canUseStatusLiveData
private val _coinChargeStatusLiveData = MutableLiveData<List<GetCanChargeStatusResponseItem>>()
val coinChargeStatusLiveData: LiveData<List<GetCanChargeStatusResponseItem>>
get() = _coinChargeStatusLiveData
private val _canChargeStatusLiveData = MutableLiveData<List<GetCanChargeStatusResponseItem>>()
val canChargeStatusLiveData: LiveData<List<GetCanChargeStatusResponseItem>>
get() = _canChargeStatusLiveData
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
@ -53,9 +53,9 @@ class CanStatusViewModel(private val repository: CanRepository) : BaseViewModel(
{
_isLoading.value = false
if (it.success && it.data != null) {
_totalCoinLiveData.postValue(it.data.chargeCan + it.data.rewardCan)
_paidCoinLiveData.postValue(it.data.chargeCan)
_rewardCoinLiveData.postValue(it.data.rewardCan)
_totalCanLiveData.postValue(it.data.chargeCan + it.data.rewardCan)
_paidCanLiveData.postValue(it.data.chargeCan)
_rewardCanLiveData.postValue(it.data.rewardCan)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
@ -75,7 +75,7 @@ class CanStatusViewModel(private val repository: CanRepository) : BaseViewModel(
)
}
fun getCoinUseStatus() {
fun getCanUseStatus() {
_isLoading.value = true
compositeDisposable.add(
repository.getCanUseStatus(
@ -89,7 +89,7 @@ class CanStatusViewModel(private val repository: CanRepository) : BaseViewModel(
{
_isLoading.value = false
if (it.success && it.data != null) {
_coinUseStatusLiveData.postValue(it.data!!)
_canUseStatusLiveData.postValue(it.data!!)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
@ -123,7 +123,7 @@ class CanStatusViewModel(private val repository: CanRepository) : BaseViewModel(
{
_isLoading.value = false
if (it.success && it.data != null) {
_coinChargeStatusLiveData.postValue(it.data!!)
_canChargeStatusLiveData.postValue(it.data!!)
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)

View File

@ -13,7 +13,7 @@ class CanChargeStatusAdapter : RecyclerView.Adapter<CanChargeStatusAdapter.ViewH
private val binding: ItemCanChargeStatusBinding
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: GetCanChargeStatusResponseItem) {
binding.tvTitle.text = item.coinTitle
binding.tvTitle.text = item.canTitle
binding.tvDate.text = item.date
binding.tvChargeMethod.text = item.chargeMethod
}

View File

@ -30,7 +30,7 @@ class CanChargeStatusFragment(
@SuppressLint("NotifyDataSetChanged")
private fun setupView() {
val recyclerView = binding.rvCoinStatus
val recyclerView = binding.rvCanStatus
adapter = CanChargeStatusAdapter()
recyclerView.layoutManager = LinearLayoutManager(
@ -72,7 +72,7 @@ class CanChargeStatusFragment(
@SuppressLint("NotifyDataSetChanged")
private fun bindData() {
viewModel.coinChargeStatusLiveData.observe(viewLifecycleOwner) {
viewModel.canChargeStatusLiveData.observe(viewLifecycleOwner) {
adapter.items.addAll(it)
adapter.notifyDataSetChanged()
}

View File

@ -3,8 +3,8 @@ package kr.co.vividnext.sodalive.mypage.can.status.charge
import com.google.gson.annotations.SerializedName
data class GetCanChargeStatusResponseItem(
@SerializedName("coinTitle")
val coinTitle: String,
@SerializedName("canTitle")
val canTitle: String,
@SerializedName("date")
val date: String,
@SerializedName("chargeMethod")

View File

@ -15,7 +15,7 @@ class CanUseStatusAdapter : RecyclerView.Adapter<CanUseStatusAdapter.ViewHolder>
fun bind(item: GetCanUseStatusResponseItem) {
binding.tvTitle.text = item.title
binding.tvDate.text = item.date
binding.tvCoin.text = "${item.can}"
binding.tvCan.text = "${item.can}"
}
}

View File

@ -25,11 +25,11 @@ class CanUseStatusFragment(
setupView()
bindData()
viewModel.getCoinUseStatus()
viewModel.getCanUseStatus()
}
private fun setupView() {
val recyclerView = binding.rvCoinStatus
val recyclerView = binding.rvCanStatus
adapter = CanUseStatusAdapter()
recyclerView.layoutManager = LinearLayoutManager(
@ -71,7 +71,7 @@ class CanUseStatusFragment(
@SuppressLint("NotifyDataSetChanged")
private fun bindData() {
viewModel.coinUseStatusLiveData.observe(viewLifecycleOwner) {
viewModel.canUseStatusLiveData.observe(viewLifecycleOwner) {
adapter.items.addAll(it)
adapter.notifyDataSetChanged()
}

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_232323" />
<corners android:radius="10dp" />
<stroke
android:width="1dp"
android:color="@color/color_777777" />
</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_4d9970ff" />
<corners android:radius="10dp" />
<stroke
android:width="1dp"
android:color="@color/color_9970ff" />
</shape>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<include
android:id="@+id/toolbar"
layout="@layout/detail_toolbar" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_charge_can"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,210 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black">
<include
android:id="@+id/toolbar"
layout="@layout/detail_toolbar" />
<ScrollView
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/ll_payment"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_charge_can"
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_16_7_222222"
android:paddingHorizontal="13.3dp"
android:paddingVertical="23.3dp">
<ImageView
android:id="@+id/ic_can"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_can"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="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 코인" />
<LinearLayout
android:id="@+id/ll_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/tv_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:textColor="@color/color_eeeeee"
android:textSize="15.3sp"
tools:text="3,300" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_light"
android:text=" 원"
android:textColor="@color/color_eeeeee"
android:textSize="15.3sp" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="26.7dp"
android:fontFamily="@font/gmarket_sans_bold"
android:text="결제 수단 선택"
android:textColor="@color/color_eeeeee"
android:textSize="16.7sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"
android:layout_marginTop="16.7dp">
<TextView
android:id="@+id/tv_method_card"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_10_232323_777777"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:paddingVertical="16.7dp"
android:text="카드"
android:textColor="@color/color_eeeeee"
android:textSize="13.3sp" />
<TextView
android:id="@+id/tv_method_bank"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:layout_weight="1"
android:background="@drawable/bg_round_corner_10_232323_777777"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:paddingVertical="16.7dp"
android:text="계좌이체"
android:textColor="@color/color_eeeeee"
android:textSize="13.3sp" />
</LinearLayout>
<TextView
android:id="@+id/tv_agree"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="26.7dp"
android:layout_marginTop="13.3dp"
android:button="@null"
android:drawablePadding="6.7dp"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:text="구매조건 확인 및 결제 진행 동의"
android:textColor="@color/color_eeeeee"
android:textSize="14.7sp"
app:drawableStartCompat="@drawable/ic_select" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="26.7dp"
android:layout_marginTop="13.3dp"
android:fontFamily="@font/gmarket_sans_medium"
android:text="- 충전된 코인의 유효기간은 충전 후 5년 입니다.\n- 결제 취소는 결제 후 7일 이내에만 할 수 있습니다.\n 단, 코인의 일부를 사용하면 결제 취소할 수 없습니다.\n- 광고성 이벤트 등 회사가 무료로 지급한 \n포인트는 환불되지 않습니다.\n- 자세한 내용은 요즘 이용약관에서 확인할 수 있습니다."
android:textColor="@color/color_777777"
android:textSize="12sp" />
</LinearLayout>
</ScrollView>
<LinearLayout
android:id="@+id/ll_payment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_top_round_corner_16_7_222222"
android:paddingVertical="13.3dp"
android:paddingStart="22dp"
android:paddingEnd="13.3dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:text="결제금액"
android:textColor="@color/color_eeeeee"
android:textSize="13.3sp" />
<TextView
android:id="@+id/tv_payment_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:fontFamily="@font/gmarket_sans_bold"
android:textColor="@color/color_eeeeee"
android:textSize="23.3sp"
tools:text="3,300원" />
</LinearLayout>
<TextView
android:id="@+id/tv_payment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="31dp"
android:background="@drawable/bg_round_corner_10_80d8ff"
android:fontFamily="@font/gmarket_sans_bold"
android:gravity="center"
android:paddingVertical="16dp"
android:text="결제하기"
android:textColor="@color/color_1313bc"
android:textSize="18.3sp" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -13,7 +13,7 @@
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_above="@+id/fl_charge_coin"
android:layout_above="@+id/fl_charge_can"
android:layout_below="@+id/toolbar">
<LinearLayout
@ -32,7 +32,7 @@
android:paddingVertical="16.7dp">
<TextView
android:id="@+id/tv_total_coin"
android:id="@+id/tv_total_can"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="6.7dp"
@ -55,7 +55,7 @@
android:layout_weight="1">
<TextView
android:id="@+id/tv_payment_coin_title"
android:id="@+id/tv_payment_can_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
@ -67,13 +67,13 @@
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_payment_coin_title"
android:layout_below="@+id/tv_payment_can_title"
android:layout_centerHorizontal="true"
android:layout_marginTop="6.7dp"
android:gravity="center">
<TextView
android:id="@+id/tv_paid_coin"
android:id="@+id/tv_paid_can"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
@ -105,7 +105,7 @@
android:layout_weight="1">
<TextView
android:id="@+id/tv_reward_coin_title"
android:id="@+id/tv_reward_can_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
@ -117,13 +117,13 @@
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_reward_coin_title"
android:layout_below="@+id/tv_reward_can_title"
android:layout_centerHorizontal="true"
android:layout_marginTop="6.7dp"
android:gravity="center">
<TextView
android:id="@+id/tv_reward_coin"
android:id="@+id/tv_reward_can"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
@ -166,14 +166,14 @@
</androidx.core.widget.NestedScrollView>
<FrameLayout
android:id="@+id/fl_charge_coin"
android:id="@+id/fl_charge_can"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="@drawable/bg_top_round_corner_16_7_222222">
<LinearLayout
android:id="@+id/ll_charge_coin"
android:id="@+id/ll_charge_can"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="13.3dp"

View File

@ -4,7 +4,7 @@
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_coin_status"
android:id="@+id/rv_can_status"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View File

@ -166,7 +166,7 @@
android:padding="13.3dp">
<LinearLayout
android:id="@+id/ll_total_coin"
android:id="@+id/ll_total_can"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
@ -174,7 +174,7 @@
tools:ignore="RelativeOverlap">
<TextView
android:id="@+id/tv_total_coin"
android:id="@+id/tv_total_can"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
@ -201,7 +201,7 @@
</LinearLayout>
<TextView
android:id="@+id/tv_charge_coin"
android:id="@+id/tv_charge_can"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_round_corner_16_7_111111"
android:paddingHorizontal="13.3dp"
android:paddingVertical="23.3dp">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="13.3dp"
android:fontFamily="@font/gmarket_sans_bold"
android:textColor="@color/color_eeeeee"
android:textSize="15.3sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="5000 코인 + 1000 코인" />
<LinearLayout
android:id="@+id/ll_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/tv_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:textColor="@color/color_eeeeee"
android:textSize="15.3sp"
tools:text="3,300" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_medium"
android:text=" 원"
android:textColor="@color/color_eeeeee"
android:textSize="15.3sp" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -33,7 +33,7 @@
tools:text="2021.07.22 | 21:02:45" />
<TextView
android:id="@+id/tv_coin"
android:id="@+id/tv_can"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="6.7dp"

View File

@ -3,6 +3,8 @@
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="color_80d8ff">#80DFFF</color>
<color name="color_1313bc">#1313BC</color>
<color name="color_9970ff">#9970FF</color>
<color name="color_eeeeee">#EEEEEE</color>
<color name="color_777777">#777777</color>
@ -24,11 +26,11 @@
<color name="color_fdca2f">#fdca2f</color>
<color name="color_352953">#352953</color>
<color name="color_664aab">#664aab</color>
<color name="color_232323">#232323</color>
<color name="color_b3909090">#B3909090</color>
<color name="color_88909090">#88909090</color>
<color name="color_339970ff">#339970FF</color>
<color name="color_7fe2e2e2">#7FE2E2E2</color>
<color name="color_80d8ff">#80DFFF</color>
<color name="color_1313bc">#1313BC</color>
<color name="color_4d9970ff">#4D9970FF</color>
</resources>