From 79127801c68d68f807bfe4546948ca330865288e Mon Sep 17 00:00:00 2001 From: klaus Date: Sun, 30 Jul 2023 16:20:58 +0900 Subject: [PATCH] =?UTF-8?q?=EC=BD=94=EC=9D=B8=20=EC=B6=A9=EC=A0=84,=20?= =?UTF-8?q?=EC=BD=94=EC=9D=B8=20=EB=82=B4=EC=97=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 2 + .../co/vividnext/sodalive/common/Constants.kt | 2 + .../sodalive/common/CustomTypefaceSpan.kt | 15 ++ .../java/kr/co/vividnext/sodalive/di/AppDI.kt | 4 + .../sodalive/extensions/StringExtensions.kt | 48 ++++ .../sodalive/mypage/MyPageFragment.kt | 16 +- .../sodalive/mypage/MyPageResponse.kt | 8 +- .../vividnext/sodalive/mypage/can/CanApi.kt | 23 ++ .../sodalive/mypage/can/CanRepository.kt | 14 ++ .../mypage/can/charge/CanChargeActivity.kt | 126 ++++++++++ .../mypage/can/charge/CanChargeAdapter.kt | 52 ++++ .../mypage/can/charge/CanChargeViewModel.kt | 54 +++++ .../sodalive/mypage/can/charge/CanResponse.kt | 19 ++ .../sodalive/mypage/can/charge/ChargeData.kt | 23 ++ .../mypage/can/payment/CanPaymentActivity.kt | 227 ++++++++++++++++++ .../mypage/can/payment/CanPaymentViewModel.kt | 93 +++++++ .../mypage/can/payment/PaymentGateway.kt | 8 + .../mypage/can/status/CanStatusActivity.kt | 22 +- .../mypage/can/status/CanStatusViewModel.kt | 42 ++-- .../status/charge/CanChargeStatusAdapter.kt | 2 +- .../status/charge/CanChargeStatusFragment.kt | 4 +- .../charge/GetCanChargeStatusResponseItem.kt | 4 +- .../can/status/use/CanUseStatusAdapter.kt | 2 +- .../can/status/use/CanUseStatusFragment.kt | 6 +- .../bg_round_corner_10_232323_777777.xml | 8 + .../bg_round_corner_10_4d9970ff_9970ff.xml | 8 + .../main/res/layout/activity_can_charge.xml | 20 ++ .../main/res/layout/activity_can_payment.xml | 210 ++++++++++++++++ .../main/res/layout/activity_can_status.xml | 20 +- .../main/res/layout/fragment_can_status.xml | 2 +- app/src/main/res/layout/fragment_my.xml | 6 +- app/src/main/res/layout/item_can_charge.xml | 51 ++++ .../main/res/layout/item_can_use_status.xml | 2 +- app/src/main/res/values/colors.xml | 6 +- 34 files changed, 1086 insertions(+), 63 deletions(-) create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/common/CustomTypefaceSpan.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/extensions/StringExtensions.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/CanChargeActivity.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/CanChargeAdapter.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/CanChargeViewModel.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/CanResponse.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/ChargeData.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/CanPaymentActivity.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/CanPaymentViewModel.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/PaymentGateway.kt create mode 100644 app/src/main/res/drawable/bg_round_corner_10_232323_777777.xml create mode 100644 app/src/main/res/drawable/bg_round_corner_10_4d9970ff_9970ff.xml create mode 100644 app/src/main/res/layout/activity_can_charge.xml create mode 100644 app/src/main/res/layout/activity_can_payment.xml create mode 100644 app/src/main/res/layout/item_can_charge.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8bbc36b..8c90386 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -34,6 +34,8 @@ + + (FragmentMyBinding::inflat binding.ivEdit.setOnClickListener {} - binding.llTotalCoin.setOnClickListener { + binding.llTotalCan.setOnClickListener { startActivity( Intent( requireActivity(), @@ -56,7 +57,14 @@ class MyPageFragment : BaseFragment(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::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}" } } } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageResponse.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageResponse.kt index a502b7f..04e8f01 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageResponse.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/MyPageResponse.kt @@ -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, ) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/CanApi.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/CanApi.kt index 83f1d4b..a3ce8c7 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/CanApi.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/CanApi.kt @@ -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> + + @POST("/charge/verify") + fun verifyCharge( + @Body request: VerifyRequest, + @Header("Authorization") authHeader: String + ): Single> + + @GET("/can") + fun getCans( + @Header("Authorization") authHeader: String + ): Single>> + @GET("/can/status") fun getCanStatus( @Query("container") container: String = "aos", diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/CanRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/CanRepository.kt index a8f939b..37ddf6d 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/CanRepository.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/CanRepository.kt @@ -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( diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/CanChargeActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/CanChargeActivity.kt new file mode 100644 index 0000000..4938104 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/CanChargeActivity.kt @@ -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::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 + + 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() + } + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/CanChargeAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/CanChargeAdapter.kt new file mode 100644 index 0000000..55a58e3 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/CanChargeAdapter.kt @@ -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() { + + val items = mutableListOf() + + 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() +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/CanChargeViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/CanChargeViewModel.kt new file mode 100644 index 0000000..e177de5 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/CanChargeViewModel.kt @@ -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>() + val canChargeLiveData: LiveData> + get() = _canChargesLiveData + + private val _toastLiveData = MutableLiveData() + val toastLiveData: LiveData + get() = _toastLiveData + + private var _isLoading = MutableLiveData(false) + val isLoading: LiveData + 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("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.") + } + ) + ) + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/CanResponse.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/CanResponse.kt new file mode 100644 index 0000000..4251309 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/CanResponse.kt @@ -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 diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/ChargeData.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/ChargeData.kt new file mode 100644 index 0000000..ec53798 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/ChargeData.kt @@ -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 +) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/CanPaymentActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/CanPaymentActivity.kt new file mode 100644 index 0000000..c1473ed --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/CanPaymentActivity.kt @@ -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::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() + } + ) + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/CanPaymentViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/CanPaymentViewModel.kt new file mode 100644 index 0000000..ca825ce --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/CanPaymentViewModel.kt @@ -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() + val paymentMethodLiveData: LiveData + get() = _paymentMethodLiveData + + private var _isLoading = MutableLiveData(false) + val isLoading: LiveData + 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 + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/PaymentGateway.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/PaymentGateway.kt new file mode 100644 index 0000000..cd164f5 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/payment/PaymentGateway.kt @@ -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 +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/status/CanStatusActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/status/CanStatusActivity.kt index 2a82c9f..518e317 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/status/CanStatusActivity.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/status/CanStatusActivity.kt @@ -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( 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( @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) { diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/status/CanStatusViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/status/CanStatusViewModel.kt index 557d19f..336c731 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/status/CanStatusViewModel.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/status/CanStatusViewModel.kt @@ -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() - val totalCoinLiveData: LiveData - get() = _totalCoinLiveData + private val _totalCanLiveData = MutableLiveData() + val totalCanLiveData: LiveData + get() = _totalCanLiveData - private val _paidCoinLiveData = MutableLiveData() - val paidCoinLiveData: LiveData - get() = _paidCoinLiveData + private val _paidCanLiveData = MutableLiveData() + val paidCanLiveData: LiveData + get() = _paidCanLiveData - private val _rewardCoinLiveData = MutableLiveData() - val rewardCoinLiveData: LiveData - get() = _rewardCoinLiveData + private val _rewardCanLiveData = MutableLiveData() + val rewardCanLiveData: LiveData + get() = _rewardCanLiveData - private val _coinUseStatusLiveData = MutableLiveData>() - val coinUseStatusLiveData: LiveData> - get() = _coinUseStatusLiveData + private val _canUseStatusLiveData = MutableLiveData>() + val canUseStatusLiveData: LiveData> + get() = _canUseStatusLiveData - private val _coinChargeStatusLiveData = MutableLiveData>() - val coinChargeStatusLiveData: LiveData> - get() = _coinChargeStatusLiveData + private val _canChargeStatusLiveData = MutableLiveData>() + val canChargeStatusLiveData: LiveData> + get() = _canChargeStatusLiveData private val _toastLiveData = MutableLiveData() val toastLiveData: LiveData @@ -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) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/status/charge/CanChargeStatusAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/status/charge/CanChargeStatusAdapter.kt index 29f0da0..6608194 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/status/charge/CanChargeStatusAdapter.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/status/charge/CanChargeStatusAdapter.kt @@ -13,7 +13,7 @@ class CanChargeStatusAdapter : RecyclerView.Adapter fun bind(item: GetCanUseStatusResponseItem) { binding.tvTitle.text = item.title binding.tvDate.text = item.date - binding.tvCoin.text = "${item.can}" + binding.tvCan.text = "${item.can}" } } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/status/use/CanUseStatusFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/status/use/CanUseStatusFragment.kt index d1fa376..b86d098 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/status/use/CanUseStatusFragment.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/status/use/CanUseStatusFragment.kt @@ -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() } diff --git a/app/src/main/res/drawable/bg_round_corner_10_232323_777777.xml b/app/src/main/res/drawable/bg_round_corner_10_232323_777777.xml new file mode 100644 index 0000000..9b82a2b --- /dev/null +++ b/app/src/main/res/drawable/bg_round_corner_10_232323_777777.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/app/src/main/res/drawable/bg_round_corner_10_4d9970ff_9970ff.xml b/app/src/main/res/drawable/bg_round_corner_10_4d9970ff_9970ff.xml new file mode 100644 index 0000000..ffe4bab --- /dev/null +++ b/app/src/main/res/drawable/bg_round_corner_10_4d9970ff_9970ff.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/app/src/main/res/layout/activity_can_charge.xml b/app/src/main/res/layout/activity_can_charge.xml new file mode 100644 index 0000000..cc89b85 --- /dev/null +++ b/app/src/main/res/layout/activity_can_charge.xml @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/app/src/main/res/layout/activity_can_payment.xml b/app/src/main/res/layout/activity_can_payment.xml new file mode 100644 index 0000000..3f050e4 --- /dev/null +++ b/app/src/main/res/layout/activity_can_payment.xml @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_can_status.xml b/app/src/main/res/layout/activity_can_status.xml index 41ae6b0..e5169e8 100644 --- a/app/src/main/res/layout/activity_can_status.xml +++ b/app/src/main/res/layout/activity_can_status.xml @@ -13,7 +13,7 @@ diff --git a/app/src/main/res/layout/fragment_my.xml b/app/src/main/res/layout/fragment_my.xml index 13f8d31..da41b9e 100644 --- a/app/src/main/res/layout/fragment_my.xml +++ b/app/src/main/res/layout/fragment_my.xml @@ -166,7 +166,7 @@ android:padding="13.3dp"> + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_can_use_status.xml b/app/src/main/res/layout/item_can_use_status.xml index 421879a..feff05b 100644 --- a/app/src/main/res/layout/item_can_use_status.xml +++ b/app/src/main/res/layout/item_can_use_status.xml @@ -33,7 +33,7 @@ tools:text="2021.07.22 | 21:02:45" /> #FF000000 #FFFFFFFF + #80DFFF + #1313BC #9970FF #EEEEEE #777777 @@ -24,11 +26,11 @@ #fdca2f #352953 #664aab + #232323 #B3909090 #88909090 #339970FF #7FE2E2E2 - #80DFFF - #1313BC + #4D9970FF