@@ -8,15 +8,11 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.Build
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.RequiresApi
|
||||
import be.mygod.vpnhotspot.App.Companion.app
|
||||
import be.mygod.vpnhotspot.net.TetheringManager
|
||||
import be.mygod.vpnhotspot.util.broadcastReceiver
|
||||
import be.mygod.vpnhotspot.util.readableMessage
|
||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||
import timber.log.Timber
|
||||
import java.io.IOException
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
|
||||
class BluetoothTethering(context: Context, val stateListener: () -> Unit) :
|
||||
@@ -26,9 +22,16 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) :
|
||||
* PAN Profile
|
||||
*/
|
||||
private const val PAN = 5
|
||||
private val isTetheringOn by lazy {
|
||||
Class.forName("android.bluetooth.BluetoothPan").getDeclaredMethod("isTetheringOn")
|
||||
private val clazz by lazy { Class.forName("android.bluetooth.BluetoothPan") }
|
||||
private val constructor by lazy {
|
||||
clazz.getDeclaredConstructor(Context::class.java, BluetoothProfile.ServiceListener::class.java)
|
||||
}
|
||||
private val isTetheringOn by lazy { clazz.getDeclaredMethod("isTetheringOn") }
|
||||
|
||||
fun pan(context: Context, serviceListener: BluetoothProfile.ServiceListener) =
|
||||
constructor.newInstance(context, serviceListener) as BluetoothProfile
|
||||
val BluetoothProfile.isTetheringOn get() = isTetheringOn(this) as Boolean
|
||||
fun BluetoothProfile.closePan() = BluetoothAdapter.getDefaultAdapter()!!.closeProfileProxy(PAN, this)
|
||||
|
||||
private fun registerBluetoothStateListener(receiver: BroadcastReceiver) =
|
||||
app.registerReceiver(receiver, IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED))
|
||||
@@ -41,23 +44,8 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) :
|
||||
@TargetApi(24)
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
when (intent?.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) {
|
||||
BluetoothAdapter.STATE_ON -> try {
|
||||
BluetoothAdapter.STATE_ON -> {
|
||||
TetheringManager.startTethering(TetheringManager.TETHERING_BLUETOOTH, true, pendingCallback!!)
|
||||
} catch (e: IOException) {
|
||||
Timber.w(e)
|
||||
Toast.makeText(context, e.readableMessage, Toast.LENGTH_LONG).show()
|
||||
pendingCallback!!.onException()
|
||||
} catch (e: InvocationTargetException) {
|
||||
if (e.targetException !is SecurityException) Timber.w(e)
|
||||
var cause: Throwable? = e
|
||||
while (cause != null) {
|
||||
cause = cause.cause
|
||||
if (cause != null && cause !is InvocationTargetException) {
|
||||
Toast.makeText(context, cause.readableMessage, Toast.LENGTH_LONG).show()
|
||||
pendingCallback!!.onException()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
BluetoothAdapter.STATE_OFF, BluetoothAdapter.ERROR -> { }
|
||||
else -> return // ignore transition states
|
||||
@@ -81,18 +69,18 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) :
|
||||
}
|
||||
}
|
||||
|
||||
private var connected = false
|
||||
private var pan: BluetoothProfile? = null
|
||||
var activeFailureCause: Throwable? = null
|
||||
/**
|
||||
* Requires BLUETOOTH_PRIVILEGED on API 30+.
|
||||
*
|
||||
* Based on: https://android.googlesource.com/platform/packages/apps/Settings/+/78d5efd/src/com/android/settings/TetherSettings.java
|
||||
*/
|
||||
val active: Boolean? get() {
|
||||
activeFailureCause = null
|
||||
val pan = pan ?: return null
|
||||
if (!connected) return null
|
||||
activeFailureCause = null
|
||||
return BluetoothAdapter.getDefaultAdapter()?.state == BluetoothAdapter.STATE_ON && try {
|
||||
isTetheringOn(pan) as Boolean
|
||||
pan.isTetheringOn
|
||||
} catch (e: InvocationTargetException) {
|
||||
activeFailureCause = e.cause ?: e
|
||||
if (e.cause is SecurityException && Build.VERSION.SDK_INT >= 30) Timber.d(e) else Timber.w(e)
|
||||
@@ -104,24 +92,23 @@ class BluetoothTethering(context: Context, val stateListener: () -> Unit) :
|
||||
|
||||
init {
|
||||
try {
|
||||
BluetoothAdapter.getDefaultAdapter()?.getProfileProxy(context, this, PAN)
|
||||
} catch (e: SecurityException) {
|
||||
pan = pan(context, this)
|
||||
} catch (e: InvocationTargetException) {
|
||||
Timber.w(e)
|
||||
SmartSnackbar.make(e).show()
|
||||
activeFailureCause = e
|
||||
}
|
||||
registerBluetoothStateListener(receiver)
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected(profile: Int) {
|
||||
pan = null
|
||||
connected = false
|
||||
}
|
||||
override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) {
|
||||
pan = proxy
|
||||
connected = true
|
||||
stateListener()
|
||||
}
|
||||
override fun close() {
|
||||
app.unregisterReceiver(receiver)
|
||||
BluetoothAdapter.getDefaultAdapter()?.closeProfileProxy(PAN, pan)
|
||||
pan = null
|
||||
pan?.closePan()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import be.mygod.vpnhotspot.R
|
||||
import be.mygod.vpnhotspot.net.IpNeighbour
|
||||
import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor
|
||||
import be.mygod.vpnhotspot.util.KillableTileService
|
||||
import java.net.Inet4Address
|
||||
|
||||
@RequiresApi(24)
|
||||
abstract class IpNeighbourMonitoringTileService : KillableTileService(), IpNeighbourMonitor.Callback {
|
||||
@@ -24,7 +25,7 @@ abstract class IpNeighbourMonitoringTileService : KillableTileService(), IpNeigh
|
||||
|
||||
protected fun Tile.subtitleDevices(filter: (String) -> Boolean) {
|
||||
val size = neighbours
|
||||
.filter { it.state != IpNeighbour.State.FAILED && filter(it.dev) }
|
||||
.filter { it.ip is Inet4Address && it.state != IpNeighbour.State.FAILED && filter(it.dev) }
|
||||
.distinctBy { it.lladdr }
|
||||
.size
|
||||
if (size > 0) subtitle(resources.getQuantityString(
|
||||
|
||||
@@ -210,9 +210,8 @@ class RepeaterManager(private val parent: TetheringFragment) : Manager(), Servic
|
||||
band = SoftApConfigurationCompat.BAND_ANY
|
||||
channel = RepeaterService.operatingChannel
|
||||
try {
|
||||
val config = withContext(Dispatchers.Default) {
|
||||
P2pSupplicantConfiguration(group, RepeaterService.lastMac)
|
||||
}
|
||||
val config = P2pSupplicantConfiguration(group)
|
||||
config.init(RepeaterService.lastMac)
|
||||
holder.config = config
|
||||
passphrase = config.psk
|
||||
bssid = config.bssid
|
||||
|
||||
@@ -12,6 +12,7 @@ import androidx.core.content.ContextCompat
|
||||
import be.mygod.vpnhotspot.R
|
||||
import be.mygod.vpnhotspot.RepeaterService
|
||||
import be.mygod.vpnhotspot.util.KillableTileService
|
||||
import be.mygod.vpnhotspot.util.Services
|
||||
import be.mygod.vpnhotspot.util.stopAndUnbind
|
||||
|
||||
@RequiresApi(24)
|
||||
@@ -22,13 +23,13 @@ class RepeaterTileService : KillableTileService() {
|
||||
|
||||
override fun onStartListening() {
|
||||
super.onStartListening()
|
||||
if (RepeaterService.supported) {
|
||||
if (Services.p2p != null) {
|
||||
bindService(Intent(this, RepeaterService::class.java), this, Context.BIND_AUTO_CREATE)
|
||||
} else updateTile()
|
||||
}
|
||||
|
||||
override fun onStopListening() {
|
||||
if (RepeaterService.supported) stopAndUnbind(this)
|
||||
if (Services.p2p != null) stopAndUnbind(this)
|
||||
super.onStopListening()
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,10 @@ import be.mygod.vpnhotspot.net.TetheringManager
|
||||
import be.mygod.vpnhotspot.net.wifi.WifiApManager
|
||||
import be.mygod.vpnhotspot.util.readableMessage
|
||||
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.IOException
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
|
||||
sealed class TetherManager(protected val parent: TetheringFragment) : Manager(),
|
||||
@@ -50,25 +52,12 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(),
|
||||
} catch (e: RuntimeException) {
|
||||
app.logEvent("manage_write_settings") { param("message", e.toString()) }
|
||||
}
|
||||
val started = manager.isStarted
|
||||
try {
|
||||
if (started) manager.stop() else manager.start()
|
||||
} catch (e: IOException) {
|
||||
Timber.w(e)
|
||||
Toast.makeText(mainActivity, e.readableMessage, Toast.LENGTH_LONG).show()
|
||||
ManageBar.start(itemView.context)
|
||||
if (manager.isStarted) try {
|
||||
manager.stop()
|
||||
} catch (e: InvocationTargetException) {
|
||||
if (e.targetException !is SecurityException) Timber.w(e)
|
||||
var cause: Throwable? = e
|
||||
while (cause != null) {
|
||||
cause = cause.cause
|
||||
if (cause != null && cause !is InvocationTargetException) {
|
||||
Toast.makeText(mainActivity, cause.readableMessage, Toast.LENGTH_LONG).show()
|
||||
ManageBar.start(itemView.context)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
manager.onException(e)
|
||||
} else manager.start()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +85,14 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(),
|
||||
error?.let { SmartSnackbar.make("$tetherType: ${TetheringManager.tetherErrorMessage(it)}") }
|
||||
data.notifyChange()
|
||||
}
|
||||
override fun onException(e: Exception) {
|
||||
if (e !is InvocationTargetException || e.targetException !is SecurityException) Timber.w(e)
|
||||
GlobalScope.launch(Dispatchers.Main.immediate) {
|
||||
val context = parent.context ?: app
|
||||
Toast.makeText(context, e.readableMessage, Toast.LENGTH_LONG).show()
|
||||
ManageBar.start(context)
|
||||
}
|
||||
}
|
||||
|
||||
override fun bindTo(viewHolder: RecyclerView.ViewHolder) {
|
||||
(viewHolder as ViewHolder).manager = this
|
||||
@@ -124,7 +121,7 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(),
|
||||
override val type get() = VIEW_TYPE_WIFI
|
||||
|
||||
override fun start() = TetheringManager.startTethering(TetheringManager.TETHERING_WIFI, true, this)
|
||||
override fun stop() = TetheringManager.stopTethering(TetheringManager.TETHERING_WIFI)
|
||||
override fun stop() = TetheringManager.stopTethering(TetheringManager.TETHERING_WIFI, this::onException)
|
||||
}
|
||||
@RequiresApi(24)
|
||||
class Usb(parent: TetheringFragment) : TetherManager(parent) {
|
||||
@@ -133,7 +130,7 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(),
|
||||
override val type get() = VIEW_TYPE_USB
|
||||
|
||||
override fun start() = TetheringManager.startTethering(TetheringManager.TETHERING_USB, true, this)
|
||||
override fun stop() = TetheringManager.stopTethering(TetheringManager.TETHERING_USB)
|
||||
override fun stop() = TetheringManager.stopTethering(TetheringManager.TETHERING_USB, this::onException)
|
||||
}
|
||||
@RequiresApi(24)
|
||||
class Bluetooth(parent: TetheringFragment) : TetherManager(parent), DefaultLifecycleObserver {
|
||||
@@ -153,8 +150,6 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(),
|
||||
override val type get() = VIEW_TYPE_BLUETOOTH
|
||||
override val isStarted get() = tethering.active == true
|
||||
|
||||
override fun onException() = ManageBar.start(parent.context ?: app)
|
||||
|
||||
private var baseError: CharSequence? = null
|
||||
private fun makeErrorMessage(): CharSequence = listOfNotNull(
|
||||
if (tethering.active == null) tethering.activeFailureCause?.readableMessage else null,
|
||||
@@ -166,7 +161,7 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(),
|
||||
|
||||
override fun start() = BluetoothTethering.start(this)
|
||||
override fun stop() {
|
||||
TetheringManager.stopTethering(TetheringManager.TETHERING_BLUETOOTH)
|
||||
TetheringManager.stopTethering(TetheringManager.TETHERING_BLUETOOTH, this::onException)
|
||||
Thread.sleep(1) // give others a room to breathe
|
||||
onTetheringStarted() // force flush state
|
||||
}
|
||||
@@ -178,7 +173,7 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(),
|
||||
override val type get() = VIEW_TYPE_ETHERNET
|
||||
|
||||
override fun start() = TetheringManager.startTethering(TetheringManager.TETHERING_ETHERNET, true, this)
|
||||
override fun stop() = TetheringManager.stopTethering(TetheringManager.TETHERING_ETHERNET)
|
||||
override fun stop() = TetheringManager.stopTethering(TetheringManager.TETHERING_ETHERNET, this::onException)
|
||||
}
|
||||
@RequiresApi(30)
|
||||
class Ncm(parent: TetheringFragment) : TetherManager(parent) {
|
||||
@@ -187,7 +182,7 @@ sealed class TetherManager(protected val parent: TetheringFragment) : Manager(),
|
||||
override val type get() = VIEW_TYPE_NCM
|
||||
|
||||
override fun start() = TetheringManager.startTethering(TetheringManager.TETHERING_NCM, true, this)
|
||||
override fun stop() = TetheringManager.stopTethering(TetheringManager.TETHERING_NCM)
|
||||
override fun stop() = TetheringManager.stopTethering(TetheringManager.TETHERING_NCM, this::onException)
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
|
||||
@@ -27,10 +27,9 @@ import be.mygod.vpnhotspot.net.TetheringManager.localOnlyTetheredIfaces
|
||||
import be.mygod.vpnhotspot.net.TetheringManager.tetheredIfaces
|
||||
import be.mygod.vpnhotspot.net.wifi.WifiApDialogFragment
|
||||
import be.mygod.vpnhotspot.net.wifi.WifiApManager
|
||||
import be.mygod.vpnhotspot.util.ServiceForegroundConnector
|
||||
import be.mygod.vpnhotspot.util.broadcastReceiver
|
||||
import be.mygod.vpnhotspot.util.isNotGone
|
||||
import be.mygod.vpnhotspot.util.showAllowingStateLoss
|
||||
import be.mygod.vpnhotspot.root.RootManager
|
||||
import be.mygod.vpnhotspot.root.WifiApCommands
|
||||
import be.mygod.vpnhotspot.util.*
|
||||
import be.mygod.vpnhotspot.widget.SmartSnackbar
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import timber.log.Timber
|
||||
@@ -89,7 +88,7 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
|
||||
updateEnabledTypes()
|
||||
|
||||
val list = ArrayList<Manager>()
|
||||
if (RepeaterService.supported) list.add(repeaterManager)
|
||||
if (Services.p2p != null) list.add(repeaterManager)
|
||||
if (Build.VERSION.SDK_INT >= 26) list.add(localOnlyHotspotManager)
|
||||
val monitoredIfaces = binder?.monitoredIfaces ?: emptyList()
|
||||
updateMonitorList(activeIfaces - monitoredIfaces)
|
||||
@@ -150,10 +149,12 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var apConfigurationRunning = false
|
||||
override fun onMenuItemClick(item: MenuItem?): Boolean {
|
||||
return when (item?.itemId) {
|
||||
R.id.configuration -> item.subMenu.run {
|
||||
findItem(R.id.configuration_repeater).isNotGone = RepeaterService.supported
|
||||
findItem(R.id.configuration_repeater).isNotGone = Services.p2p != null
|
||||
findItem(R.id.configuration_temp_hotspot).isNotGone =
|
||||
adapter.localOnlyHotspotManager.binder?.configuration != null
|
||||
true
|
||||
@@ -170,16 +171,30 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
|
||||
}.showAllowingStateLoss(parentFragmentManager)
|
||||
true
|
||||
}
|
||||
R.id.configuration_ap -> try {
|
||||
WifiApDialogFragment().apply {
|
||||
arg(WifiApDialogFragment.Arg(WifiApManager.configuration))
|
||||
key()
|
||||
}.showAllowingStateLoss(parentFragmentManager)
|
||||
R.id.configuration_ap -> if (apConfigurationRunning) false else {
|
||||
apConfigurationRunning = true
|
||||
viewLifecycleOwner.lifecycleScope.launchWhenCreated {
|
||||
try {
|
||||
WifiApManager.configuration
|
||||
} catch (e: InvocationTargetException) {
|
||||
if (e.targetException !is SecurityException) Timber.w(e)
|
||||
try {
|
||||
RootManager.use { it.execute(WifiApCommands.GetConfiguration()) }
|
||||
} catch (eRoot: Exception) {
|
||||
eRoot.addSuppressed(e)
|
||||
Timber.w(eRoot)
|
||||
SmartSnackbar.make(eRoot).show()
|
||||
null
|
||||
}
|
||||
}?.let { configuration ->
|
||||
WifiApDialogFragment().apply {
|
||||
arg(WifiApDialogFragment.Arg(configuration))
|
||||
key()
|
||||
}.showAllowingStateLoss(parentFragmentManager)
|
||||
}
|
||||
apConfigurationRunning = false
|
||||
}
|
||||
true
|
||||
} catch (e: InvocationTargetException) {
|
||||
if (e.targetException !is SecurityException) Timber.w(e)
|
||||
SmartSnackbar.make(e.targetException).show()
|
||||
false
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
@@ -187,13 +202,20 @@ class TetheringFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClick
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
AlertDialogFragment.setResultListener<WifiApDialogFragment, WifiApDialogFragment.Arg>(this) { which, ret ->
|
||||
if (which == DialogInterface.BUTTON_POSITIVE) try {
|
||||
WifiApManager.configuration = ret!!.configuration
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Timber.d(e)
|
||||
SmartSnackbar.make(R.string.configuration_rejected).show()
|
||||
} catch (e: InvocationTargetException) {
|
||||
SmartSnackbar.make(e.targetException).show()
|
||||
if (which == DialogInterface.BUTTON_POSITIVE) viewLifecycleOwner.lifecycleScope.launchWhenCreated {
|
||||
val success = try {
|
||||
WifiApManager.setConfiguration(ret!!.configuration)
|
||||
} catch (e: InvocationTargetException) {
|
||||
try {
|
||||
RootManager.use { it.execute(WifiApCommands.SetConfiguration(ret!!.configuration)) }
|
||||
} catch (eRoot: Exception) {
|
||||
eRoot.addSuppressed(e)
|
||||
Timber.w(eRoot)
|
||||
SmartSnackbar.make(eRoot).show()
|
||||
null
|
||||
}
|
||||
}
|
||||
if (success == false) SmartSnackbar.make(R.string.configuration_rejected).show()
|
||||
}
|
||||
}
|
||||
binding = FragmentTetheringBinding.inflate(inflater, container, false)
|
||||
|
||||
@@ -11,6 +11,7 @@ import android.service.quicksettings.Tile
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.content.ContextCompat
|
||||
import be.mygod.vpnhotspot.App
|
||||
import be.mygod.vpnhotspot.R
|
||||
import be.mygod.vpnhotspot.TetheringService
|
||||
import be.mygod.vpnhotspot.net.TetherType
|
||||
@@ -20,8 +21,10 @@ import be.mygod.vpnhotspot.net.wifi.WifiApManager
|
||||
import be.mygod.vpnhotspot.util.broadcastReceiver
|
||||
import be.mygod.vpnhotspot.util.readableMessage
|
||||
import be.mygod.vpnhotspot.util.stopAndUnbind
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import java.io.IOException
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
|
||||
@RequiresApi(24)
|
||||
@@ -97,30 +100,17 @@ sealed class TetheringTileService : IpNeighbourMonitoringTileService(), Tetherin
|
||||
}
|
||||
}
|
||||
|
||||
protected inline fun safeInvoker(func: () -> Unit) = try {
|
||||
func()
|
||||
} catch (e: IOException) {
|
||||
Timber.w(e)
|
||||
Toast.makeText(this, e.readableMessage, Toast.LENGTH_LONG).show()
|
||||
} catch (e: InvocationTargetException) {
|
||||
if (e.targetException !is SecurityException) Timber.w(e)
|
||||
var cause: Throwable? = e
|
||||
while (cause != null) {
|
||||
cause = cause.cause
|
||||
if (cause != null && cause !is InvocationTargetException) {
|
||||
Toast.makeText(this, cause.readableMessage, Toast.LENGTH_LONG).show()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
override fun onClick() {
|
||||
val interested = interested ?: return
|
||||
if (interested.isEmpty()) safeInvoker { start() } else {
|
||||
if (interested.isEmpty()) start() else {
|
||||
val binder = binder
|
||||
if (binder == null) tapPending = true else {
|
||||
val inactive = interested.filterNot(binder::isActive)
|
||||
if (inactive.isEmpty()) safeInvoker { stop() }
|
||||
else ContextCompat.startForegroundService(this, Intent(this, TetheringService::class.java)
|
||||
if (inactive.isEmpty()) try {
|
||||
stop()
|
||||
} catch (e: Exception) {
|
||||
onException(e)
|
||||
} else ContextCompat.startForegroundService(this, Intent(this, TetheringService::class.java)
|
||||
.putExtra(TetheringService.EXTRA_ADD_INTERFACES, inactive.toTypedArray()))
|
||||
}
|
||||
}
|
||||
@@ -132,6 +122,12 @@ sealed class TetheringTileService : IpNeighbourMonitoringTileService(), Tetherin
|
||||
error?.let { Toast.makeText(this, TetheringManager.tetherErrorMessage(it), Toast.LENGTH_LONG).show() }
|
||||
updateTile()
|
||||
}
|
||||
override fun onException(e: Exception) {
|
||||
if (e !is InvocationTargetException || e.targetException !is SecurityException) Timber.w(e)
|
||||
GlobalScope.launch(Dispatchers.Main.immediate) {
|
||||
Toast.makeText(this@TetheringTileService, e.readableMessage, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
class Wifi : TetheringTileService() {
|
||||
override val labelString get() = R.string.tethering_manage_wifi
|
||||
@@ -139,14 +135,14 @@ sealed class TetheringTileService : IpNeighbourMonitoringTileService(), Tetherin
|
||||
override val icon get() = R.drawable.ic_device_wifi_tethering
|
||||
|
||||
override fun start() = TetheringManager.startTethering(TetheringManager.TETHERING_WIFI, true, this)
|
||||
override fun stop() = TetheringManager.stopTethering(TetheringManager.TETHERING_WIFI)
|
||||
override fun stop() = TetheringManager.stopTethering(TetheringManager.TETHERING_WIFI, this::onException)
|
||||
}
|
||||
class Usb : TetheringTileService() {
|
||||
override val labelString get() = R.string.tethering_manage_usb
|
||||
override val tetherType get() = TetherType.USB
|
||||
|
||||
override fun start() = TetheringManager.startTethering(TetheringManager.TETHERING_USB, true, this)
|
||||
override fun stop() = TetheringManager.stopTethering(TetheringManager.TETHERING_USB)
|
||||
override fun stop() = TetheringManager.stopTethering(TetheringManager.TETHERING_USB, this::onException)
|
||||
}
|
||||
class Bluetooth : TetheringTileService() {
|
||||
private var tethering: BluetoothTethering? = null
|
||||
@@ -156,7 +152,7 @@ sealed class TetheringTileService : IpNeighbourMonitoringTileService(), Tetherin
|
||||
|
||||
override fun start() = BluetoothTethering.start(this)
|
||||
override fun stop() {
|
||||
TetheringManager.stopTethering(TetheringManager.TETHERING_BLUETOOTH)
|
||||
TetheringManager.stopTethering(TetheringManager.TETHERING_BLUETOOTH, this::onException)
|
||||
Thread.sleep(1) // give others a room to breathe
|
||||
onTetheringStarted() // force flush state
|
||||
}
|
||||
@@ -202,12 +198,15 @@ sealed class TetheringTileService : IpNeighbourMonitoringTileService(), Tetherin
|
||||
val binder = binder
|
||||
if (binder == null) tapPending = true else {
|
||||
val inactive = (interested ?: return).filterNot(binder::isActive)
|
||||
if (inactive.isEmpty()) safeInvoker { stop() }
|
||||
else ContextCompat.startForegroundService(this, Intent(this, TetheringService::class.java)
|
||||
if (inactive.isEmpty()) try {
|
||||
stop()
|
||||
} catch (e: Exception) {
|
||||
onException(e)
|
||||
} else ContextCompat.startForegroundService(this, Intent(this, TetheringService::class.java)
|
||||
.putExtra(TetheringService.EXTRA_ADD_INTERFACES, inactive.toTypedArray()))
|
||||
}
|
||||
}
|
||||
false -> safeInvoker { start() }
|
||||
false -> start()
|
||||
else -> tapPending = true
|
||||
}
|
||||
}
|
||||
@@ -218,7 +217,7 @@ sealed class TetheringTileService : IpNeighbourMonitoringTileService(), Tetherin
|
||||
override val tetherType get() = TetherType.ETHERNET
|
||||
|
||||
override fun start() = TetheringManager.startTethering(TetheringManager.TETHERING_ETHERNET, true, this)
|
||||
override fun stop() = TetheringManager.stopTethering(TetheringManager.TETHERING_ETHERNET)
|
||||
override fun stop() = TetheringManager.stopTethering(TetheringManager.TETHERING_ETHERNET, this::onException)
|
||||
}
|
||||
@RequiresApi(30)
|
||||
class Ncm : TetheringTileService() {
|
||||
@@ -226,7 +225,7 @@ sealed class TetheringTileService : IpNeighbourMonitoringTileService(), Tetherin
|
||||
override val tetherType get() = TetherType.NCM
|
||||
|
||||
override fun start() = TetheringManager.startTethering(TetheringManager.TETHERING_NCM, true, this)
|
||||
override fun stop() = TetheringManager.stopTethering(TetheringManager.TETHERING_NCM)
|
||||
override fun stop() = TetheringManager.stopTethering(TetheringManager.TETHERING_NCM, this::onException)
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
|
||||
Reference in New Issue
Block a user