Update dependencies
Support slow card for donations.
This commit is contained in:
@@ -49,6 +49,7 @@ class App : Application() {
|
||||
override fun onFailed(throwable: Throwable?) = Timber.d(throwable)
|
||||
})
|
||||
})
|
||||
EBegFragment.init()
|
||||
if (DhcpWorkaround.shouldEnable) DhcpWorkaround.enable(true)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,40 +1,66 @@
|
||||
package be.mygod.vpnhotspot
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Button
|
||||
import android.widget.Spinner
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatDialogFragment
|
||||
import be.mygod.vpnhotspot.App.Companion.app
|
||||
import be.mygod.vpnhotspot.util.launchUrl
|
||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||
import com.android.billingclient.api.*
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.android.synthetic.main.fragment_ebeg.view.*
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Based on: https://github.com/PrivacyApps/donations/blob/747d36a18433c7e9329691054122a8ad337a62d2/Donations/src/main/java/org/sufficientlysecure/donations/DonationsFragment.java
|
||||
*/
|
||||
class EBegFragment : AppCompatDialogFragment(), PurchasesUpdatedListener, BillingClientStateListener,
|
||||
SkuDetailsResponseListener, ConsumeResponseListener {
|
||||
@Parcelize
|
||||
data class MessageArg(@StringRes val title: Int, @StringRes val message: Int) : Parcelable
|
||||
class MessageDialogFragment : AlertDialogFragment<MessageArg, Empty>() {
|
||||
override fun AlertDialog.Builder.prepare(listener: DialogInterface.OnClickListener) {
|
||||
setTitle(arg.title)
|
||||
setMessage(arg.message)
|
||||
setNeutralButton(R.string.donations__button_close, null)
|
||||
class EBegFragment : AppCompatDialogFragment(), SkuDetailsResponseListener {
|
||||
companion object : BillingClientStateListener, PurchasesUpdatedListener, ConsumeResponseListener {
|
||||
private lateinit var billingClient: BillingClient
|
||||
|
||||
fun init() {
|
||||
billingClient = BillingClient.newBuilder(app).apply {
|
||||
enablePendingPurchases()
|
||||
}.setListener(this).build().also { it.startConnection(this) }
|
||||
}
|
||||
|
||||
override fun onBillingSetupFinished(billingResult: BillingResult?) {
|
||||
if (billingResult?.responseCode == BillingClient.BillingResponseCode.OK) {
|
||||
billingClient.queryPurchases(BillingClient.SkuType.INAPP).apply {
|
||||
if (responseCode == BillingClient.BillingResponseCode.OK) {
|
||||
onPurchasesUpdated(this.billingResult, purchasesList)
|
||||
}
|
||||
}
|
||||
} else Timber.e("onBillingSetupFinished: ${billingResult?.responseCode}")
|
||||
}
|
||||
|
||||
override fun onBillingServiceDisconnected() {
|
||||
Timber.e("onBillingServiceDisconnected")
|
||||
billingClient.startConnection(this)
|
||||
}
|
||||
|
||||
override fun onPurchasesUpdated(billingResult: BillingResult?, purchases: MutableList<Purchase>?) {
|
||||
if (billingResult?.responseCode == BillingClient.BillingResponseCode.OK && purchases != null) {
|
||||
// directly consume in-app purchase, so that people can donate multiple times
|
||||
for (purchase in purchases) if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
|
||||
billingClient.consumeAsync(ConsumeParams.newBuilder().apply {
|
||||
setPurchaseToken(purchase.purchaseToken)
|
||||
}.build(), this)
|
||||
}
|
||||
} else Timber.e("onPurchasesUpdated: ${billingResult?.responseCode}")
|
||||
}
|
||||
|
||||
override fun onConsumeResponse(billingResult: BillingResult?, purchaseToken: String?) {
|
||||
if (billingResult?.responseCode == BillingClient.BillingResponseCode.OK) {
|
||||
SmartSnackbar.make(R.string.donations__thanks_dialog).show()
|
||||
} else Timber.e("onConsumeResponse: ${billingResult?.responseCode}")
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var billingClient: BillingClient
|
||||
private lateinit var googleSpinner: Spinner
|
||||
private var skus: MutableList<SkuDetails>? = null
|
||||
set(value) {
|
||||
@@ -53,62 +79,27 @@ class EBegFragment : AppCompatDialogFragment(), PurchasesUpdatedListener, Billin
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
dialog!!.setTitle(R.string.settings_misc_donate)
|
||||
googleSpinner = view.donations__google_android_market_spinner
|
||||
onBillingServiceDisconnected()
|
||||
billingClient.querySkuDetailsAsync(
|
||||
SkuDetailsParams.newBuilder().apply {
|
||||
setSkusList(listOf("donate001", "donate002", "donate005", "donate010", "donate020", "donate050",
|
||||
"donate100", "donate200", "donatemax"))
|
||||
setType(BillingClient.SkuType.INAPP)
|
||||
}.build(), this)
|
||||
view.donations__google_android_market_donate_button.setOnClickListener {
|
||||
val sku = skus?.getOrNull(googleSpinner.selectedItemPosition)
|
||||
if (sku == null) {
|
||||
openDialog(R.string.donations__google_android_market_not_supported_title,
|
||||
R.string.donations__google_android_market_not_supported)
|
||||
} else billingClient.launchBillingFlow(requireActivity(), BillingFlowParams.newBuilder()
|
||||
.setSkuDetails(sku).build())
|
||||
if (sku != null) billingClient.launchBillingFlow(requireActivity(), BillingFlowParams.newBuilder().apply {
|
||||
setSkuDetails(sku)
|
||||
}.build()) else SmartSnackbar.make(R.string.donations__google_android_market_not_supported).show()
|
||||
}
|
||||
@Suppress("ConstantConditionIf")
|
||||
if (BuildConfig.DONATIONS) (view.donations__more_stub.inflate() as Button)
|
||||
.setOnClickListener { requireContext().launchUrl("https://mygod.be/donate/") }
|
||||
}
|
||||
|
||||
private fun openDialog(@StringRes title: Int, @StringRes message: Int) {
|
||||
val fragmentManager = fragmentManager
|
||||
if (fragmentManager == null) SmartSnackbar.make(message).show() else try {
|
||||
MessageDialogFragment().withArg(MessageArg(title, message)).show(fragmentManager, "MessageDialogFragment")
|
||||
} catch (e: IllegalStateException) {
|
||||
SmartSnackbar.make(message).show()
|
||||
override fun onSkuDetailsResponse(billingResult: BillingResult?, skuDetailsList: MutableList<SkuDetails>?) {
|
||||
if (billingResult?.responseCode == BillingClient.BillingResponseCode.OK) skus = skuDetailsList else {
|
||||
Timber.e("onSkuDetailsResponse: ${billingResult?.responseCode}")
|
||||
SmartSnackbar.make(R.string.donations__google_android_market_not_supported).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBillingServiceDisconnected() {
|
||||
skus = null
|
||||
billingClient = BillingClient.newBuilder(context ?: return).setListener(this).build()
|
||||
.also { it.startConnection(this) }
|
||||
}
|
||||
|
||||
override fun onBillingSetupFinished(responseCode: Int) {
|
||||
if (responseCode == BillingClient.BillingResponse.OK) {
|
||||
billingClient.querySkuDetailsAsync(
|
||||
SkuDetailsParams.newBuilder().apply {
|
||||
setSkusList(listOf("donate001", "donate002", "donate005", "donate010", "donate020", "donate050",
|
||||
"donate100", "donate200", "donatemax"))
|
||||
setType(BillingClient.SkuType.INAPP)
|
||||
}.build(), this)
|
||||
} else Timber.e("onBillingSetupFinished: $responseCode")
|
||||
}
|
||||
|
||||
override fun onSkuDetailsResponse(responseCode: Int, skuDetailsList: MutableList<SkuDetails>?) {
|
||||
if (responseCode == BillingClient.BillingResponse.OK) skus = skuDetailsList
|
||||
else Timber.e("onSkuDetailsResponse: $responseCode")
|
||||
}
|
||||
|
||||
override fun onPurchasesUpdated(responseCode: Int, purchases: MutableList<Purchase>?) {
|
||||
if (responseCode == BillingClient.BillingResponse.OK && purchases != null) {
|
||||
// directly consume in-app purchase, so that people can donate multiple times
|
||||
purchases.forEach { billingClient.consumeAsync(it.purchaseToken, this) }
|
||||
} else Timber.e("onPurchasesUpdated: $responseCode")
|
||||
}
|
||||
|
||||
override fun onConsumeResponse(responseCode: Int, purchaseToken: String?) {
|
||||
if (responseCode == BillingClient.BillingResponse.OK) {
|
||||
openDialog(R.string.donations__thanks_dialog_title, R.string.donations__thanks_dialog)
|
||||
dismissAllowingStateLoss()
|
||||
} else Timber.e("onConsumeResponse: $responseCode")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package be.mygod.vpnhotspot
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.Gravity
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
@@ -17,13 +16,10 @@ import be.mygod.vpnhotspot.databinding.ActivityMainBinding
|
||||
import be.mygod.vpnhotspot.manage.TetheringFragment
|
||||
import be.mygod.vpnhotspot.util.ServiceForegroundConnector
|
||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationMenuView
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import q.rorbin.badgeview.QBadgeView
|
||||
|
||||
class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemSelectedListener {
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
private lateinit var badge: QBadgeView
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -31,15 +27,15 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS
|
||||
binding.lifecycleOwner = this
|
||||
binding.navigation.setOnNavigationItemSelectedListener(this)
|
||||
if (savedInstanceState == null) displayFragment(TetheringFragment())
|
||||
badge = QBadgeView(this)
|
||||
badge.bindTarget((binding.navigation.getChildAt(0) as BottomNavigationMenuView).getChildAt(1))
|
||||
badge.badgeBackgroundColor = ContextCompat.getColor(this, R.color.colorSecondary)
|
||||
badge.badgeTextColor = ContextCompat.getColor(this, R.color.primary_text_default_material_light)
|
||||
badge.badgeGravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL
|
||||
badge.setGravityOffset(16f, 0f, true)
|
||||
val model = ViewModelProviders.of(this).get<ClientViewModel>()
|
||||
if (RepeaterService.supported) ServiceForegroundConnector(this, model, RepeaterService::class)
|
||||
model.clients.observe(this, Observer { badge.badgeNumber = it.size })
|
||||
model.clients.observe(this, Observer {
|
||||
if (it.isNotEmpty()) binding.navigation.showBadge(R.id.navigation_clients).apply {
|
||||
backgroundColor = ContextCompat.getColor(this@MainActivity, R.color.colorSecondary)
|
||||
badgeTextColor = ContextCompat.getColor(this@MainActivity, R.color.primary_text_default_material_light)
|
||||
number = it.size
|
||||
} else binding.navigation.removeBadge(R.id.navigation_clients)
|
||||
})
|
||||
SmartSnackbar.Register(lifecycle, binding.fragmentHolder)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user