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
25 changes: 24 additions & 1 deletion app/src/main/java/com/chiller3/basicsync/Notifications.kt
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,30 @@ class Notifications(private val context: Context) {
setOngoing(true)
setOnlyAlertOnce(true)

if (runState.showBlockedReasons) {
if (runState.showFolderStates) {
setContentText(buildString {
append(context.resources.getQuantityString(
R.plurals.connected_devices,
state.connectedDevices,
state.connectedDevices,
))

for ((resId, count) in arrayOf(
R.plurals.folder_state_idle to state.folderStates.idle,
R.plurals.folder_state_scanning to state.folderStates.scanning,
R.plurals.folder_state_syncing to state.folderStates.syncing,
R.plurals.folder_state_cleaning to state.folderStates.cleaning,
R.plurals.folder_state_errored to state.folderStates.errored,
R.plurals.folder_state_starting to state.folderStates.starting,
)) {
if (count > 0) {
append('\n')
append(context.resources.getQuantityString(resId, count, count))
}
}
})
style = Notification.BigTextStyle()
} else if (runState.showBlockedReasons) {
setContentText(buildString {
for ((i, reason) in state.blockedReasons.withIndex()) {
if (i > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,8 @@ private fun PreviewSettingsScreen() {
allowAutoMode = true,
preRunAction = null,
showExit = false,
folderStates = SyncthingService.FolderStates(),
connectedDevices = 0,
)

AppTheme {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,10 @@ class DeviceStateTracker(private val context: Context) :
}
}

fun updateBusyFolders(count: Int) {
fun updateBusyFolders(folderStates: SyncthingService.FolderStates) {
// We only want mutating operations to interrupt the idle timer.
val count = folderStates.syncing + folderStates.cleaning + folderStates.starting

handler.post {
if (state.busyFolders != count) {
alarmManager.cancel(scheduleIdlePendingIntent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ class SyncthingService : Service(), SyncthingStatusReceiver, DeviceStateListener
IMPORTING,
EXPORTING;

val showFolderStates: Boolean
get() = this == RUNNING

val showBlockedReasons: Boolean
get() = this == NOT_RUNNING || this == PAUSED

Expand All @@ -85,6 +88,8 @@ class SyncthingService : Service(), SyncthingStatusReceiver, DeviceStateListener
private val allowAutoMode: Boolean,
private val preRunAction: PreRunAction?,
private val showExit: Boolean,
val folderStates: FolderStates,
val connectedDevices: Int,
) {
private val shouldResume: Boolean
get() = blockedReasons.isEmpty()
Expand Down Expand Up @@ -179,6 +184,24 @@ class SyncthingService : Service(), SyncthingStatusReceiver, DeviceStateListener
}
}

data class FolderStates(
val idle: Int,
val scanning: Int,
val syncing: Int,
val cleaning: Int,
val errored: Int,
val starting: Int,
) {
constructor() : this(
idle = 0,
scanning = 0,
syncing = 0,
cleaning = 0,
errored = 0,
starting = 0,
)
}

private lateinit var prefs: Preferences
private lateinit var notifications: Notifications
private val runnerThread = Thread(::runner)
Expand Down Expand Up @@ -240,6 +263,10 @@ class SyncthingService : Service(), SyncthingStatusReceiver, DeviceStateListener
notifications.sendOrClearAlertsNotification(count)
}
}
@GuardedBy("stateLock")
private var syncthingFolderStates = FolderStates()
@GuardedBy("stateLock")
private var syncthingConnectedDevices = 0

private val isResumed: Boolean
@GuardedBy("stateLock")
Expand Down Expand Up @@ -443,12 +470,17 @@ class SyncthingService : Service(), SyncthingStatusReceiver, DeviceStateListener
allowAutoMode = prefs.allowAutoMode,
preRunAction = currentPreRunAction,
showExit = prefs.showExit,
folderStates = syncthingFolderStates,
connectedDevices = syncthingConnectedDevices,
)

val wasChanged = notificationState != lastServiceState

if (wasChanged || forceShowNotification) {
if (wasChanged) {
deviceStateTracker.updateBusyFolders(notificationState.folderStates)
deviceStateTracker.updateConnectedDevices(notificationState.connectedDevices)

val guiInfo = guiInfo

allListeners { it.onRunStateChanged(notificationState, guiInfo) }
Expand Down Expand Up @@ -592,11 +624,10 @@ class SyncthingService : Service(), SyncthingStatusReceiver, DeviceStateListener
Log.i(TAG, "Syncthing successfully stopped")

synchronized(stateLock) {
deviceStateTracker.updateBusyFolders(0)
deviceStateTracker.updateConnectedDevices(0)

syncthingConflicts = emptyList()
syncthingAlerts = 0
syncthingFolderStates = FolderStates()
syncthingConnectedDevices = 0
syncthingApp = null

stateChanged()
Expand Down Expand Up @@ -627,13 +658,33 @@ class SyncthingService : Service(), SyncthingStatusReceiver, DeviceStateListener
}

@WorkerThread
override fun onBusyFoldersUpdated(count: Int) {
deviceStateTracker.updateBusyFolders(count)
override fun onFolderStatesUpdated(
idle: Int,
scanning: Int,
syncing: Int,
cleaning: Int,
errored: Int,
starting: Int,
) {
synchronized(stateLock) {
syncthingFolderStates = FolderStates(
idle = idle,
scanning = scanning,
syncing = syncing,
cleaning = cleaning,
errored = errored,
starting = starting,
)
stateChanged()
}
}

@WorkerThread
override fun onConnectedDevicesUpdated(count: Int) {
deviceStateTracker.updateConnectedDevices(count)
synchronized(stateLock) {
syncthingConnectedDevices = count
stateChanged()
}
}

data class GuiInfo(
Expand Down
36 changes: 36 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,42 @@
<!-- Syncing is blocked because the current time is outside of the time window where syncing is permitted. -->
<string name="blocked_reason_time_schedule">• Outside of time schedule</string>

<!-- Number of folders that are up to date. -->
<plurals name="folder_state_idle">
<item quantity="one">• %d folder up to date</item>
<item quantity="other">• %d folders up to date</item>
</plurals>
<!-- Number of folders that are scanning. -->
<plurals name="folder_state_scanning">
<item quantity="one">• %d folder scanning</item>
<item quantity="other">• %d folders scanning</item>
</plurals>
<!-- Number of folders that are syncing. -->
<plurals name="folder_state_syncing">
<item quantity="one">• %d folder syncing</item>
<item quantity="other">• %d folders syncing</item>
</plurals>
<!-- Number of folders that are cleaning up old files according to the configured file versioning. -->
<plurals name="folder_state_cleaning">
<item quantity="one">• %d folder cleaning old files</item>
<item quantity="other">• %d folders cleaning old files</item>
</plurals>
<!-- Number of folders that have errors. -->
<plurals name="folder_state_errored">
<item quantity="one">• %d folder with errors</item>
<item quantity="other">• %d folders with errors</item>
</plurals>
<!-- Number of folders that are initializing during startup. -->
<plurals name="folder_state_starting">
<item quantity="one">• %d folder starting</item>
<item quantity="other">• %d folders starting</item>
</plurals>
<!-- Number of devices currently connected. -->
<plurals name="connected_devices">
<item quantity="one">• %d device connected</item>
<item quantity="other">• %d devices connected</item>
</plurals>

<!-- Text shown in quick settings tile when in auto mode. Keep text as short as possible. -->
<string name="quick_settings_tile_auto_mode">Auto mode</string>
<!-- Text shown in quick settings tile when in manual mode and Syncthing is started. Keep text as short as possible. -->
Expand Down
85 changes: 65 additions & 20 deletions stbridge/stbridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,14 @@ type SyncthingStatusReceiver interface {
// Can be sent before OnSyncthingStarted, but not after OnSyncthingStopped.
OnAlertsUpdated(count int32)

OnBusyFoldersUpdated(count int32)
OnFolderStatesUpdated(
idle int32,
scanning int32,
syncing int32,
cleaning int32,
errored int32,
starting int32,
)

OnConnectedDevicesUpdated(count int32)
}
Expand Down Expand Up @@ -365,30 +372,68 @@ func dispatchAlerts(
receiver.OnAlertsUpdated(int32(count))
}

// The scanning states are intentionally excluded because we only want mutating
// operations to interrupt the idle timer.
var busyEvents = []string{
model.FolderSyncWaiting.String(),
model.FolderSyncPreparing.String(),
model.FolderSyncing.String(),
model.FolderCleaning.String(),
model.FolderCleanWaiting.String(),
model.FolderStarting.String(),
}
var (
idleEvents = []string{
model.FolderIdle.String(),
}
scanEvents = []string{
model.FolderScanning.String(),
model.FolderScanWaiting.String(),
}
syncEvents = []string{
model.FolderSyncWaiting.String(),
model.FolderSyncPreparing.String(),
model.FolderSyncing.String(),
}
cleanEvents = []string{
model.FolderCleaning.String(),
model.FolderCleanWaiting.String(),
}
errorEvents = []string{
model.FolderError.String(),
}
startEvents = []string{
model.FolderStarting.String(),
}
)

func dispatchBusyFolders(
func dispatchFolderStates(
folderStates map[string]string,
receiver SyncthingStatusReceiver,
) {
busyCount := int32(0)

for _, state := range folderStates {
if slices.Contains(busyEvents, state) {
busyCount += 1
idle := int32(0)
scanning := int32(0)
syncing := int32(0)
cleaning := int32(0)
errored := int32(0)
starting := int32(0)

for folderID, state := range folderStates {
if slices.Contains(idleEvents, state) {
idle += 1
} else if slices.Contains(scanEvents, state) {
scanning += 1
} else if slices.Contains(syncEvents, state) {
syncing += 1
} else if slices.Contains(cleanEvents, state) {
cleaning += 1
} else if slices.Contains(errorEvents, state) {
errored += 1
} else if slices.Contains(startEvents, state) {
starting += 1
} else {
log.Printf("Unknown folder state: %v: %v", folderID, state)
}
}

receiver.OnBusyFoldersUpdated(busyCount)
receiver.OnFolderStatesUpdated(
idle,
scanning,
syncing,
cleaning,
errored,
starting,
)
}

func dispatchConnectedDevices(
Expand Down Expand Up @@ -529,7 +574,7 @@ func eventLoop(

folderStates[folder] = state

dispatchBusyFolders(folderStates, receiver)
dispatchFolderStates(folderStates, receiver)

case events.DeviceConnected:
data := evt.Data.(map[string]string)
Expand Down Expand Up @@ -573,7 +618,7 @@ func eventLoop(
}

dispatchConflicts(conflictsInfo, receiver)
dispatchBusyFolders(folderStates, receiver)
dispatchFolderStates(folderStates, receiver)
// Unlike folders, we don't need to remove deleted devices from
// devicesConnected. We'll always receive a disconnection event
// when connections are closed during deletion.
Expand Down