Merge branch 'v2.4' into q-beta

This commit is contained in:
Mygod
2019-05-09 14:03:16 +08:00
10 changed files with 74 additions and 91 deletions

View File

@@ -50,6 +50,7 @@ class App : Application() {
override fun onFailed(throwable: Throwable?) = Timber.d(throwable)
})
})
EBegFragment.init()
if (DhcpWorkaround.shouldEnable) DhcpWorkaround.enable(true)
}

View File

@@ -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")
}
}

View File

@@ -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
@@ -18,13 +17,10 @@ import be.mygod.vpnhotspot.manage.TetheringFragment
import be.mygod.vpnhotspot.net.wifi.WifiDoubleLock
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)
@@ -32,15 +28,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)
WifiDoubleLock.ActivityListener(this)
}