Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions mobile/src/main/java/net/activitywatch/android/AWPreferences.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,14 @@ class AWPreferences(context: Context) {
fun setHostnameMigrated() {
sharedPreferences.edit().putBoolean("hasMigratedHostname", true).apply()
}

// Sync is off by default; user must explicitly enable it once a sync directory
// is configured (e.g. via Storage Access Framework).
fun isSyncEnabled(): Boolean {
return sharedPreferences.getBoolean("syncEnabled", false)
}

fun setSyncEnabled(enabled: Boolean) {
sharedPreferences.edit().putBoolean("syncEnabled", enabled).apply()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,14 @@ class BackgroundService : Service() {
}
}

// Start the sync scheduler
syncScheduler.start()
// Start the sync scheduler only when the user has enabled sync.
// Default is off — the sync directory is not accessible to other apps
// (Android scoped storage), so auto-sync would silently no-op for most users.
if (prefs.isSyncEnabled()) {
syncScheduler.start()
} else {
Log.i(TAG, "Sync is disabled (default). Enable it in settings to start syncing.")
}

// Schedule event parsing
scheduleEventParsing()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ class SyncAlarmReceiver : BroadcastReceiver() {

when (intent.action) {
"net.activitywatch.android.SYNC_ALARM" -> {
if (!AWPreferences(context).isSyncEnabled()) {
Log.i(TAG, "Sync is disabled; cancelling stale alarm")
SyncScheduler.cancelAlarm(context)
return
}
Comment on lines +19 to +23

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Disabled Sync Leaves Alarm Armed

When an older build or a previous enabled session has already registered the repeating wakeup alarm, this branch skips the sync work but leaves that alarm active. Because SyncScheduler.stop() does not cancel the AlarmManager fallback, the app can keep waking every 15 minutes and logging this path even though sync is now disabled.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 1d8d23a: added SyncScheduler.cancelAlarm(context) to the disabled-sync branch in SyncAlarmReceiver. When the alarm fires but sync is off, we now cancel the AlarmManager wakeup via FLAG_NO_CREATE PendingIntent retrieval + am.cancel(), so no further 15-minute wakeups occur. The cancel logic lives in a new SyncScheduler.companion object to keep the PendingIntent construction in one place.

Log.i(TAG, "Performing scheduled sync...")
val pendingResult = goAsync()
// Create SyncInterface and perform sync on IO dispatcher to avoid
Expand Down
20 changes: 20 additions & 0 deletions mobile/src/main/java/net/activitywatch/android/SyncScheduler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,24 @@ class SyncScheduler(private val context: Context) {
}
}
}

companion object {
// Called by SyncAlarmReceiver when sync is disabled, to cancel a stale alarm left
// from a previous enable session or an older build that registered it before the pref existed.
fun cancelAlarm(context: Context) {
val am = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val intent = Intent(ACTION_SYNC_ALARM).setPackage(context.packageName)
val pi = PendingIntent.getBroadcast(
context,
0,
intent,
PendingIntent.FLAG_NO_CREATE or PendingIntent.FLAG_IMMUTABLE
)
if (pi != null) {
am.cancel(pi)
pi.cancel()
Log.i(TAG, "Cancelled stale AlarmManager sync alarm (sync disabled)")
}
}
}
}
Loading