mirror of https://github.com/tasks/tasks
Add PurchaseActivityViewModel
parent
93231f3c10
commit
04a7eef174
@ -1,138 +1,55 @@
|
||||
package org.tasks.billing
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.launch
|
||||
import org.tasks.LocalBroadcastManager
|
||||
import org.tasks.R
|
||||
import org.tasks.analytics.Firebase
|
||||
import org.tasks.compose.PurchaseText.SubscriptionScreen
|
||||
import org.tasks.extensions.Context.toast
|
||||
import org.tasks.preferences.Preferences
|
||||
import org.tasks.extensions.Context.findActivity
|
||||
import org.tasks.themes.TasksTheme
|
||||
import org.tasks.themes.Theme
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class PurchaseActivity : AppCompatActivity(), OnPurchasesUpdated {
|
||||
@Inject lateinit var theme: Theme
|
||||
@Inject lateinit var billingClient: BillingClient
|
||||
@Inject lateinit var localBroadcastManager: LocalBroadcastManager
|
||||
@Inject lateinit var inventory: Inventory
|
||||
@Inject lateinit var preferences: Preferences
|
||||
@Inject lateinit var firebase: Firebase
|
||||
|
||||
private var currentSubscription: Purchase? = null
|
||||
private val purchaseReceiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
setup()
|
||||
}
|
||||
}
|
||||
private val nameYourPrice = mutableStateOf(false)
|
||||
private val sliderPosition = mutableStateOf(-1f)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val github = intent?.extras?.getBoolean(EXTRA_GITHUB) ?: false
|
||||
|
||||
theme.applyToContext(this)
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
nameYourPrice.value = intent?.extras?.getBoolean(EXTRA_NAME_YOUR_PRICE) ?: firebase.nameYourPrice
|
||||
} else {
|
||||
nameYourPrice.value = savedInstanceState.getBoolean(EXTRA_NAME_YOUR_PRICE)
|
||||
sliderPosition.value = savedInstanceState.getFloat(EXTRA_PRICE)
|
||||
}
|
||||
|
||||
setContent {
|
||||
TasksTheme(theme = theme.themeBase.index) {
|
||||
BackHandler {
|
||||
finish()
|
||||
}
|
||||
val viewModel: PurchaseActivityViewModel = viewModel()
|
||||
val state = viewModel.viewState.collectAsStateWithLifecycle().value
|
||||
val context = LocalContext.current
|
||||
SubscriptionScreen(
|
||||
nameYourPrice = nameYourPrice,
|
||||
sliderPosition = sliderPosition,
|
||||
github = github,
|
||||
subscribe = this::purchase,
|
||||
nameYourPrice = state.nameYourPrice,
|
||||
sliderPosition = state.price,
|
||||
github = state.isGithub,
|
||||
subscribe = { price, isMonthly ->
|
||||
context.findActivity()?.let { viewModel.purchase(it, price, isMonthly) }
|
||||
},
|
||||
setPrice = { viewModel.setPrice(it) },
|
||||
setNameYourPrice = { viewModel.setNameYourPrice(it) },
|
||||
onBack = { finish() },
|
||||
)
|
||||
LaunchedEffect(key1 = Unit) {
|
||||
firebase.logEvent(R.string.event_showed_purchase_dialog)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
|
||||
localBroadcastManager.registerPurchaseReceiver(purchaseReceiver)
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
billingClient.queryPurchases(throwError = true)
|
||||
} catch (e: Exception) {
|
||||
toast(e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
localBroadcastManager.unregisterReceiver(purchaseReceiver)
|
||||
}
|
||||
|
||||
private fun setup() {
|
||||
currentSubscription = inventory.subscription.value
|
||||
if (sliderPosition.value < 0) {
|
||||
sliderPosition.value =
|
||||
currentSubscription
|
||||
?.subscriptionPrice
|
||||
?.coerceAtMost(25)
|
||||
?.toFloat() ?: 10f
|
||||
}
|
||||
}
|
||||
|
||||
private fun purchase(price: Int, monthly: Boolean) = lifecycleScope.launch {
|
||||
val newSku = String.format(Locale.US, "%s_%02d", if (monthly) "monthly" else "annual", price)
|
||||
try {
|
||||
billingClient.initiatePurchaseFlow(
|
||||
this@PurchaseActivity,
|
||||
newSku,
|
||||
BillingClientImpl.TYPE_SUBS,
|
||||
currentSubscription?.takeIf { it.sku != newSku }
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
this@PurchaseActivity.toast(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPurchasesUpdated(success: Boolean) {
|
||||
if (success) {
|
||||
setResult(RESULT_OK)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putFloat(EXTRA_PRICE, sliderPosition.value)
|
||||
outState.putBoolean(EXTRA_NAME_YOUR_PRICE, nameYourPrice.value)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val EXTRA_GITHUB = "extra_github"
|
||||
private const val EXTRA_PRICE = "extra_price"
|
||||
const val EXTRA_NAME_YOUR_PRICE = "extra_name_your_price"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,109 @@
|
||||
package org.tasks.billing
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import org.tasks.LocalBroadcastManager
|
||||
import org.tasks.R
|
||||
import org.tasks.analytics.Firebase
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class PurchaseActivityViewModel @Inject constructor(
|
||||
savedStateHandle: SavedStateHandle,
|
||||
private val inventory: Inventory,
|
||||
private val billingClient: BillingClient,
|
||||
private val localBroadcastManager: LocalBroadcastManager,
|
||||
firebase: Firebase,
|
||||
) : ViewModel() {
|
||||
|
||||
data class ViewState(
|
||||
val nameYourPrice: Boolean,
|
||||
val isGithub: Boolean,
|
||||
val price: Float = -1f,
|
||||
val subscription: Purchase? = null,
|
||||
val error: String? = null,
|
||||
)
|
||||
|
||||
private val purchaseReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val subscription = inventory.subscription.value
|
||||
_viewState.update { state ->
|
||||
state.copy(
|
||||
subscription = subscription,
|
||||
price = state.price.takeIf { it > 0 }
|
||||
?: subscription?.subscriptionPrice?.coerceAtMost(25)?.toFloat()
|
||||
?: 10f
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setPrice(price: Float) {
|
||||
_viewState.update { it.copy(price = price) }
|
||||
}
|
||||
|
||||
private val _viewState = MutableStateFlow(
|
||||
ViewState(
|
||||
nameYourPrice = savedStateHandle.get<Boolean>(EXTRA_NAME_YOUR_PRICE) ?: firebase.nameYourPrice,
|
||||
isGithub = savedStateHandle.get<Boolean>(EXTRA_GITHUB) ?: false,
|
||||
)
|
||||
)
|
||||
val viewState: StateFlow<ViewState> = _viewState
|
||||
|
||||
init {
|
||||
localBroadcastManager.registerPurchaseReceiver(purchaseReceiver)
|
||||
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
billingClient.queryPurchases(throwError = true)
|
||||
} catch (e: Exception) {
|
||||
_viewState.update {
|
||||
it.copy(error = e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
firebase.logEvent(R.string.event_showed_purchase_dialog)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
localBroadcastManager.unregisterReceiver(purchaseReceiver)
|
||||
}
|
||||
|
||||
fun purchase(activity: Activity, price: Int, monthly: Boolean) = viewModelScope.launch {
|
||||
val newSku = String.format(Locale.US, "%s_%02d", if (monthly) "monthly" else "annual", price)
|
||||
try {
|
||||
billingClient.initiatePurchaseFlow(
|
||||
activity,
|
||||
newSku,
|
||||
BillingClientImpl.TYPE_SUBS,
|
||||
_viewState.value.subscription?.takeIf { it.sku != newSku },
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
_viewState.update {
|
||||
it.copy(error = e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setNameYourPrice(nameYourPrice: Boolean) {
|
||||
_viewState.update { it.copy(nameYourPrice = nameYourPrice) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val EXTRA_GITHUB = "extra_github"
|
||||
const val EXTRA_NAME_YOUR_PRICE = "extra_name_your_price"
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue