1
0
mirror of https://github.com/avinal/nikki.git synced 2026-07-03 21:40:09 +05:30

Auto sync setting, pretty notifications, background permissions,

reliable alarm scheduling

Auto sync:
- Configurable interval: 1/2/5/10/15/30/60 min (default 5)
- MemoRepository.syncIntervalMinutes updated live from DataStore
- Setting shown under "memos" section in settings

Notifications by priority:
- p1: 🔴 alarm sound, long vibration, wakes screen, bypasses DND
- p2: 🟠 notification sound, medium vibration, heads-up
- p3: 🔵 notification sound, short vibration
- none: silent, no vibration
- BigTextStyle with priority tag, colored accent bar
- 4 separate Android notification channels

Background reliability:
- FOREGROUND_SERVICE, WAKE_LOCK, REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
- Battery optimization exemption requested on first launch
- DirectAlarmScheduler: schedules alarms directly from app process
  using live Room data (bypasses WorkManager DB sync issue)
- onContentChanged fires direct scheduling on every memo create/update
- TaskReminderReceiver moved to androidApp module for reliable
  cold-start instantiation
- WorkManager kept as 15-min backup for server-side changes

123 tests passing.

Co-Authored-By: Claude Opus 4.6 (1M context)

Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
This commit is contained in:
2026-05-21 21:14:27 +05:30
parent 8c3ab59f2f
commit 0512c9a698
17 changed files with 400 additions and 182 deletions
+4 -1
View File
@@ -6,6 +6,9 @@
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.USE_EXACT_ALARM" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:allowBackup="true"
@@ -28,7 +31,7 @@
</activity>
<receiver
android:name="com.avinal.memos.notifications.TaskAlarmReceiver"
android:name=".TaskReminderReceiver"
android:exported="false" />
<receiver
@@ -28,8 +28,9 @@ class MainActivity : ComponentActivity() {
deps.initialize()
com.avinal.memos.util.appContext = applicationContext
TaskNotificationManager.createChannel(this)
TaskNotificationManager.createChannels(this)
requestNotificationPermission()
requestBatteryOptimizationExemption()
scheduleTaskChecker(applicationContext)
enableEdgeToEdge()
@@ -49,4 +50,16 @@ class MainActivity : ComponentActivity() {
}
}
}
private fun requestBatteryOptimizationExemption() {
val pm = getSystemService(android.os.PowerManager::class.java)
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
try {
startActivity(android.content.Intent(
android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
android.net.Uri.parse("package:$packageName")
))
} catch (_: Exception) {}
}
}
}
@@ -0,0 +1,26 @@
package com.avinal.memos
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import com.avinal.memos.notifications.TaskNotificationManager
class TaskReminderReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
android.util.Log.d("TaskReminderReceiver", "onReceive fired!")
val taskText = intent.getStringExtra("task_text") ?: "Task reminder"
val dueLabel = intent.getStringExtra("due_label") ?: "due"
val notificationId = intent.getIntExtra("notification_id", 0)
val priority = intent.getIntExtra("priority", 0)
android.util.Log.d("TaskReminderReceiver", "Showing: $taskText - $dueLabel (p=$priority, id=$notificationId)")
TaskNotificationManager.createChannels(context)
TaskNotificationManager.showTaskNotification(
context = context,
notificationId = notificationId,
taskText = taskText,
dueLabel = dueLabel,
priority = priority,
)
}
}