Skip to content

Commit 103a62f

Browse files
Fix: Use two-phase startForeground to prevent Android 14 timeout crash
1 parent 614245b commit 103a62f

1 file changed

Lines changed: 40 additions & 22 deletions

File tree

‎app/src/main/java/com/joebuildsthings/gamedeck/OverlayService.kt‎

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import androidx.compose.ui.text.font.FontWeight
3030
import androidx.compose.ui.unit.dp
3131
import androidx.compose.ui.unit.sp
3232
import androidx.core.app.NotificationCompat
33+
import androidx.core.app.ServiceCompat
3334
import com.joebuildsthings.gamedeck.ui.theme.AccentRed
3435
import com.joebuildsthings.gamedeck.ui.theme.AccentRedBright
3536
import com.joebuildsthings.gamedeck.ui.theme.AccentRedDim
@@ -52,31 +53,48 @@ class OverlayService : Service() {
5253
override fun onBind(intent: Intent?): IBinder? = null
5354

5455
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
55-
// 1. Start foreground immediately to satisfy Android's 5-second rule
56-
try {
57-
val notification = buildNotification()
58-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
59-
startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE)
60-
} else {
61-
startForeground(NOTIFICATION_ID, notification)
62-
}
63-
Log.i(TAG, "Foreground service started successfully.")
64-
} catch (t: Throwable) {
65-
Log.e(TAG, "Failed to start foreground service.", t)
66-
stopSelf()
67-
return START_NOT_STICKY
68-
}
69-
70-
// 2. Now handle the intent action
7156
when (intent?.action) {
7257
ACTION_START -> {
73-
if (overlayView == null) {
74-
try {
75-
showOverlay()
76-
Log.i(TAG, "Overlay view added successfully.")
77-
} catch (t: Throwable) {
78-
Log.e(TAG, "Failed to add overlay view.", t)
58+
try {
59+
// Phase 1: Immediately start foreground with a minimal notification
60+
// to satisfy Android's 5-second rule.
61+
val minimalNotification = NotificationCompat.Builder(this, CHANNEL_ID)
62+
.setContentTitle("GameDeck")
63+
.setContentText("Initializing...")
64+
.setSmallIcon(android.R.drawable.ic_menu_info_details)
65+
.setOngoing(true)
66+
.setSilent(true)
67+
.build()
68+
69+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
70+
ServiceCompat.startForeground(
71+
this,
72+
NOTIFICATION_ID,
73+
minimalNotification,
74+
ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
75+
)
76+
} else {
77+
startForeground(NOTIFICATION_ID, minimalNotification)
78+
}
79+
Log.i(TAG, "Foreground service started with minimal notification.")
80+
81+
// Phase 2: Now build the full notification and update it.
82+
val fullNotification = buildNotification()
83+
val mgr = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
84+
mgr.notify(NOTIFICATION_ID, fullNotification)
85+
Log.i(TAG, "Full notification updated.")
86+
87+
if (overlayView == null) {
88+
try {
89+
showOverlay()
90+
Log.i(TAG, "Overlay view added successfully.")
91+
} catch (t: Throwable) {
92+
Log.e(TAG, "Failed to add overlay view.", t)
93+
}
7994
}
95+
} catch (t: Throwable) {
96+
Log.e(TAG, "Failed to start foreground service.", t)
97+
stopSelf()
8098
}
8199
}
82100
ACTION_STOP -> {

0 commit comments

Comments
 (0)