Exposes phone connection status via HTTP API for tethered devices. Features: - Auto-start/stop server when tethering is enabled/disabled - REST API with /status and /health endpoints - Connection type, signal strength, carrier, battery info - Foreground service with persistent notification Stack: Kotlin 2.3, AGP 8.13, Gradle 8.13, compileSdk 36
62 lines
2.1 KiB
Kotlin
62 lines
2.1 KiB
Kotlin
package dev.itsh.tetherapi.receiver
|
|
|
|
import android.content.BroadcastReceiver
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import android.net.ConnectivityManager
|
|
import android.os.Build
|
|
import androidx.preference.PreferenceManager
|
|
import dev.itsh.tetherapi.service.TetherApiService
|
|
|
|
class TetherStateReceiver : BroadcastReceiver() {
|
|
|
|
override fun onReceive(context: Context, intent: Intent) {
|
|
if (intent.action != ACTION_TETHER_STATE_CHANGED) return
|
|
|
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
|
val autoStartEnabled = prefs.getBoolean(PREF_AUTO_START, true)
|
|
|
|
if (!autoStartEnabled) return
|
|
|
|
val isTethering = isTetherActive(context, intent)
|
|
val port = prefs.getInt(PREF_PORT, TetherApiService.DEFAULT_PORT)
|
|
|
|
if (isTethering && !TetherApiService.isRunning) {
|
|
TetherApiService.start(context, port)
|
|
} else if (!isTethering && TetherApiService.isRunning) {
|
|
TetherApiService.stop(context)
|
|
}
|
|
}
|
|
|
|
@Suppress("DEPRECATION")
|
|
private fun isTetherActive(context: Context, intent: Intent): Boolean {
|
|
val extras = intent.extras ?: return false
|
|
|
|
val activeList = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
extras.getStringArrayList("activeArray")
|
|
} else {
|
|
@Suppress("UNCHECKED_CAST")
|
|
extras.get("activeArray") as? ArrayList<String>
|
|
}
|
|
|
|
if (!activeList.isNullOrEmpty()) {
|
|
return true
|
|
}
|
|
|
|
return try {
|
|
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
|
val method = cm.javaClass.getDeclaredMethod("getTetheredIfaces")
|
|
val tetheredIfaces = method.invoke(cm) as? Array<*>
|
|
tetheredIfaces != null && tetheredIfaces.isNotEmpty()
|
|
} catch (e: Exception) {
|
|
false
|
|
}
|
|
}
|
|
|
|
companion object {
|
|
const val ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED"
|
|
const val PREF_AUTO_START = "auto_start_on_tether"
|
|
const val PREF_PORT = "api_port"
|
|
}
|
|
}
|