a7c5ed713a
- Request POST_NOTIFICATIONS permission at runtime in MainActivity (primary fix for notifications never appearing on Android 13+ phones including Android 16) - Register all 4 notification channels eagerly in SyncFlowApp.onCreate() instead of lazily inside workers - Add FOREGROUND_SERVICE_SHORT_SERVICE permission + shortService foreground type for Android 16 foreground service compatibility - Add global activity Log tab (new tab 2 in main nav) showing all sync events across all pairs, grouped by date with pair name, event icon, and file detail - Fix FileWatchService ON_CHANGE detection: ContentObserver on SAF tree URIs only fires for SAF-API writes, not raw filesystem writes. Now resolves primary:/* tree URIs to /storage/emulated/0/* and uses FileObserver for reliable detection - Bump version to 1.0.21 (build 22) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
63 lines
2.5 KiB
Kotlin
63 lines
2.5 KiB
Kotlin
package com.syncflow
|
|
|
|
import android.app.Application
|
|
import android.app.NotificationChannel
|
|
import android.app.NotificationManager
|
|
import android.content.Context
|
|
import androidx.hilt.work.HiltWorkerFactory
|
|
import androidx.work.Configuration
|
|
import com.syncflow.data.db.SyncPairDao
|
|
import com.syncflow.domain.model.ScheduleType
|
|
import com.syncflow.worker.FileWatchService
|
|
import dagger.hilt.android.HiltAndroidApp
|
|
import kotlinx.coroutines.CoroutineScope
|
|
import kotlinx.coroutines.Dispatchers
|
|
import kotlinx.coroutines.launch
|
|
import timber.log.Timber
|
|
import javax.inject.Inject
|
|
|
|
@HiltAndroidApp
|
|
class SyncFlowApp : Application(), Configuration.Provider {
|
|
|
|
@Inject lateinit var workerFactory: HiltWorkerFactory
|
|
@Inject lateinit var syncPairDao: SyncPairDao
|
|
|
|
override fun onCreate() {
|
|
super.onCreate()
|
|
if (BuildConfig.DEBUG) Timber.plant(Timber.DebugTree())
|
|
createNotificationChannels()
|
|
// Start file watcher on every app launch for any existing ON_CHANGE pairs
|
|
CoroutineScope(Dispatchers.IO).launch {
|
|
val hasOnChange = syncPairDao.getEnabled().any { it.scheduleType == ScheduleType.ON_CHANGE }
|
|
if (hasOnChange) FileWatchService.start(this@SyncFlowApp)
|
|
}
|
|
}
|
|
|
|
private fun createNotificationChannels() {
|
|
val nm = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
listOf(
|
|
NotificationChannel("sync_progress", "Sync progress", NotificationManager.IMPORTANCE_LOW).apply {
|
|
description = "Shown while a sync is running"
|
|
},
|
|
NotificationChannel("sync_complete", "Sync complete", NotificationManager.IMPORTANCE_LOW).apply {
|
|
description = "Summary after each successful sync"
|
|
},
|
|
NotificationChannel("sync_alerts", "Sync errors", NotificationManager.IMPORTANCE_DEFAULT).apply {
|
|
description = "Alerts for sync failures and conflicts"
|
|
},
|
|
NotificationChannel("sync_watching", "File watching", NotificationManager.IMPORTANCE_MIN).apply {
|
|
description = "Background service watching folders for changes"
|
|
setShowBadge(false)
|
|
},
|
|
).forEach { channel ->
|
|
if (nm.getNotificationChannel(channel.id) == null) nm.createNotificationChannel(channel)
|
|
}
|
|
}
|
|
|
|
override val workManagerConfiguration: Configuration
|
|
get() = Configuration.Builder()
|
|
.setWorkerFactory(workerFactory)
|
|
.setMinimumLoggingLevel(android.util.Log.INFO)
|
|
.build()
|
|
}
|