Skip to content

Commit b11bf04

Browse files
committed
fix: solid notification icon and hardened background service starts
Replace the notification small icon with one bold shape so it stops falling back to the Android placeholder at status-bar size. Wrap the alarm and digest foreground-service starts so an OEM refusing a background start can't break the alarm flow. Expand the restricted-settings guidance with the Samsung path.
1 parent b851e61 commit b11bf04

8 files changed

Lines changed: 81 additions & 46 deletions

File tree

app/src/main/java/com/focusforceplus/app/service/RoutineAlarmReceiver.kt

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,16 @@ class RoutineAlarmReceiver : BroadcastReceiver() {
4949
// AlarmLaunchService calls startForeground() + startActivity() from within the service.
5050
// On Android 12+ this is a no-op in practice (the activity is already running from Path A),
5151
// but having the service post the notification ensures it survives stopForeground(detach).
52-
context.startForegroundService(
53-
Intent(context, AlarmLaunchService::class.java).apply {
54-
putExtra("routineId", routineId)
55-
putExtra("routineName", routineName)
56-
}
57-
)
52+
// Wrapped: some OEMs (e.g. Samsung) can still refuse a background FGS start, and that
53+
// must not take down the whole alarm — the full-screen activity from Path A already ran.
54+
runCatching {
55+
context.startForegroundService(
56+
Intent(context, AlarmLaunchService::class.java).apply {
57+
putExtra("routineId", routineId)
58+
putExtra("routineName", routineName)
59+
}
60+
)
61+
}
5862

5963
val pendingResult = goAsync()
6064
CoroutineScope(SupervisorJob() + Dispatchers.IO).launch {

app/src/main/java/com/focusforceplus/app/service/TodoAlarmReceiver.kt

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,18 @@ class TodoAlarmReceiver : BroadcastReceiver() {
4242
}
4343
)
4444
}
45-
// Both High (2) and Medium (1) use a foreground service so the
46-
// alarm notification cannot be swiped away on any Android version.
47-
context.startForegroundService(
48-
TodoAlarmForegroundService.startIntent(
49-
context, todoId, title, snoozeCount, maxSnooze, priority,
45+
// Both High (2) and Medium (1) use a foreground service that pins the
46+
// alarm notification (non-dismissable up to Android 13; on 14+ the system
47+
// lets users swipe FGS notifications, so the full-screen alarm above is the
48+
// real attention-grabber). Wrapped so an OEM refusing the background FGS
49+
// start never crashes the alarm.
50+
runCatching {
51+
context.startForegroundService(
52+
TodoAlarmForegroundService.startIntent(
53+
context, todoId, title, snoozeCount, maxSnooze, priority,
54+
)
5055
)
51-
)
56+
}
5257
}
5358
}
5459
}

app/src/main/java/com/focusforceplus/app/service/TodoDigestReceiver.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,11 @@ class TodoDigestReceiver : BroadcastReceiver() {
5353
// Anchor FGS: keeps Medium/High group notifications stable
5454
val pinnedCount = (overdue + undated).count { it.priority > 0 }
5555
if (pinnedCount > 0) {
56-
context.startForegroundService(
57-
TodoReminderForegroundService.startIntent(context, pinnedCount)
58-
)
56+
runCatching {
57+
context.startForegroundService(
58+
TodoReminderForegroundService.startIntent(context, pinnedCount)
59+
)
60+
}
5961
} else {
6062
notifHelper.cancelNotification(TodoNotificationHelper.NOTIFICATION_ID_OVERDUE)
6163
runCatching {

app/src/main/java/com/focusforceplus/app/service/TodoSnoozeReceiver.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,13 @@ class TodoSnoozeReceiver : BroadcastReceiver() {
4040
)
4141

4242
// Transition FGS to snoozed notification so it stays pinned for Medium/High
43-
context.startForegroundService(
44-
TodoAlarmForegroundService.startSnoozedIntent(
45-
context, todoId, todo.title, triggerMillis, newCount, todo.maxSnoozeCount, todo.priority,
43+
runCatching {
44+
context.startForegroundService(
45+
TodoAlarmForegroundService.startSnoozedIntent(
46+
context, todoId, todo.title, triggerMillis, newCount, todo.maxSnoozeCount, todo.priority,
47+
)
4648
)
47-
)
49+
}
4850
} finally {
4951
pendingResult.finish()
5052
}

app/src/main/java/com/focusforceplus/app/ui/screens/blocker/DisclosureScreen.kt

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -263,14 +263,21 @@ private fun RestrictedSettingsCard(onOpenAppInfo: () -> Unit) {
263263
color = MaterialTheme.colorScheme.primary,
264264
)
265265
Text(
266-
"Because you installed FocusForce+ directly instead of from the Play " +
267-
"Store, Android blocks the accessibility toggle the first time. To " +
268-
"unblock it: open the app info page below, tap the three-dot menu in " +
269-
"the top corner, and choose \"Allow restricted settings\". Then come " +
270-
"back and turn the service on.",
266+
"Because you installed FocusForce+ directly (not from the Play Store), " +
267+
"Android blocks the accessibility toggle the first time. It's a quick " +
268+
"one-time unlock. Tap \"Open app info\" below, then:",
271269
style = MaterialTheme.typography.bodyMedium,
272270
color = MaterialTheme.colorScheme.onSurface,
273271
)
272+
Text(
273+
"• Most phones: tap the three-dot menu (top-right) and choose " +
274+
"\"Allow restricted settings\".\n" +
275+
"• Samsung: open Permissions, tap the three-dot menu (top-right), " +
276+
"then \"Allow restricted settings\".\n\n" +
277+
"After that, come back here and turn the service on.",
278+
style = MaterialTheme.typography.bodySmall,
279+
color = MaterialTheme.colorScheme.onSurfaceVariant,
280+
)
274281
Button(
275282
onClick = onOpenAppInfo,
276283
shape = RoundedCornerShape(10.dp),

app/src/main/java/com/focusforceplus/app/ui/screens/settings/SettingsScreen.kt

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.focusforceplus.app.ui.screens.settings
22

33
import androidx.activity.compose.rememberLauncherForActivityResult
44
import androidx.activity.result.contract.ActivityResultContracts
5+
import androidx.compose.foundation.background
56
import androidx.compose.foundation.clickable
67
import androidx.compose.foundation.layout.Arrangement
78
import androidx.compose.foundation.layout.Box
@@ -61,6 +62,8 @@ import androidx.compose.runtime.remember
6162
import androidx.compose.runtime.setValue
6263
import androidx.compose.ui.Alignment
6364
import androidx.compose.ui.Modifier
65+
import androidx.compose.ui.draw.clip
66+
import androidx.compose.ui.graphics.Brush
6467
import androidx.compose.ui.graphics.Color
6568
import androidx.compose.ui.platform.LocalContext
6669
import androidx.compose.ui.platform.LocalLifecycleOwner
@@ -567,7 +570,25 @@ fun SettingsScreen(
567570
}
568571
}
569572

570-
item { Spacer(Modifier.height(16.dp)) }
573+
item {
574+
Spacer(Modifier.height(22.dp))
575+
Box(
576+
modifier = Modifier
577+
.fillMaxWidth()
578+
.padding(horizontal = 110.dp)
579+
.height(3.dp)
580+
.clip(RoundedCornerShape(2.dp))
581+
.background(
582+
Brush.horizontalGradient(
583+
listOf(
584+
Color(0xFFE40303), Color(0xFFFF8C00), Color(0xFFFFED00),
585+
Color(0xFF008026), Color(0xFF004DFF), Color(0xFF750787),
586+
)
587+
)
588+
),
589+
)
590+
Spacer(Modifier.height(18.dp))
591+
}
571592
}
572593
}
573594

app/src/main/java/com/focusforceplus/app/worker/TodoOverdueWorker.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,11 @@ class TodoOverdueWorker(
6464

6565
val pinnedCount = (overdue + undated).count { it.priority > 0 }
6666
if (pinnedCount > 0) {
67-
applicationContext.startForegroundService(
68-
TodoReminderForegroundService.startIntent(applicationContext, pinnedCount)
69-
)
67+
runCatching {
68+
applicationContext.startForegroundService(
69+
TodoReminderForegroundService.startIntent(applicationContext, pinnedCount)
70+
)
71+
}
7072
} else {
7173
notifHelper.cancelNotification(TodoNotificationHelper.NOTIFICATION_ID_OVERDUE)
7274
runCatching {
Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,16 @@
11
<!--
2-
Monochrome status-bar / notification icon. Notification small icons are rendered
3-
as pure alpha silhouettes, so this is a single flat-white path (no gradients,
4-
no shadow layer) — using the full-colour ic_launcher_foreground here made Android
5-
fall back to the default placeholder blob. The launcher robot mark is re-centred
6-
and scaled up so it fills the small icon nicely.
2+
Monochrome status-bar / notification icon. Notification small icons render as a
3+
pure alpha silhouette at tiny sizes, so this is one solid flat-white path with no
4+
fine detail, gradients, or group transforms (the detailed launcher robot came out
5+
as the Android placeholder at status-bar size). A bold bolt reads clearly at any
6+
size and fits the "Force / Focus" theme.
77
-->
88
<vector xmlns:android="http://schemas.android.com/apk/res/android"
99
android:width="24dp"
1010
android:height="24dp"
11-
android:viewportWidth="108"
12-
android:viewportHeight="108">
13-
<group
14-
android:pivotX="54"
15-
android:pivotY="51.15"
16-
android:scaleX="1.6"
17-
android:scaleY="1.6"
18-
android:translateY="2.85">
19-
<path
20-
android:fillColor="#FFFFFF"
21-
android:fillType="nonZero"
22-
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z" />
23-
</group>
11+
android:viewportWidth="24"
12+
android:viewportHeight="24">
13+
<path
14+
android:fillColor="#FFFFFF"
15+
android:pathData="M13,2L4.5,13.5c-0.4,0.5 0,1.2 0.6,1.2H10l-1.5,7.3c-0.1,0.7 0.8,1.1 1.3,0.5L19.5,10.5c0.4,-0.5 0,-1.2 -0.6,-1.2H14l1.4,-6.8C15.5,1.8 14.6,1.4 14.1,2z" />
2416
</vector>

0 commit comments

Comments
 (0)