@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user