Merge branch 'v2.4' into q-beta
This commit is contained in:
@@ -128,6 +128,8 @@ Undocumented API list:
|
||||
* (since API 24) [`Landroid/net/ConnectivityManager;->getLastTetherError(Ljava/lang/String;)I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#122588)
|
||||
* (since API 24) [`Landroid/net/ConnectivityManager;->startTethering(IZLandroid/net/ConnectivityManager$OnStartTetheringCallback;Landroid/os/Handler;)V,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#122683)
|
||||
* (since API 24) [`Landroid/net/ConnectivityManager;->stopTethering(I)V,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#122685)
|
||||
* (since API 23) [`Landroid/net/wifi/WifiConfiguration;->apBand:I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#131003)
|
||||
* (since API 23) [`Landroid/net/wifi/WifiConfiguration;->apChannel:I,greylist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#131004)
|
||||
* [`Landroid/net/wifi/WifiManager;->getWifiApConfiguration()Landroid/net/wifi/WifiConfiguration;,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#131756)
|
||||
* [`Landroid/net/wifi/WifiManager;->setWifiApConfiguration(Landroid/net/wifi/WifiConfiguration;)Z,whitelist`](https://android.googlesource.com/platform/prebuilts/runtime/+/7cb2ccf/appcompat/hiddenapi-flags.csv#131825)
|
||||
* (deprecated since API 26) `Landroid/net/wifi/WifiManager;->setWifiApEnabled(Landroid/net/wifi/WifiConfiguration;Z)Z`
|
||||
|
||||
@@ -64,31 +64,30 @@ androidExtensions {
|
||||
}
|
||||
|
||||
def aux = [
|
||||
'com.crashlytics.sdk.android:crashlytics:2.9.9',
|
||||
'com.google.firebase:firebase-core:16.0.8',
|
||||
'com.crashlytics.sdk.android:crashlytics:2.10.0',
|
||||
'com.google.firebase:firebase-core:16.0.9',
|
||||
]
|
||||
def lifecycleVersion = '2.0.0'
|
||||
def roomVersion = '2.1.0-alpha07'
|
||||
def roomVersion = '2.1.0-beta01'
|
||||
dependencies {
|
||||
kapt "androidx.room:room-compiler:$roomVersion"
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation 'androidx.browser:browser:1.0.0'
|
||||
implementation 'androidx.core:core-ktx:1.0.1'
|
||||
implementation 'androidx.core:core-ktx:1.0.2'
|
||||
implementation 'androidx.emoji:emoji:1.0.0'
|
||||
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion"
|
||||
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion"
|
||||
implementation 'androidx.preference:preference:1.1.0-alpha04'
|
||||
implementation 'androidx.preference:preference:1.1.0-alpha05'
|
||||
implementation "androidx.room:room-ktx:$roomVersion"
|
||||
implementation 'com.android.billingclient:billing:1.2.2'
|
||||
implementation 'com.github.luongvo:BadgeView:1.1.5'
|
||||
implementation 'com.android.billingclient:billing:2.0.0'
|
||||
implementation 'com.github.topjohnwu.libsu:core:2.5.0'
|
||||
implementation 'com.google.android.material:material:1.0.0'
|
||||
implementation 'com.google.android.material:material:1.1.0-alpha06'
|
||||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||
implementation 'com.linkedin.dexmaker:dexmaker:2.25.0'
|
||||
implementation 'com.takisoft.preferencex:preferencex-simplemenu:1.0.0'
|
||||
implementation 'net.glxn.qrgen:android:2.0'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.1'
|
||||
for (dep in aux) {
|
||||
freedomImplementation dep
|
||||
|
||||
@@ -50,6 +50,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
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
android:orientation="vertical"
|
||||
tools:context="be.mygod.vpnhotspot.MainActivity">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -61,11 +61,9 @@
|
||||
<string name="donations__button_close">Закрыть</string>
|
||||
<string name="donations__description">Считаете это приложение полезным?\nПоддержите его разработку, отправив пожертвование разработчику!</string>
|
||||
<string name="donations__google_android_market">Google Play Store</string>
|
||||
<string name="donations__google_android_market_not_supported_title">In-App пожертвования не поддерживаются.</string>
|
||||
<string name="donations__google_android_market_not_supported">Пожертвования через приложение не поддерживаются. Google Play Store установлен правильно?</string>
|
||||
<string name="donations__google_android_market_description">Google взимает 30% комиссии с каждого пожертвования!</string>
|
||||
<string name="donations__google_android_market_donate_button">Пожертвовать!</string>
|
||||
<string name="donations__google_android_market_text">Сколько?</string>
|
||||
<string name="donations__thanks_dialog_title">Благодарю!</string>
|
||||
<string name="donations__thanks_dialog">Благодарю за пожертвование! Я очень это ценю!</string>
|
||||
</resources>
|
||||
|
||||
@@ -144,11 +144,9 @@
|
||||
<!-- Based on: https://github.com/PrivacyApps/donations/blob/747d36a18433c7e9329691054122a8ad337a62d2/Donations/src/main/res/values-zh/donations__strings.xml -->
|
||||
<string name="donations__button_close">关闭</string>
|
||||
<string name="donations__google_android_market">Google Play 商店</string>
|
||||
<string name="donations__google_android_market_not_supported_title">不支持 In-App 捐赠。</string>
|
||||
<string name="donations__google_android_market_not_supported">不支持 In-App 捐赠。你的 Google Play 商店是否安装正确了呢?</string>
|
||||
<string name="donations__google_android_market_donate_button">捐赠!</string>
|
||||
<string name="donations__google_android_market_text">捐赠多少?</string>
|
||||
<string name="donations__thanks_dialog_title">谢谢!</string>
|
||||
<string name="donations__thanks_dialog">谢谢捐赠!\n非常感谢您!</string>
|
||||
|
||||
<string name="donations__description">觉得此应用很有用?\n捐赠给该开发者以支持此应用的开发!</string>
|
||||
|
||||
@@ -153,11 +153,9 @@
|
||||
<string name="donations__button_close">Close</string>
|
||||
<string name="donations__description">Do you find this application useful?\nSupport its development by sending a donation to the developer!</string>
|
||||
<string name="donations__google_android_market">Google Play Store</string>
|
||||
<string name="donations__google_android_market_not_supported_title">In-App Donations are not supported.</string>
|
||||
<string name="donations__google_android_market_not_supported">In-App Donations are not supported. Is Google Play Store installed correctly?</string>
|
||||
<string name="donations__google_android_market_description">Google charges a fee of 30%</string>
|
||||
<string name="donations__google_android_market_donate_button">Donate!</string>
|
||||
<string name="donations__google_android_market_text">How much?</string>
|
||||
<string name="donations__thanks_dialog_title">Thanks!</string>
|
||||
<string name="donations__thanks_dialog">Thanks for donating!\nI really appreciate this!</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user