Add foreground service for reliable broadcast reception
Android 8.0+ blocks implicit broadcasts to manifest-registered receivers. Added ListenerService that dynamically registers the NtfyReceiver, ensuring LOCATE commands from the ntfy app are always received. Also handles POST_NOTIFICATIONS permission. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2f54b5a085
commit
f4c41b1851
3 changed files with 111 additions and 0 deletions
|
|
@ -6,6 +6,9 @@
|
|||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
|
@ -28,6 +31,11 @@
|
|||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".ListenerService"
|
||||
android:foregroundServiceType="location"
|
||||
android:exported="false" />
|
||||
|
||||
<receiver
|
||||
android:name=".NtfyReceiver"
|
||||
android:exported="true">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,80 @@
|
|||
package com.example.helios_location_finder
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import androidx.core.content.ContextCompat
|
||||
|
||||
class ListenerService : Service() {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "ListenerService"
|
||||
private const val CHANNEL_ID = "helios_listener"
|
||||
private const val NOTIFICATION_ID = 1
|
||||
|
||||
fun start(context: android.content.Context) {
|
||||
val intent = Intent(context, ListenerService::class.java)
|
||||
ContextCompat.startForegroundService(context, intent)
|
||||
}
|
||||
}
|
||||
|
||||
private val receiver = NtfyReceiver()
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
createNotificationChannel()
|
||||
startForeground(NOTIFICATION_ID, buildNotification())
|
||||
|
||||
val filter = IntentFilter("io.heckel.ntfy.MESSAGE_RECEIVED")
|
||||
ContextCompat.registerReceiver(
|
||||
this, receiver, filter, ContextCompat.RECEIVER_EXPORTED
|
||||
)
|
||||
Log.d(TAG, "Listener service started, receiver registered")
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
unregisterReceiver(receiver)
|
||||
Log.d(TAG, "Listener service stopped, receiver unregistered")
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? = null
|
||||
|
||||
private fun createNotificationChannel() {
|
||||
val channel = NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
"Helios Tracker",
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
).apply {
|
||||
description = "Keeps Helios Tracker listening for LOCATE commands"
|
||||
setShowBadge(false)
|
||||
}
|
||||
getSystemService(NotificationManager::class.java).createNotificationChannel(channel)
|
||||
}
|
||||
|
||||
private fun buildNotification(): Notification {
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
this, 0,
|
||||
Intent(this, MainActivity::class.java),
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
||||
return Notification.Builder(this, CHANNEL_ID)
|
||||
.setContentTitle("Helios Tracker")
|
||||
.setContentText("Lauscht auf LOCATE-Anfragen")
|
||||
.setSmallIcon(android.R.drawable.ic_menu_mylocation)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setOngoing(true)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
|
@ -50,6 +50,7 @@ class MainActivity : ComponentActivity() {
|
|||
|
||||
private val foregroundGranted = mutableStateOf(false)
|
||||
private val backgroundGranted = mutableStateOf(false)
|
||||
private val serviceRunning = mutableStateOf(false)
|
||||
|
||||
private val foregroundPermissionLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestMultiplePermissions()
|
||||
|
|
@ -62,11 +63,19 @@ class MainActivity : ComponentActivity() {
|
|||
ActivityResultContracts.RequestPermission()
|
||||
) { granted ->
|
||||
backgroundGranted.value = granted
|
||||
if (granted) startListenerService()
|
||||
}
|
||||
|
||||
private val notificationPermissionLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission()
|
||||
) { granted ->
|
||||
if (granted) startListenerService()
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
checkPermissions()
|
||||
startListenerService()
|
||||
|
||||
val listenTopic = mutableStateOf(Prefs.getListenTopic(this))
|
||||
val replyTopic = mutableStateOf(Prefs.getReplyTopic(this))
|
||||
|
|
@ -133,6 +142,20 @@ class MainActivity : ComponentActivity() {
|
|||
backgroundPermissionLauncher.launch(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
|
||||
}
|
||||
}
|
||||
|
||||
private fun startListenerService() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
this, Manifest.permission.POST_NOTIFICATIONS
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
notificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||
return
|
||||
}
|
||||
}
|
||||
ListenerService.start(this)
|
||||
serviceRunning.value = true
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue