Correctly handle TetherType changing

This commit is contained in:
Mygod
2020-05-29 21:20:53 -04:00
parent 299085293d
commit 069b32a7d9
7 changed files with 156 additions and 83 deletions

View File

@@ -32,12 +32,13 @@ import be.mygod.vpnhotspot.util.ServiceForegroundConnector
import be.mygod.vpnhotspot.util.broadcastReceiver
import be.mygod.vpnhotspot.util.isNotGone
import be.mygod.vpnhotspot.widget.SmartSnackbar
import kotlinx.coroutines.CompletableDeferred
import timber.log.Timber
import java.lang.reflect.InvocationTargetException
import java.net.NetworkInterface
import java.net.SocketException
class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickListener {
class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickListener, TetheringManager.TetheringEventCallback {
companion object {
const val START_REPEATER = 4
const val START_LOCAL_ONLY_HOTSPOT = 1
@@ -64,15 +65,36 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
TetherManager.WifiLegacy(this@TetheringFragment)
}
private var enabledIfaces = emptyList<String>()
private var listDeferred = CompletableDeferred<List<Manager>>().apply { complete(emptyList()) }
private fun updateEnabledTypes() {
this@TetheringFragment.enabledTypes = enabledIfaces.map { TetherType.ofInterface(it) }.toSet()
}
suspend fun notifyInterfaceChanged(lastList: List<Manager>? = null) {
@Suppress("NAME_SHADOWING") val lastList = lastList ?: listDeferred.await()
val first = lastList.indexOfFirst { it is InterfaceManager }
if (first >= 0) notifyItemRangeChanged(first, lastList.indexOfLast { it is InterfaceManager } - first + 1)
}
suspend fun notifyTetherTypeChanged() {
updateEnabledTypes()
val lastList = listDeferred.await()
notifyInterfaceChanged(lastList)
val first = lastList.indexOfLast { it !is TetherManager } + 1
notifyItemRangeChanged(first, lastList.size - first)
}
fun update(activeIfaces: List<String>, localOnlyIfaces: List<String>, erroredIfaces: List<String>) {
val deferred = CompletableDeferred<List<Manager>>()
listDeferred = deferred
ifaceLookup = try {
NetworkInterface.getNetworkInterfaces().asSequence().associateBy { it.name }
} catch (e: SocketException) {
Timber.d(e)
emptyMap()
}
this@TetheringFragment.enabledTypes =
(activeIfaces + localOnlyIfaces).map { TetherType.ofInterface(it) }.toSet()
enabledIfaces = activeIfaces + localOnlyIfaces
updateEnabledTypes()
val list = ArrayList<Manager>()
if (RepeaterService.supported) list.add(repeaterManager)
@@ -94,7 +116,7 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
list.add(wifiManagerLegacy)
wifiManagerLegacy.onTetheringStarted()
}
submitList(list)
submitList(list) { deferred.complete(list) }
}
override fun getItemViewType(position: Int) = getItem(position).type
@@ -230,18 +252,20 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
binder = service as TetheringService.Binder
service.routingsChanged[this] = {
requireContext().apply {
// flush tethered interfaces
unregisterReceiver(receiver)
registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED))
}
lifecycleScope.launchWhenStarted { adapter.notifyInterfaceChanged() }
}
requireContext().registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED))
if (BuildCompat.isAtLeastR()) TetheringManager.registerTetheringEventCallback(null, this)
}
override fun onServiceDisconnected(name: ComponentName?) {
(binder ?: return).routingsChanged -= this
binder = null
if (BuildCompat.isAtLeastR()) TetheringManager.unregisterTetheringEventCallback(this)
requireContext().unregisterReceiver(receiver)
}
override fun onTetherableInterfaceRegexpsChanged() {
lifecycleScope.launchWhenStarted { adapter.notifyTetherTypeChanged() }
}
}

View File

@@ -10,6 +10,7 @@ import android.service.quicksettings.Tile
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import androidx.core.os.BuildCompat
import be.mygod.vpnhotspot.R
import be.mygod.vpnhotspot.TetheringService
import be.mygod.vpnhotspot.net.TetherType
@@ -25,7 +26,8 @@ import java.io.IOException
import java.lang.reflect.InvocationTargetException
@RequiresApi(24)
sealed class TetheringTileService : KillableTileService(), TetheringManager.StartTetheringCallback {
sealed class TetheringTileService : KillableTileService(), TetheringManager.StartTetheringCallback,
TetheringManager.TetheringEventCallback {
protected val tileOff by lazy { Icon.createWithResource(application, icon) }
protected val tileOn by lazy { Icon.createWithResource(application, R.drawable.ic_quick_settings_tile_on) }
@@ -47,12 +49,15 @@ sealed class TetheringTileService : KillableTileService(), TetheringManager.Star
override fun onStartListening() {
super.onStartListening()
bindService(Intent(this, TetheringService::class.java), this, Context.BIND_AUTO_CREATE)
// we need to initialize tethered ASAP for onClick, which is not achievable using registerTetheringEventCallback
tethered = registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED))
?.tetheredIfaces
if (BuildCompat.isAtLeastR()) TetheringManager.registerTetheringEventCallback(null, this)
updateTile()
}
override fun onStopListening() {
if (BuildCompat.isAtLeastR()) TetheringManager.unregisterTetheringEventCallback(this)
unregisterReceiver(receiver)
stopAndUnbind(this)
super.onStopListening()
@@ -60,7 +65,7 @@ sealed class TetheringTileService : KillableTileService(), TetheringManager.Star
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
binder = service as TetheringService.Binder
service.routingsChanged[this] = { updateTile() }
service.routingsChanged[this] = this::updateTile
super.onServiceConnected(name, service)
}
@@ -127,6 +132,7 @@ sealed class TetheringTileService : KillableTileService(), TetheringManager.Star
error?.let { Toast.makeText(this, TetheringManager.tetherErrorMessage(it), Toast.LENGTH_LONG).show() }
updateTile()
}
override fun onTetherableInterfaceRegexpsChanged() = updateTile()
class Wifi : TetheringTileService() {
override val labelString get() = R.string.tethering_manage_wifi