Update dependencies

This commit is contained in:
Mygod
2020-01-19 14:14:21 +08:00
parent 0e527de3b9
commit 006e4e47c0
21 changed files with 131 additions and 160 deletions

View File

@@ -12,7 +12,7 @@ buildscript {
} }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.5.3' classpath 'com.android.tools.build:gradle:4.0.0-alpha08'
classpath 'com.github.ben-manes:gradle-versions-plugin:0.27.0' classpath 'com.github.ben-manes:gradle-versions-plugin:0.27.0'
classpath 'com.google.gms:google-services:4.3.3' classpath 'com.google.gms:google-services:4.3.3'
classpath 'io.fabric.tools:gradle:1.31.2' classpath 'io.fabric.tools:gradle:1.31.2'

Binary file not shown.

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-all.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

29
gradlew vendored
View File

@@ -154,19 +154,19 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
else else
eval `echo args$i`="\"$arg\"" eval `echo args$i`="\"$arg\""
fi fi
i=$((i+1)) i=`expr $i + 1`
done done
case $i in case $i in
(0) set -- ;; 0) set -- ;;
(1) set -- "$args0" ;; 1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;; 2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;; 3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;; 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac esac
fi fi
@@ -175,14 +175,9 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " " echo " "
} }
APP_ARGS=$(save "$@") APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules # Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@" exec "$JAVACMD" "$@"

View File

@@ -11,7 +11,7 @@ if (!getGradle().getStartParameter().getTaskRequests().toString().contains("Fdro
def javaVersion = JavaVersion.VERSION_1_8 def javaVersion = JavaVersion.VERSION_1_8
def aux = [ def aux = [
'com.crashlytics.sdk.android:crashlytics:2.10.1', 'com.crashlytics.sdk.android:crashlytics:2.10.1',
'com.google.firebase:firebase-analytics:17.2.1', 'com.google.firebase:firebase-analytics:17.2.2',
] ]
def lifecycleVersion = '2.2.0-rc03' def lifecycleVersion = '2.2.0-rc03'
def roomVersion = '2.2.3' def roomVersion = '2.2.3'
@@ -19,6 +19,7 @@ def roomVersion = '2.2.3'
android { android {
compileSdkVersion 29 compileSdkVersion 29
compileOptions { compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility javaVersion sourceCompatibility javaVersion
targetCompatibility javaVersion targetCompatibility javaVersion
} }
@@ -37,6 +38,10 @@ android {
] ]
buildConfigField "boolean", "DONATIONS", "true" buildConfigField "boolean", "DONATIONS", "true"
} }
buildFeatures {
dataBinding = true
viewBinding = true
}
buildTypes { buildTypes {
debug { debug {
pseudoLocalesEnabled true pseudoLocalesEnabled true
@@ -47,7 +52,6 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
} }
dataBinding.enabled = true
flavorDimensions("freedom") flavorDimensions("freedom")
packagingOptions.exclude '**/*.kotlin_*' packagingOptions.exclude '**/*.kotlin_*'
productFlavors { productFlavors {
@@ -73,22 +77,23 @@ androidExtensions {
} }
dependencies { dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.4'
kapt "androidx.room:room-compiler:$roomVersion" kapt "androidx.room:room-compiler:$roomVersion"
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.browser:browser:1.2.0' implementation 'androidx.browser:browser:1.2.0'
implementation 'androidx.core:core-ktx:1.1.0' implementation 'androidx.core:core-ktx:1.1.0'
implementation 'androidx.emoji:emoji:1.0.0' implementation 'androidx.emoji:emoji:1.0.0'
implementation 'androidx.fragment:fragment-ktx:1.2.0-rc05'
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion" implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion" implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion" implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion" implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion"
implementation 'androidx.preference:preference:1.1.0' implementation 'androidx.preference:preference:1.1.0'
implementation "androidx.room:room-ktx:$roomVersion" implementation "androidx.room:room-ktx:$roomVersion"
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
implementation 'com.android.billingclient:billing-ktx:2.1.0' implementation 'com.android.billingclient:billing-ktx:2.1.0'
implementation 'com.github.topjohnwu.libsu:core:2.5.1' implementation 'com.github.topjohnwu.libsu:core:2.5.1'
implementation 'com.google.android.material:material:1.1.0-rc01' implementation 'com.google.android.material:material:1.1.0-rc02'
implementation 'com.google.zxing:core:3.4.0' implementation 'com.google.zxing:core:3.4.0'
implementation 'com.jakewharton.timber:timber:4.7.1' implementation 'com.jakewharton.timber:timber:4.7.1'
implementation 'com.linkedin.dexmaker:dexmaker:2.25.1' implementation 'com.linkedin.dexmaker:dexmaker:2.25.1'

View File

@@ -6,14 +6,13 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.Button import android.widget.Button
import android.widget.Spinner
import androidx.appcompat.app.AppCompatDialogFragment import androidx.appcompat.app.AppCompatDialogFragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.databinding.FragmentEbegBinding
import be.mygod.vpnhotspot.util.launchUrl import be.mygod.vpnhotspot.util.launchUrl
import be.mygod.vpnhotspot.widget.SmartSnackbar import be.mygod.vpnhotspot.widget.SmartSnackbar
import com.android.billingclient.api.* import com.android.billingclient.api.*
import kotlinx.android.synthetic.main.fragment_ebeg.view.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -65,11 +64,11 @@ class EBegFragment : AppCompatDialogFragment() {
} }
} }
private lateinit var googleSpinner: Spinner private lateinit var binding: FragmentEbegBinding
private var skus: List<SkuDetails>? = null private var skus: List<SkuDetails>? = null
set(value) { set(value) {
field = value field = value
googleSpinner.apply { binding.donationsGoogleAndroidMarketSpinner.apply {
val adapter = ArrayAdapter(context ?: return, android.R.layout.simple_spinner_item, val adapter = ArrayAdapter(context ?: return, android.R.layout.simple_spinner_item,
value?.map { it.price } ?: listOf("")) value?.map { it.price } ?: listOf(""))
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
@@ -77,12 +76,13 @@ class EBegFragment : AppCompatDialogFragment() {
} }
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
inflater.inflate(R.layout.fragment_ebeg, container, false) binding = FragmentEbegBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
dialog!!.setTitle(R.string.settings_misc_donate) dialog!!.setTitle(R.string.settings_misc_donate)
googleSpinner = view.donations__google_android_market_spinner
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
billingClient.querySkuDetails(SkuDetailsParams.newBuilder().apply { billingClient.querySkuDetails(SkuDetailsParams.newBuilder().apply {
setSkusList(listOf("donate001", "donate002", "donate005", "donate010", "donate020", "donate050", setSkusList(listOf("donate001", "donate002", "donate005", "donate010", "donate020", "donate050",
@@ -95,14 +95,15 @@ class EBegFragment : AppCompatDialogFragment() {
} }
} }
} }
view.donations__google_android_market_donate_button.setOnClickListener { binding.donationsGoogleAndroidMarketDonateButton.setOnClickListener {
val sku = skus?.getOrNull(googleSpinner.selectedItemPosition) val sku = skus?.getOrNull(binding.donationsGoogleAndroidMarketSpinner.selectedItemPosition)
if (sku != null) billingClient.launchBillingFlow(requireActivity(), BillingFlowParams.newBuilder().apply { if (sku != null) billingClient.launchBillingFlow(requireActivity(), BillingFlowParams.newBuilder().apply {
setSkuDetails(sku) setSkuDetails(sku)
}.build()) else SmartSnackbar.make(R.string.donations__google_android_market_not_supported).show() }.build()) else SmartSnackbar.make(R.string.donations__google_android_market_not_supported).show()
} }
@Suppress("ConstantConditionIf") @Suppress("ConstantConditionIf")
if (BuildConfig.DONATIONS) (view.donations__more_stub.inflate() as Button) if (BuildConfig.DONATIONS) (binding.donationsMoreStub.inflate() as Button).setOnClickListener {
.setOnClickListener { requireContext().launchUrl("https://mygod.be/donate/") } requireContext().launchUrl("https://mygod.be/donate/")
}
} }
} }

View File

@@ -3,12 +3,10 @@ package be.mygod.vpnhotspot
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.get
import androidx.lifecycle.observe import androidx.lifecycle.observe
import be.mygod.vpnhotspot.client.ClientViewModel import be.mygod.vpnhotspot.client.ClientViewModel
import be.mygod.vpnhotspot.client.ClientsFragment import be.mygod.vpnhotspot.client.ClientsFragment
@@ -22,16 +20,15 @@ import com.google.android.material.bottomnavigation.BottomNavigationView
import java.net.Inet4Address import java.net.Inet4Address
class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemSelectedListener { class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemSelectedListener {
private lateinit var binding: ActivityMainBinding lateinit var binding: ActivityMainBinding
val provider by lazy { ViewModelProvider(this) }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main) binding = ActivityMainBinding.inflate(layoutInflater)
binding.lifecycleOwner = this setContentView(binding.root)
binding.navigation.setOnNavigationItemSelectedListener(this) binding.navigation.setOnNavigationItemSelectedListener(this)
if (savedInstanceState == null) displayFragment(TetheringFragment()) if (savedInstanceState == null) displayFragment(TetheringFragment())
val model = provider.get<ClientViewModel>() val model by viewModels<ClientViewModel>()
if (RepeaterService.supported) ServiceForegroundConnector(this, model, RepeaterService::class) if (RepeaterService.supported) ServiceForegroundConnector(this, model, RepeaterService::class)
model.clients.observe(this) { clients -> model.clients.observe(this) { clients ->
val count = clients.count { val count = clients.count {

View File

@@ -5,8 +5,6 @@ import android.os.Build
import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.net.Routing import be.mygod.vpnhotspot.net.Routing
import be.mygod.vpnhotspot.net.wifi.WifiDoubleLock import be.mygod.vpnhotspot.net.wifi.WifiDoubleLock
import be.mygod.vpnhotspot.util.putIfAbsentCompat
import be.mygod.vpnhotspot.util.removeCompat
import be.mygod.vpnhotspot.widget.SmartSnackbar import be.mygod.vpnhotspot.widget.SmartSnackbar
import timber.log.Timber import timber.log.Timber
@@ -56,7 +54,7 @@ abstract class RoutingManager(private val caller: Any, val downstream: String, p
val started get() = active[downstream] === this val started get() = active[downstream] === this
private var routing: Routing? = null private var routing: Routing? = null
fun start() = when (val other = active.putIfAbsentCompat(downstream, this)) { fun start() = when (val other = active.putIfAbsent(downstream, this)) {
null -> { null -> {
if (isWifi) WifiDoubleLock.acquire(this) if (isWifi) WifiDoubleLock.acquire(this)
initRouting() initRouting()
@@ -85,7 +83,7 @@ abstract class RoutingManager(private val caller: Any, val downstream: String, p
protected abstract fun Routing.configure() protected abstract fun Routing.configure()
fun stop() { fun stop() {
if (active.removeCompat(downstream, this)) { if (active.remove(downstream, this)) {
if (isWifi) WifiDoubleLock.release(this) if (isWifi) WifiDoubleLock.release(this)
routing?.revert() routing?.revert()
} }

View File

@@ -12,12 +12,11 @@ import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.EditText import android.widget.EditText
import androidx.activity.viewModels
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.databinding.BaseObservable import androidx.databinding.BaseObservable
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.get
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.observe import androidx.lifecycle.observe
import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.DefaultItemAnimator
@@ -38,7 +37,6 @@ import be.mygod.vpnhotspot.room.ClientStats
import be.mygod.vpnhotspot.room.TrafficRecord import be.mygod.vpnhotspot.room.TrafficRecord
import be.mygod.vpnhotspot.room.macToString import be.mygod.vpnhotspot.room.macToString
import be.mygod.vpnhotspot.util.SpanFormatter import be.mygod.vpnhotspot.util.SpanFormatter
import be.mygod.vpnhotspot.util.computeIfAbsentCompat
import be.mygod.vpnhotspot.util.toPluralInt import be.mygod.vpnhotspot.util.toPluralInt
import be.mygod.vpnhotspot.widget.SmartSnackbar import be.mygod.vpnhotspot.widget.SmartSnackbar
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
@@ -182,7 +180,7 @@ class ClientsFragment : Fragment() {
val client = getItem(position) val client = getItem(position)
holder.binding.client = client holder.binding.client = client
holder.binding.rate = holder.binding.rate =
rates.computeIfAbsentCompat(Pair(client.iface, client.mac)) { TrafficRate() } rates.computeIfAbsent(Pair(client.iface, client.mac)) { TrafficRate() }
holder.binding.executePendingBindings() holder.binding.executePendingBindings()
} }
@@ -197,7 +195,7 @@ class ClientsFragment : Fragment() {
check(newRecord.receivedPackets == oldRecord.receivedPackets) check(newRecord.receivedPackets == oldRecord.receivedPackets)
check(newRecord.receivedBytes == oldRecord.receivedBytes) check(newRecord.receivedBytes == oldRecord.receivedBytes)
} else { } else {
val rate = rates.computeIfAbsentCompat(Pair(newRecord.downstream, newRecord.mac)) { TrafficRate() } val rate = rates.computeIfAbsent(Pair(newRecord.downstream, newRecord.mac)) { TrafficRate() }
if (rate.send < 0 || rate.receive < 0) { if (rate.send < 0 || rate.receive < 0) {
rate.send = 0 rate.send = 0
rate.receive = 0 rate.receive = 0
@@ -215,8 +213,7 @@ class ClientsFragment : Fragment() {
private var rates = HashMap<Pair<String, Long>, TrafficRate>() private var rates = HashMap<Pair<String, Long>, TrafficRate>()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_clients, container, false) binding = FragmentClientsBinding.inflate(inflater, container, false)
binding.lifecycleOwner = this
binding.clients.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false) binding.clients.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
binding.clients.itemAnimator = DefaultItemAnimator() binding.clients.itemAnimator = DefaultItemAnimator()
binding.clients.adapter = adapter binding.clients.adapter = adapter
@@ -224,7 +221,7 @@ class ClientsFragment : Fragment() {
binding.swipeRefresher.setOnRefreshListener { binding.swipeRefresher.setOnRefreshListener {
IpNeighbourMonitor.instance?.flush() IpNeighbourMonitor.instance?.flush()
} }
(activity as MainActivity).provider.get<ClientViewModel>().clients.observe(this) { (activity as MainActivity).viewModels<ClientViewModel>().value.clients.observe(this) {
adapter.submitList(it.toMutableList()) adapter.submitList(it.toMutableList())
} }
return binding.root return binding.root

View File

@@ -20,9 +20,8 @@ import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.databinding.BaseObservable import androidx.databinding.BaseObservable
import androidx.databinding.Bindable import androidx.databinding.Bindable
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.get
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import be.mygod.vpnhotspot.* import be.mygod.vpnhotspot.*
import be.mygod.vpnhotspot.databinding.ListitemRepeaterBinding import be.mygod.vpnhotspot.databinding.ListitemRepeaterBinding
@@ -136,7 +135,7 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
internal var binder: RepeaterService.Binder? = null internal var binder: RepeaterService.Binder? = null
private var p2pInterface: String? = null private var p2pInterface: String? = null
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
private val holder = ViewModelProvider(parent).get<ConfigHolder>() private val holder by parent.viewModels<ConfigHolder>()
override fun bindTo(viewHolder: RecyclerView.ViewHolder) { override fun bindTo(viewHolder: RecyclerView.ViewHolder) {
(viewHolder as ViewHolder).binding.data = data (viewHolder as ViewHolder).binding.data = data

View File

@@ -12,7 +12,6 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.DefaultItemAnimator
@@ -31,7 +30,6 @@ import be.mygod.vpnhotspot.util.ServiceForegroundConnector
import be.mygod.vpnhotspot.util.broadcastReceiver import be.mygod.vpnhotspot.util.broadcastReceiver
import be.mygod.vpnhotspot.util.isNotGone import be.mygod.vpnhotspot.util.isNotGone
import be.mygod.vpnhotspot.widget.SmartSnackbar import be.mygod.vpnhotspot.widget.SmartSnackbar
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import timber.log.Timber import timber.log.Timber
@@ -107,8 +105,8 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
} }
private fun updateMonitorList(canMonitor: List<String> = emptyList()) { private fun updateMonitorList(canMonitor: List<String> = emptyList()) {
val activity = activity val activity = activity as? MainActivity
val item = activity?.toolbar?.menu?.findItem(R.id.monitor) ?: return // assuming no longer foreground val item = activity?.binding?.toolbar?.menu?.findItem(R.id.monitor) ?: return // assuming no longer foreground
item.isNotGone = canMonitor.isNotEmpty() item.isNotGone = canMonitor.isNotEmpty()
item.subMenu.apply { item.subMenu.apply {
clear() clear()
@@ -157,14 +155,13 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_tethering, container, false) binding = FragmentTetheringBinding.inflate(inflater, container, false)
binding.lifecycleOwner = this
binding.interfaces.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false) binding.interfaces.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
binding.interfaces.itemAnimator = DefaultItemAnimator() binding.interfaces.itemAnimator = DefaultItemAnimator()
binding.interfaces.adapter = adapter binding.interfaces.adapter = adapter
adapter.update(emptyList(), emptyList(), emptyList()) adapter.update(emptyList(), emptyList(), emptyList())
ServiceForegroundConnector(this, this, TetheringService::class) ServiceForegroundConnector(this, this, TetheringService::class)
requireActivity().toolbar.apply { (activity as MainActivity).binding.toolbar.apply {
inflateMenu(R.menu.toolbar_tethering) inflateMenu(R.menu.toolbar_tethering)
setOnMenuItemClickListener(this@TetheringFragment) setOnMenuItemClickListener(this@TetheringFragment)
} }
@@ -173,7 +170,7 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
requireActivity().toolbar.apply { (activity as MainActivity).binding.toolbar.apply {
menu.clear() menu.clear()
setOnMenuItemClickListener(null) setOnMenuItemClickListener(null)
} }

View File

@@ -11,7 +11,6 @@ import be.mygod.vpnhotspot.net.monitor.TrafficRecorder
import be.mygod.vpnhotspot.net.monitor.UpstreamMonitor import be.mygod.vpnhotspot.net.monitor.UpstreamMonitor
import be.mygod.vpnhotspot.room.AppDatabase import be.mygod.vpnhotspot.room.AppDatabase
import be.mygod.vpnhotspot.util.RootSession import be.mygod.vpnhotspot.util.RootSession
import be.mygod.vpnhotspot.util.computeIfAbsentCompat
import be.mygod.vpnhotspot.widget.SmartSnackbar import be.mygod.vpnhotspot.widget.SmartSnackbar
import timber.log.Timber import timber.log.Timber
import java.io.IOException import java.io.IOException
@@ -235,7 +234,7 @@ class Routing(private val caller: Any, private val downstream: String) : IpNeigh
AppDatabase.instance.clientRecordDao.lookupOrDefaultBlocking(neighbour.lladdr).blocked) continue AppDatabase.instance.clientRecordDao.lookupOrDefaultBlocking(neighbour.lladdr).blocked) continue
toRemove.remove(neighbour.ip) toRemove.remove(neighbour.ip)
try { try {
clients.computeIfAbsentCompat(neighbour.ip) { Client(neighbour.ip, neighbour.lladdr) } clients.computeIfAbsent(neighbour.ip) { Client(neighbour.ip, neighbour.lladdr) }
} catch (e: Exception) { } catch (e: Exception) {
Timber.w(e) Timber.w(e)
SmartSnackbar.make(e).show() SmartSnackbar.make(e).show()

View File

@@ -9,7 +9,6 @@ import be.mygod.vpnhotspot.room.TrafficRecord
import be.mygod.vpnhotspot.util.Event2 import be.mygod.vpnhotspot.util.Event2
import be.mygod.vpnhotspot.util.RootSession import be.mygod.vpnhotspot.util.RootSession
import be.mygod.vpnhotspot.util.parseNumericAddress import be.mygod.vpnhotspot.util.parseNumericAddress
import be.mygod.vpnhotspot.util.putIfAbsentCompat
import be.mygod.vpnhotspot.widget.SmartSnackbar import be.mygod.vpnhotspot.widget.SmartSnackbar
import timber.log.Timber import timber.log.Timber
import java.net.InetAddress import java.net.InetAddress
@@ -29,7 +28,7 @@ object TrafficRecorder {
AppDatabase.instance.trafficRecordDao.insert(record) AppDatabase.instance.trafficRecordDao.insert(record)
synchronized(this) { synchronized(this) {
DebugHelper.log(TAG, "Registering $ip%$downstream") DebugHelper.log(TAG, "Registering $ip%$downstream")
check(records.putIfAbsentCompat(Pair(ip, downstream), record) == null) check(records.putIfAbsent(Pair(ip, downstream), record) == null)
scheduleUpdateLocked() scheduleUpdateLocked()
} }
} }

View File

@@ -21,12 +21,12 @@ import be.mygod.vpnhotspot.AlertDialogFragment
import be.mygod.vpnhotspot.App.Companion.app import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.R import be.mygod.vpnhotspot.R
import be.mygod.vpnhotspot.RepeaterService import be.mygod.vpnhotspot.RepeaterService
import be.mygod.vpnhotspot.databinding.DialogWifiApBinding
import be.mygod.vpnhotspot.util.QRCodeDialog import be.mygod.vpnhotspot.util.QRCodeDialog
import be.mygod.vpnhotspot.util.toByteArray import be.mygod.vpnhotspot.util.toByteArray
import be.mygod.vpnhotspot.util.toParcelable import be.mygod.vpnhotspot.util.toParcelable
import be.mygod.vpnhotspot.widget.SmartSnackbar import be.mygod.vpnhotspot.widget.SmartSnackbar
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.dialog_wifi_ap.view.*
import java.nio.charset.Charset import java.nio.charset.Charset
/** /**
@@ -72,7 +72,7 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
} }
} }
private lateinit var dialogView: View private lateinit var dialogView: DialogWifiApBinding
private lateinit var bandOptions: MutableList<BandOption> private lateinit var bandOptions: MutableList<BandOption>
private var started = false private var started = false
private val selectedSecurity get() = private val selectedSecurity get() =
@@ -92,14 +92,14 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
override fun AlertDialog.Builder.prepare(listener: DialogInterface.OnClickListener) { override fun AlertDialog.Builder.prepare(listener: DialogInterface.OnClickListener) {
val activity = requireActivity() val activity = requireActivity()
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
dialogView = activity.layoutInflater.inflate(R.layout.dialog_wifi_ap, null) dialogView = DialogWifiApBinding.inflate(activity.layoutInflater)
setView(dialogView) setView(dialogView.root)
if (!arg.readOnly) setPositiveButton(R.string.wifi_save, listener) if (!arg.readOnly) setPositiveButton(R.string.wifi_save, listener)
setNegativeButton(R.string.donations__button_close, null) setNegativeButton(R.string.donations__button_close, null)
dialogView.toolbar.inflateMenu(R.menu.toolbar_configuration) dialogView.toolbar.inflateMenu(R.menu.toolbar_configuration)
dialogView.toolbar.setOnMenuItemClickListener(this@WifiApDialogFragment) dialogView.toolbar.setOnMenuItemClickListener(this@WifiApDialogFragment)
if (!arg.readOnly) dialogView.ssid.addTextChangedListener(this@WifiApDialogFragment) if (!arg.readOnly) dialogView.ssid.addTextChangedListener(this@WifiApDialogFragment)
if (arg.p2pMode) dialogView.security_wrapper.isGone = true else dialogView.security.apply { if (arg.p2pMode) dialogView.securityWrapper.isGone = true else dialogView.security.apply {
adapter = ArrayAdapter(activity, android.R.layout.simple_spinner_item, 0, adapter = ArrayAdapter(activity, android.R.layout.simple_spinner_item, 0,
WifiConfiguration.KeyMgmt.strings).apply { WifiConfiguration.KeyMgmt.strings).apply {
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
@@ -107,7 +107,7 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
onItemSelectedListener = object : AdapterView.OnItemSelectedListener { onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) = error("Must select something") override fun onNothingSelected(parent: AdapterView<*>?) = error("Must select something")
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
dialogView.password_wrapper.isGone = position == WifiConfiguration.KeyMgmt.NONE dialogView.passwordWrapper.isGone = position == WifiConfiguration.KeyMgmt.NONE
} }
} }
} }
@@ -133,7 +133,7 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
if (Build.VERSION.SDK_INT < 23) { if (Build.VERSION.SDK_INT < 23) {
setSelection(bandOptions.indexOfFirst { it.apChannel == RepeaterService.operatingChannel }) setSelection(bandOptions.indexOfFirst { it.apChannel == RepeaterService.operatingChannel })
} }
} else dialogView.band_wrapper.isGone = true } else dialogView.bandWrapper.isGone = true
populateFromConfiguration(arg.configuration) populateFromConfiguration(arg.configuration)
} }
@@ -165,7 +165,7 @@ class WifiApDialogFragment : AlertDialogFragment<WifiApDialogFragment.Arg, WifiA
WifiConfiguration.KeyMgmt.WPA_PSK, WPA2_PSK -> dialogView.password.length() >= 8 WifiConfiguration.KeyMgmt.WPA_PSK, WPA2_PSK -> dialogView.password.length() >= 8
else -> true // do not try to validate else -> true // do not try to validate
} }
dialogView.password_wrapper.error = if (passwordValid) null else { dialogView.passwordWrapper.error = if (passwordValid) null else {
requireContext().getString(R.string.credentials_password_too_short) requireContext().getString(R.string.credentials_password_too_short)
} }
(dialog as? AlertDialog)?.getButton(DialogInterface.BUTTON_POSITIVE)?.isEnabled = ssidValid && passwordValid (dialog as? AlertDialog)?.getButton(DialogInterface.BUTTON_POSITIVE)?.isEnabled = ssidValid && passwordValid

View File

@@ -123,15 +123,3 @@ var MenuItem.isNotGone: Boolean
isVisible = value isVisible = value
isEnabled = value isEnabled = value
} }
fun <K, V> MutableMap<K, V>.computeIfAbsentCompat(key: K, value: () -> V) = if (Build.VERSION.SDK_INT >= 24)
computeIfAbsent(key) { value() } else this[key] ?: value().also { put(key, it) }
fun <K, V> MutableMap<K, V>.putIfAbsentCompat(key: K, value: V) = if (Build.VERSION.SDK_INT >= 24)
putIfAbsent(key, value) else this[key] ?: put(key, value)
fun <K, V> MutableMap<K, V>.removeCompat(key: K, value: V) = if (Build.VERSION.SDK_INT >= 24) remove(key, value) else {
val curValue = get(key)
if (curValue === value && (curValue != null || containsKey(key))) {
remove(key)
true
} else false
}

View File

@@ -1,50 +1,46 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="be.mygod.vpnhotspot.MainActivity">
<LinearLayout <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="?attr/actionBarSize"
android:orientation="vertical" android:background="?attr/colorPrimary"
tools:context="be.mygod.vpnhotspot.MainActivity"> android:elevation="4dp"
app:title="@string/app_name"/>
<com.google.android.material.appbar.MaterialToolbar <androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/toolbar" android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<FrameLayout
android:id="@+id/fragmentHolder"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="match_parent"/>
android:background="?attr/colorPrimary"
android:elevation="4dp"
app:title="@string/app_name"/>
<androidx.coordinatorlayout.widget.CoordinatorLayout </androidx.coordinatorlayout.widget.CoordinatorLayout>
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<FrameLayout <com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/fragmentHolder" android:id="@+id/navigation"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"/> android:layout_height="wrap_content"
android:background="?android:attr/windowBackground"
app:menu="@menu/navigation"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout> <View
android:id="@+id/line"
android:layout_width="match_parent"
android:layout_height="0.1px"
android:background="?android:attr/navigationBarColor"
android:elevation="9dp"
tools:ignore="PxUsage"/>
<com.google.android.material.bottomnavigation.BottomNavigationView </LinearLayout>
android:id="@+id/navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/windowBackground"
app:menu="@menu/navigation"/>
<View
android:id="@+id/line"
android:layout_width="match_parent"
android:layout_height="0.1px"
android:background="?android:attr/navigationBarColor"
android:elevation="9dp"
tools:ignore="PxUsage"/>
</LinearLayout>
</layout>

View File

@@ -3,7 +3,8 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
tools:viewBindingIgnore="true">
<androidx.emoji.widget.EmojiEditText <androidx.emoji.widget.EmojiEditText
android:id="@android:id/edit" android:id="@android:id/edit"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -3,7 +3,8 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
tools:viewBindingIgnore="true">
<EditText <EditText
android:id="@android:id/edit" android:id="@android:id/edit"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -1,19 +1,16 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/swipeRefresher"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout <androidx.recyclerview.widget.RecyclerView
android:id="@+id/swipeRefresher" android:id="@+id/clients"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:clipToPadding="false"
<androidx.recyclerview.widget.RecyclerView android:scrollbars="vertical"
android:id="@+id/clients" tools:listitem="@layout/listitem_client"/>
android:layout_width="match_parent" </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
android:layout_height="match_parent"
android:clipToPadding="false"
android:scrollbars="vertical"
tools:listitem="@layout/listitem_client"/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</layout>

View File

@@ -1,6 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android" <Button
android:id="@+id/donations__more_donate_button" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" xmlns:tools="http://schemas.android.com/tools"
android:layout_height="wrap_content" android:id="@+id/donations__more_donate_button"
android:text="@string/settings_misc_donate_more"/> android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_misc_donate_more"
tools:viewBindingIgnore="true"/>

View File

@@ -1,12 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout <androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools"
<androidx.recyclerview.widget.RecyclerView android:id="@+id/interfaces"
android:id="@+id/interfaces" android:layout_width="match_parent"
android:layout_width="match_parent" android:layout_height="match_parent"
android:layout_height="match_parent" android:clipToPadding="false"
android:clipToPadding="false" android:scrollbars="vertical"
android:scrollbars="vertical" tools:listitem="@layout/listitem_interface"/>
tools:listitem="@layout/listitem_interface"/>
</layout>