Add support for checking app updates
This commit is contained in:
@@ -6,26 +6,46 @@ import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import be.mygod.vpnhotspot.client.ClientViewModel
|
||||
import be.mygod.vpnhotspot.client.ClientsFragment
|
||||
import be.mygod.vpnhotspot.databinding.ActivityMainBinding
|
||||
import be.mygod.vpnhotspot.manage.TetheringFragment
|
||||
import be.mygod.vpnhotspot.net.IpNeighbour
|
||||
import be.mygod.vpnhotspot.net.wifi.WifiDoubleLock
|
||||
import be.mygod.vpnhotspot.util.AppUpdate
|
||||
import be.mygod.vpnhotspot.util.ServiceForegroundConnector
|
||||
import be.mygod.vpnhotspot.util.Services
|
||||
import be.mygod.vpnhotspot.util.UpdateChecker
|
||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||
import com.google.android.material.badge.BadgeDrawable
|
||||
import com.google.android.material.navigation.NavigationBarView
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.launch
|
||||
import java.net.Inet4Address
|
||||
|
||||
class MainActivity : AppCompatActivity(), NavigationBarView.OnItemSelectedListener {
|
||||
lateinit var binding: ActivityMainBinding
|
||||
private lateinit var updateItem: MenuItem
|
||||
private lateinit var updateBadge: BadgeDrawable
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
binding.navigation.setOnItemSelectedListener(this)
|
||||
val badge = binding.navigation.getOrCreateBadge(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)
|
||||
}
|
||||
updateItem = binding.navigation.menu.findItem(R.id.navigation_update)
|
||||
updateItem.isCheckable = false
|
||||
updateBadge = binding.navigation.getOrCreateBadge(R.id.navigation_update).apply {
|
||||
backgroundColor = ContextCompat.getColor(this@MainActivity, R.color.colorSecondary)
|
||||
badgeTextColor = ContextCompat.getColor(this@MainActivity, R.color.primary_text_default_material_light)
|
||||
}
|
||||
if (savedInstanceState == null) displayFragment(TetheringFragment())
|
||||
val model by viewModels<ClientViewModel>()
|
||||
lifecycle.addObserver(model)
|
||||
@@ -34,38 +54,57 @@ class MainActivity : AppCompatActivity(), NavigationBarView.OnItemSelectedListen
|
||||
val count = clients.count {
|
||||
it.ip.any { (ip, state) -> ip is Inet4Address && state == IpNeighbour.State.VALID }
|
||||
}
|
||||
if (count > 0) binding.navigation.getOrCreateBadge(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 = count
|
||||
} else binding.navigation.removeBadge(R.id.navigation_clients)
|
||||
badge.isVisible = count > 0
|
||||
badge.number = count
|
||||
}
|
||||
SmartSnackbar.Register(binding.fragmentHolder)
|
||||
WifiDoubleLock.ActivityListener(this)
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
onAppUpdateAvailable(null)
|
||||
UpdateChecker.check().collect(this@MainActivity::onAppUpdateAvailable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var lastUpdate: AppUpdate? = null
|
||||
private fun onAppUpdateAvailable(update: AppUpdate?) {
|
||||
lastUpdate = update
|
||||
updateItem.isVisible = update != null
|
||||
if (update == null) return
|
||||
updateItem.isEnabled = update.downloaded != false
|
||||
updateItem.setIcon(when (update.downloaded) {
|
||||
null -> R.drawable.ic_action_update
|
||||
false -> R.drawable.ic_file_downloading
|
||||
true -> R.drawable.ic_action_autorenew
|
||||
})
|
||||
updateItem.title = update.message ?: "Update"
|
||||
updateBadge.isVisible = when (val days = update.stalenessDays) {
|
||||
null -> false
|
||||
else -> {
|
||||
if (days > 0) updateBadge.number = days else updateBadge.clearNumber()
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNavigationItemSelected(item: MenuItem) = when (item.itemId) {
|
||||
R.id.navigation_clients -> {
|
||||
if (!item.isChecked) {
|
||||
item.isChecked = true
|
||||
displayFragment(ClientsFragment())
|
||||
}
|
||||
displayFragment(ClientsFragment())
|
||||
true
|
||||
}
|
||||
R.id.navigation_tethering -> {
|
||||
if (!item.isChecked) {
|
||||
item.isChecked = true
|
||||
displayFragment(TetheringFragment())
|
||||
}
|
||||
displayFragment(TetheringFragment())
|
||||
true
|
||||
}
|
||||
R.id.navigation_settings -> {
|
||||
if (!item.isChecked) {
|
||||
item.isChecked = true
|
||||
displayFragment(SettingsPreferenceFragment())
|
||||
}
|
||||
displayFragment(SettingsPreferenceFragment())
|
||||
true
|
||||
}
|
||||
R.id.navigation_update -> {
|
||||
lastUpdate!!.updateForResult(this, 1)
|
||||
false
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import be.mygod.vpnhotspot.App.Companion.app
|
||||
import be.mygod.vpnhotspot.R
|
||||
import be.mygod.vpnhotspot.net.MacAddressCompat
|
||||
import be.mygod.vpnhotspot.room.AppDatabase
|
||||
import be.mygod.vpnhotspot.util.disconnectCompat
|
||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
@@ -36,7 +37,7 @@ object MacLookup {
|
||||
@MainThread
|
||||
fun abort(mac: MacAddressCompat) = macLookupBusy.remove(mac)?.let { (conn, job) ->
|
||||
job.cancel()
|
||||
if (Build.VERSION.SDK_INT < 26) GlobalScope.launch(Dispatchers.IO) { conn.disconnect() } else conn.disconnect()
|
||||
conn.disconnectCompat()
|
||||
}
|
||||
|
||||
@MainThread
|
||||
|
||||
10
mobile/src/main/java/be/mygod/vpnhotspot/util/AppUpdate.kt
Normal file
10
mobile/src/main/java/be/mygod/vpnhotspot/util/AppUpdate.kt
Normal file
@@ -0,0 +1,10 @@
|
||||
package be.mygod.vpnhotspot.util
|
||||
|
||||
import android.app.Activity
|
||||
|
||||
interface AppUpdate {
|
||||
val downloaded: Boolean? get() = null
|
||||
val message: String? get() = null
|
||||
val stalenessDays: Int? get() = null
|
||||
fun updateForResult(activity: Activity, requestCode: Int): Unit = error("Update not supported")
|
||||
}
|
||||
@@ -24,6 +24,9 @@ import androidx.fragment.app.FragmentManager
|
||||
import be.mygod.vpnhotspot.App.Companion.app
|
||||
import be.mygod.vpnhotspot.net.MacAddressCompat
|
||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
@@ -32,6 +35,7 @@ import java.lang.invoke.MethodHandles
|
||||
import java.lang.reflect.InvocationHandler
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import java.lang.reflect.Method
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.InetAddress
|
||||
import java.net.NetworkInterface
|
||||
import java.net.SocketException
|
||||
@@ -66,6 +70,10 @@ fun Method.matchesCompat(name: String, args: Array<out Any?>?, vararg classes: C
|
||||
}
|
||||
} else matches(name, *classes)
|
||||
|
||||
fun HttpURLConnection.disconnectCompat() {
|
||||
if (Build.VERSION.SDK_INT < 26) GlobalScope.launch(Dispatchers.IO) { disconnect() } else disconnect()
|
||||
}
|
||||
|
||||
fun Context.ensureReceiverUnregistered(receiver: BroadcastReceiver) {
|
||||
try {
|
||||
unregisterReceiver(receiver)
|
||||
|
||||
Reference in New Issue
Block a user