diff --git a/app/build.gradle b/app/build.gradle
index 2c71ffe..7cba4b4 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -40,8 +40,8 @@ android {
applicationId "kr.co.vividnext.sodalive"
minSdk 23
targetSdk 33
- versionCode 41
- versionName "1.8.16"
+ versionCode 46
+ versionName "1.8.21"
}
buildTypes {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3812e77..01bf13f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -85,7 +85,9 @@
-
+
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
index b3bb242..6d18abd 100644
--- 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
@@ -41,7 +41,8 @@ class CanChargeActivity : BaseActivity(
false
)
- changeFragment(IAP_TAG_KEY)
+ supportFragmentManager.beginTransaction()
+ .replace(R.id.fl_container, CanChargeIapFragment()).commit()
}
override fun setupView() {
@@ -59,13 +60,18 @@ class CanChargeActivity : BaseActivity(
val tabs = binding.tabs
tabs.visibility = View.VISIBLE
- tabs.addTab(tabs.newTab().setText("인 앱 결제").setTag(IAP_TAG_KEY))
- tabs.addTab(tabs.newTab().setText("PG").setTag(PG_TAG_KEY))
+ tabs.addTab(tabs.newTab().setText("인 앱 결제"))
+ tabs.addTab(tabs.newTab().setText("PG"))
tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {
- val tag = tab.tag as String
- changeFragment(tag)
+ when (tab.position) {
+ 0 -> supportFragmentManager.beginTransaction()
+ .replace(R.id.fl_container, CanChargeIapFragment()).commit()
+
+ 1 -> supportFragmentManager.beginTransaction()
+ .replace(R.id.fl_container, CanChargePgFragment()).commit()
+ }
}
override fun onTabUnselected(tab: TabLayout.Tab) {
@@ -79,33 +85,6 @@ class CanChargeActivity : BaseActivity(
}
}
- private fun changeFragment(tag: String) {
- val fragmentManager = supportFragmentManager
- val fragmentTransaction = fragmentManager.beginTransaction()
-
- val currentFragment = fragmentManager.primaryNavigationFragment
- if (currentFragment != null) {
- fragmentTransaction.hide(currentFragment)
- }
-
- var fragment = fragmentManager.findFragmentByTag(tag)
- if (fragment == null) {
- fragment = if (tag == PG_TAG_KEY) {
- CanChargePgFragment()
- } else {
- CanChargeIapFragment()
- }
-
- fragmentTransaction.add(R.id.fl_container, fragment, tag)
- } else {
- fragmentTransaction.show(fragment)
- }
-
- fragmentTransaction.setPrimaryNavigationFragment(fragment)
- fragmentTransaction.setReorderingAllowed(true)
- fragmentTransaction.commitNow()
- }
-
fun selectCan(model: CanResponse) {
val intent = Intent(applicationContext, CanPaymentActivity::class.java)
intent.putExtra(Constants.EXTRA_CAN, model)
@@ -123,9 +102,4 @@ class CanChargeActivity : BaseActivity(
finish()
}
-
- companion object {
- const val IAP_TAG_KEY = "iap"
- const val PG_TAG_KEY = "pg"
- }
}
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/iap/CanChargeIapFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/iap/CanChargeIapFragment.kt
index 662725c..0027063 100644
--- a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/iap/CanChargeIapFragment.kt
+++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/iap/CanChargeIapFragment.kt
@@ -1,6 +1,7 @@
package kr.co.vividnext.sodalive.mypage.can.charge.iap
import android.annotation.SuppressLint
+import android.content.Context
import android.graphics.Rect
import android.os.Bundle
import android.os.Handler
@@ -13,10 +14,12 @@ import com.android.billingclient.api.BillingClient
import com.android.billingclient.api.BillingClientStateListener
import com.android.billingclient.api.BillingFlowParams
import com.android.billingclient.api.BillingResult
+import com.android.billingclient.api.ConsumeParams
import com.android.billingclient.api.ProductDetails
import com.android.billingclient.api.Purchase
import com.android.billingclient.api.PurchasesUpdatedListener
import com.android.billingclient.api.QueryProductDetailsParams
+import com.android.billingclient.api.QueryPurchasesParams
import kr.co.vividnext.sodalive.base.BaseFragment
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
@@ -37,57 +40,10 @@ class CanChargeIapFragment : BaseFragment(
private val handler = Handler(Looper.getMainLooper())
private var selectedProductDetails: ProductDetails? = null
- private val purchaseUpdateListener = PurchasesUpdatedListener { billingResult, purchases ->
- if (
- billingResult.responseCode == BillingClient.BillingResponseCode.OK &&
- purchases != null &&
- selectedProductDetails != null
- ) {
- for (purchase in purchases) {
- handlePurchase(purchase)
- }
- } else if (billingResult.responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
- selectedProductDetails = null
- handler.post {
- Toast.makeText(requireActivity(), "구매를 취소했습니다.", Toast.LENGTH_LONG).show()
- }
- } else {
- selectedProductDetails = null
- Toast.makeText(
- requireActivity(),
- "구매를 하지 못했습니다.\n다시 시도해 주세요.",
- Toast.LENGTH_LONG
- ).show()
- }
- }
+ private lateinit var purchaseUpdateListener: PurchasesUpdatedListener
- private fun handlePurchase(purchase: Purchase) {
- if (
- purchase.purchaseState == Purchase.PurchaseState.PURCHASED &&
- !purchase.isAcknowledged
- ) {
- viewModel.chargeCan(
- title = selectedProductDetails!!.name,
- selectedProductDetails = selectedProductDetails!!,
- purchase = purchase
- ) { chargeCan ->
- handler.post {
- Toast.makeText(
- requireActivity(),
- "캔이 충전되었습니다",
- Toast.LENGTH_LONG
- ).show()
- SharedPreferenceManager.can += chargeCan
-
- val activity = requireActivity() as? CanChargeActivity
- if (activity != null) {
- activity.successIapCharge()
- } else {
- requireActivity().finish()
- }
- }
- }
- }
+ fun safeContext(): Context? {
+ return if (isAdded) context else null
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -100,6 +56,31 @@ class CanChargeIapFragment : BaseFragment(
setupBillingClient()
}
+ override fun onStart() {
+ super.onStart()
+
+ billingClient.startConnection(object : BillingClientStateListener {
+ override fun onBillingServiceDisconnected() {
+ viewModel.showToast("인 앱 결제 이용이 불가능 합니다. 다시 시도해 주세요.")
+ viewModel.setLoading(false)
+ }
+
+ override fun onBillingSetupFinished(billingResult: BillingResult) {
+ if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
+ queryAndConsumeUnconsumedPurchases()
+ } else {
+ viewModel.showToast("인 앱 결제 이용이 불가능 합니다. 다시 시도해 주세요.")
+ viewModel.setLoading(false)
+ }
+ }
+ })
+ }
+
+ override fun onStop() {
+ super.onStop()
+ billingClient.endConnection()
+ }
+
private fun bindData() {
viewModel.isLoading.observe(viewLifecycleOwner) {
if (it) {
@@ -162,39 +143,34 @@ class CanChargeIapFragment : BaseFragment(
}
private fun setupBillingClient() {
+ purchaseUpdateListener = PurchasesUpdatedListener { billingResult, purchases ->
+ handler.post {
+ if (
+ billingResult.responseCode == BillingClient.BillingResponseCode.OK &&
+ purchases != null &&
+ selectedProductDetails != null
+ ) {
+ for (purchase in purchases) {
+ handlePurchase(purchase)
+ }
+ } else if (
+ billingResult.responseCode == BillingClient.BillingResponseCode.USER_CANCELED
+ ) {
+ selectedProductDetails = null
+ viewModel.showToast("구매를 취소했습니다.")
+ } else {
+ selectedProductDetails = null
+ viewModel.showToast("구매를 하지 못했습니다.\n다시 시도해 주세요.")
+ }
+ }
+ }
+
billingClient = BillingClient.newBuilder(requireActivity())
.enablePendingPurchases()
.setListener(purchaseUpdateListener)
.build()
loadingDialog.show(screenWidth)
- billingClient.startConnection(object : BillingClientStateListener {
- override fun onBillingServiceDisconnected() {
- handler.post {
- Toast.makeText(
- requireActivity(),
- "인 앱 결제 이용이 불가능 합니다. 다시 시도해 주세요.",
- Toast.LENGTH_LONG
- ).show()
- }
- loadingDialog.dismiss()
- }
-
- override fun onBillingSetupFinished(billingResult: BillingResult) {
- if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
- queryAvailableCans()
- } else {
- handler.post {
- Toast.makeText(
- requireActivity(),
- "인 앱 결제 이용이 불가능 합니다. 다시 시도해 주세요.",
- Toast.LENGTH_LONG
- ).show()
- }
- loadingDialog.dismiss()
- }
- }
- })
}
@SuppressLint("NotifyDataSetChanged")
@@ -224,16 +200,70 @@ class CanChargeIapFragment : BaseFragment(
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
handler.post { adapter.addItems(productDetailsList) }
} else {
- handler.post {
- Toast.makeText(
- requireActivity(),
- "인 앱 결제 이용이 불가능 합니다. 다시 시도해 주세요.",
- Toast.LENGTH_LONG
- ).show()
- }
+ viewModel.showToast("인 앱 결제 이용이 불가능 합니다. 다시 시도해 주세요.")
}
- handler.post { loadingDialog.dismiss() }
+ viewModel.setLoading(false)
+ }
+ }
+
+ private fun queryAndConsumeUnconsumedPurchases() {
+ val queryPurchaseParams = QueryPurchasesParams.newBuilder()
+ .setProductType(BillingClient.ProductType.INAPP)
+ .build()
+
+ billingClient.queryPurchasesAsync(
+ queryPurchaseParams
+ ) { result, purchaseList ->
+ if (result.responseCode == BillingClient.BillingResponseCode.OK) {
+ if (purchaseList.isNotEmpty()) {
+ for (purchase in purchaseList) {
+ if (!purchase.isAcknowledged) {
+ consumePurchase(purchase)
+ }
+ }
+ } else {
+ queryAvailableCans()
+ }
+ }
+ }
+ }
+
+ private fun consumePurchase(purchase: Purchase) {
+ val params = ConsumeParams.newBuilder()
+ .setPurchaseToken(purchase.purchaseToken)
+ .build()
+
+ billingClient.consumeAsync(params) { billingResult, _ ->
+ if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
+ queryAvailableCans()
+ }
+ }
+ }
+
+ private fun handlePurchase(purchase: Purchase) {
+ if (
+ purchase.purchaseState == Purchase.PurchaseState.PURCHASED &&
+ !purchase.isAcknowledged
+ ) {
+ viewModel.chargeCan(
+ title = selectedProductDetails!!.name,
+ selectedProductDetails = selectedProductDetails!!,
+ purchase = purchase
+ ) { chargeCan ->
+ handler.post {
+ viewModel.showToast("캔이 충전되었습니다")
+ SharedPreferenceManager.can += chargeCan
+
+ if (activity != null) {
+ if (activity as? CanChargeActivity != null) {
+ (activity as CanChargeActivity).successIapCharge()
+ } else {
+ requireActivity().finish()
+ }
+ }
+ }
+ }
}
}
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/iap/CanChargeIapViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/iap/CanChargeIapViewModel.kt
index e5b64b1..24f7bdb 100644
--- a/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/iap/CanChargeIapViewModel.kt
+++ b/app/src/main/java/kr/co/vividnext/sodalive/mypage/can/charge/iap/CanChargeIapViewModel.kt
@@ -71,4 +71,12 @@ class CanChargeIapViewModel(private val repository: CanRepository) : BaseViewMod
_toastLiveData.value = "구매를 하지 못했습니다.\n고객센터로 문의해 주시기 바랍니다."
}
}
+
+ fun showToast(message: String) {
+ _toastLiveData.value = message
+ }
+
+ fun setLoading(isLoading: Boolean) {
+ _isLoading.value = isLoading
+ }
}