VPN Hotspot 2.0: Client+ (#39)
Fix #13, #38. I don't have a lot of confidence that this would work very well for every device. Also here's an SQL command that hopefully somebody could make into the app for me: `SELECT TrafficRecord.mac, SUM(TrafficRecord.sentPackets), SUM(TrafficRecord.sentBytes), SUM(TrafficRecord.receivedPackets), SUM(TrafficRecord.receivedBytes) FROM TrafficRecord LEFT JOIN TrafficRecord AS Next ON TrafficRecord.id = Next.previousId WHERE Next.id IS NULL GROUP BY TrafficRecord.mac;`
This commit is contained in:
@@ -12,11 +12,8 @@ open class Event0 : ConcurrentHashMap<Any, () -> Unit>() {
|
||||
}
|
||||
|
||||
class StickyEvent0 : Event0() {
|
||||
override fun put(key: Any, value: () -> Unit): (() -> Unit)? {
|
||||
val result = super.put(key, value)
|
||||
if (result == null) value()
|
||||
return result
|
||||
}
|
||||
override fun put(key: Any, value: () -> Unit): (() -> Unit)? =
|
||||
super.put(key, value).also { if (it == null) value() }
|
||||
}
|
||||
|
||||
open class Event1<T> : ConcurrentHashMap<Any, (T) -> Unit>() {
|
||||
@@ -26,9 +23,12 @@ open class Event1<T> : ConcurrentHashMap<Any, (T) -> Unit>() {
|
||||
}
|
||||
|
||||
class StickyEvent1<T>(private val fire: () -> T) : Event1<T>() {
|
||||
override fun put(key: Any, value: (T) -> Unit): ((T) -> Unit)? {
|
||||
val result = super.put(key, value)
|
||||
if (result == null) value(fire())
|
||||
return result
|
||||
override fun put(key: Any, value: (T) -> Unit): ((T) -> Unit)? =
|
||||
super.put(key, value).also { if (it == null) value(fire()) }
|
||||
}
|
||||
|
||||
open class Event2<T1, T2> : ConcurrentHashMap<Any, (T1, T2) -> Unit>() {
|
||||
operator fun invoke(arg1: T1, arg2: T2) {
|
||||
for ((_, handler) in this) handler(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package be.mygod.vpnhotspot.util
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.HandlerThread
|
||||
import android.util.Log
|
||||
import androidx.core.os.postDelayed
|
||||
import be.mygod.vpnhotspot.App.Companion.app
|
||||
import com.crashlytics.android.Crashlytics
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.concurrent.withLock
|
||||
@@ -14,6 +16,8 @@ class RootSession : AutoCloseable {
|
||||
companion object {
|
||||
private const val TAG = "RootSession"
|
||||
|
||||
val handler = Handler(HandlerThread("$TAG-HandlerThread").apply { start() }.looper)
|
||||
|
||||
private val monitor = ReentrantLock()
|
||||
private fun onUnlock() {
|
||||
if (monitor.holdCount == 1) instance?.startTimeout()
|
||||
@@ -72,8 +76,10 @@ class RootSession : AutoCloseable {
|
||||
shell.close()
|
||||
if (instance == this) instance = null
|
||||
}
|
||||
private fun startTimeout() = app.handler.postDelayed(60 * 1000, this) { monitor.withLock { close() } }
|
||||
private fun haltTimeout() = app.handler.removeCallbacksAndMessages(this)
|
||||
private fun startTimeout() = handler.postDelayed(TimeUnit.MINUTES.toMillis(5), this) {
|
||||
monitor.withLock { close() }
|
||||
}
|
||||
private fun haltTimeout() = handler.removeCallbacksAndMessages(this)
|
||||
|
||||
/**
|
||||
* Don't care about the results, but still sync.
|
||||
@@ -95,11 +101,12 @@ class RootSession : AutoCloseable {
|
||||
}).exec()
|
||||
}
|
||||
fun exec(command: String) = checkOutput(command, execQuiet(command))
|
||||
fun execOut(command: String): String {
|
||||
fun execOutUnjoined(command: String): List<String> {
|
||||
val result = execQuiet(command)
|
||||
checkOutput(command, result, false)
|
||||
return result.out.joinToString("\n")
|
||||
return result.out
|
||||
}
|
||||
fun execOut(command: String): String = execOutUnjoined(command).joinToString("\n")
|
||||
|
||||
/**
|
||||
* This transaction is different from what you may have in mind since you can revert it after committing it.
|
||||
|
||||
@@ -1,20 +1,33 @@
|
||||
package be.mygod.vpnhotspot.util
|
||||
|
||||
import android.content.*
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.databinding.BindingAdapter
|
||||
import be.mygod.vpnhotspot.App.Companion.app
|
||||
import be.mygod.vpnhotspot.BuildConfig
|
||||
import be.mygod.vpnhotspot.R
|
||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||
import com.crashlytics.android.Crashlytics
|
||||
import java.net.InetAddress
|
||||
import java.net.NetworkInterface
|
||||
import java.net.SocketException
|
||||
|
||||
/**
|
||||
* This is a hack: we wrap longs around in 1 billion and such. Hopefully every language counts in base 10 and this works
|
||||
* marvelously for everybody.
|
||||
*/
|
||||
fun Long.toPluralInt(): Int {
|
||||
check(this >= 0) // please don't mess with me
|
||||
if (this <= Int.MAX_VALUE) return toInt()
|
||||
return (this % 1000000000).toInt() + 1000000000
|
||||
}
|
||||
|
||||
fun CharSequence?.onEmpty(otherwise: CharSequence): CharSequence = if (isNullOrEmpty()) otherwise else this!!
|
||||
|
||||
fun debugLog(tag: String?, message: String?) {
|
||||
if (BuildConfig.DEBUG) Log.d(tag, message)
|
||||
Crashlytics.log("$tag: $message")
|
||||
@@ -51,6 +64,19 @@ fun NetworkInterface.formatAddresses() =
|
||||
}))
|
||||
.joinToString("\n")
|
||||
|
||||
private val parseNumericAddress by lazy {
|
||||
// parseNumericAddressNoThrow is in dark grey list unfortunately
|
||||
InetAddress::class.java.getDeclaredMethod("parseNumericAddress", String::class.java).apply {
|
||||
isAccessible = true
|
||||
}
|
||||
}
|
||||
fun parseNumericAddress(address: String) = parseNumericAddress.invoke(null, address) as InetAddress
|
||||
fun parseNumericAddressNoThrow(address: String): InetAddress? = try {
|
||||
parseNumericAddress(address)
|
||||
} catch (_: IllegalArgumentException) {
|
||||
null
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for kotlin.concurrent.thread that silences uncaught exceptions.
|
||||
*/
|
||||
@@ -69,3 +95,6 @@ fun Context.stopAndUnbind(connection: ServiceConnection) {
|
||||
connection.onServiceDisconnected(null)
|
||||
unbindService(connection)
|
||||
}
|
||||
|
||||
fun <K, V> HashMap<K, V>.computeIfAbsentCompat(key: K, value: () -> V) = if (Build.VERSION.SDK_INT >= 26)
|
||||
computeIfAbsent(key) { value() } else this[key] ?: value().also { put(key, it) }
|
||||
|
||||
Reference in New Issue
Block a user