mirror of
https://github.com/twinksoftware/Gearslip.git
synced 2026-11-10 16:02:25 +01:00
A wireless drive keeps Gearslip in the foreground
From the moment the car's Bluetooth channel opens until the drive ends, a quiet notification keeps Gearslip in the foreground, so Android lets it join the car's Wi-Fi with the phone locked.
This commit is contained in:
1 parent
cc78020eda
commit
07e398cf79
3 files changed
+76
No files matched your search
@@ -19,6 +19,8 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
|
||||
<!-- Keeps a wireless start going with the phone locked; see WirelessKeepAlive. -->
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
|
||||
|
||||
<!--
|
||||
Wireless Android Auto: Bluetooth reaches the paired car's Android Auto service, which
|
||||
@@ -204,6 +206,11 @@
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="location" />
|
||||
|
||||
<service
|
||||
android:name=".wireless.WirelessKeepAlive"
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="connectedDevice" />
|
||||
|
||||
<!--
|
||||
Holds the capture session that carries a media app's audio to the car. It also claims
|
||||
the microphone, so voice replies can hear the driver with the phone locked.
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package app.seb3thehacker.gearslip.wireless
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.os.IBinder
|
||||
import app.seb3thehacker.gearslip.GearslipLog
|
||||
|
||||
/**
|
||||
* Keeps Gearslip in the foreground while a wireless car is connected, from the moment its
|
||||
* Bluetooth channel opens until the drive ends. Android treats an app in the background as
|
||||
* unwanted and may refuse its Wi-Fi request or kill it, which is what happens to a start-up with
|
||||
* the phone locked in a pocket. It does no work of its own; the notification is the cost.
|
||||
*/
|
||||
class WirelessKeepAlive : Service() {
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? = null
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
val manager = getSystemService(NotificationManager::class.java)
|
||||
manager.createNotificationChannel(
|
||||
NotificationChannel(CHANNEL, "Wireless car connection", NotificationManager.IMPORTANCE_LOW),
|
||||
)
|
||||
val notification = Notification.Builder(this, CHANNEL)
|
||||
.setSmallIcon(android.R.drawable.stat_sys_data_bluetooth)
|
||||
.setContentTitle("Gearslip is connected to the car wirelessly")
|
||||
.setOngoing(true)
|
||||
.build()
|
||||
// Refused if Android thinks Gearslip may not start one now; the start-up carries on
|
||||
// without it, as it did before this service existed.
|
||||
runCatching {
|
||||
startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE)
|
||||
}.onFailure {
|
||||
GearslipLog.w("wireless keep-alive: could not start the foreground service - ${it.message}")
|
||||
stopSelf()
|
||||
}
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val CHANNEL = "gearslip_wireless"
|
||||
private const val NOTIFICATION_ID = 45
|
||||
|
||||
fun start(context: Context) {
|
||||
runCatching { context.startForegroundService(Intent(context, WirelessKeepAlive::class.java)) }
|
||||
.onFailure { GearslipLog.w("wireless keep-alive: start refused - ${it.message}") }
|
||||
}
|
||||
|
||||
fun stop(context: Context) {
|
||||
runCatching { context.stopService(Intent(context, WirelessKeepAlive::class.java)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -132,6 +132,15 @@ object WirelessLink {
|
||||
*/
|
||||
private fun startUp(context: Context, device: BluetoothDevice) {
|
||||
val me = Thread.currentThread()
|
||||
try {
|
||||
tryAndRetry(context, device, me)
|
||||
} finally {
|
||||
// A newer start-up has the service now; stop() already ended this one's.
|
||||
if (worker === me) WirelessKeepAlive.stop(context)
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryAndRetry(context: Context, device: BluetoothDevice, me: Thread) {
|
||||
var tries = 0
|
||||
while (true) {
|
||||
handedOver = false
|
||||
@@ -165,6 +174,7 @@ object WirelessLink {
|
||||
worker?.interrupt()
|
||||
worker = null
|
||||
release(context)
|
||||
WirelessKeepAlive.stop(context)
|
||||
// Stopped before the car's session began, so nothing else resets Home's card.
|
||||
if (starting) {
|
||||
log.i("wireless start-up stopped")
|
||||
@@ -219,6 +229,8 @@ object WirelessLink {
|
||||
}
|
||||
rfcomm = socket
|
||||
log.i("Bluetooth: channel open")
|
||||
// Before the Wi-Fi request: Android may refuse one from an app it sees as in the background.
|
||||
WirelessKeepAlive.start(context)
|
||||
SessionStatus.connecting("Bluetooth: talking to $name")
|
||||
|
||||
val input = DataInputStream(socket.inputStream)
|
||||
|
||||
Reference in new issue
Block a user