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
8 changes: 5 additions & 3 deletions app/src/main/java/com/chiller3/basicsync/Notifications.kt
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,14 @@ class Notifications(private val context: Context) {
if (runState.showFolderStates) {
setContentText(buildString {
append(context.resources.getQuantityString(
R.plurals.connected_devices,
state.connectedDevices,
state.connectedDevices,
R.plurals.device_state_connected,
state.deviceStates.connected,
state.deviceStates.connected,
))

for ((resId, count) in arrayOf(
R.plurals.device_state_syncing to state.deviceStates.syncing,
R.plurals.device_state_pending to state.deviceStates.pending,
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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -926,7 +926,7 @@ private fun PreviewSettingsScreen() {
preRunAction = null,
showExit = false,
folderStates = SyncthingService.FolderStates(),
connectedDevices = 0,
deviceStates = SyncthingService.DeviceStates(),
)

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

fun updateConnectedDevices(count: Int) {
fun updateConnectedDevices(deviceStates: SyncthingService.DeviceStates) {
val count = deviceStates.connected

handler.post {
if (state.connectedDevices != count) {
val connectedBefore = state.connectedOnce
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class SyncthingService : Service(), SyncthingStatusReceiver, DeviceStateListener
private val preRunAction: PreRunAction?,
private val showExit: Boolean,
val folderStates: FolderStates,
val connectedDevices: Int,
val deviceStates: DeviceStates,
) {
private val shouldResume: Boolean
get() = blockedReasons.isEmpty()
Expand Down Expand Up @@ -202,6 +202,18 @@ class SyncthingService : Service(), SyncthingStatusReceiver, DeviceStateListener
)
}

data class DeviceStates(
val connected: Int,
val syncing: Int,
val pending: Int,
) {
constructor() : this(
connected = 0,
syncing = 0,
pending = 0,
)
}

private lateinit var prefs: Preferences
private lateinit var notifications: Notifications
private val runnerThread = Thread(::runner)
Expand Down Expand Up @@ -266,7 +278,7 @@ class SyncthingService : Service(), SyncthingStatusReceiver, DeviceStateListener
@GuardedBy("stateLock")
private var syncthingFolderStates = FolderStates()
@GuardedBy("stateLock")
private var syncthingConnectedDevices = 0
private var syncthingDeviceStates = DeviceStates()

private val isResumed: Boolean
@GuardedBy("stateLock")
Expand Down Expand Up @@ -471,15 +483,15 @@ class SyncthingService : Service(), SyncthingStatusReceiver, DeviceStateListener
preRunAction = currentPreRunAction,
showExit = prefs.showExit,
folderStates = syncthingFolderStates,
connectedDevices = syncthingConnectedDevices,
deviceStates = syncthingDeviceStates,
)

val wasChanged = notificationState != lastServiceState

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

val guiInfo = guiInfo

Expand Down Expand Up @@ -627,7 +639,7 @@ class SyncthingService : Service(), SyncthingStatusReceiver, DeviceStateListener
syncthingConflicts = emptyList()
syncthingAlerts = 0
syncthingFolderStates = FolderStates()
syncthingConnectedDevices = 0
syncthingDeviceStates = DeviceStates()
syncthingApp = null

stateChanged()
Expand Down Expand Up @@ -680,9 +692,13 @@ class SyncthingService : Service(), SyncthingStatusReceiver, DeviceStateListener
}

@WorkerThread
override fun onConnectedDevicesUpdated(count: Int) {
override fun onDeviceStatesUpdated(connected: Int, syncing: Int, pending: Int) {
synchronized(stateLock) {
syncthingConnectedDevices = count
syncthingDeviceStates = DeviceStates(
connected = connected,
syncing = syncing,
pending = pending,
)
stateChanged()
}
}
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/values-pl/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@
<item quantity="many">• %d folderów uruchamianych</item>
<item quantity="other">• %d folderów uruchamianych</item>
</plurals>
<plurals name="connected_devices">
<plurals name="device_state_connected">
<item quantity="one">• %d podłączone urządzenie</item>
<item quantity="few">• %d podłączone urządzenia</item>
<item quantity="many">• %d podłączonych urządzeń</item>
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/values-tr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@
<item quantity="one">• %d klasör başlatılıyor</item>
<item quantity="other">• %d klasör başlatılıyor</item>
</plurals>
<plurals name="connected_devices">
<plurals name="device_state_connected">
<item quantity="one">• %d cihaz bağlandı</item>
<item quantity="other">• %d cihaz bağlandı</item>
</plurals>
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/values-zh-rCN/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@
<plurals name="folder_state_starting">
<item quantity="other">• %d 个文件夹正在启动</item>
</plurals>
<plurals name="connected_devices">
<plurals name="device_state_connected">
<item quantity="other">• %d 台设备已连接</item>
</plurals>
</resources>
12 changes: 11 additions & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -337,10 +337,20 @@
<item quantity="other">• %d folders starting</item>
</plurals>
<!-- Number of devices currently connected. -->
<plurals name="connected_devices">
<plurals name="device_state_connected">
<item quantity="one">• %d device connected</item>
<item quantity="other">• %d devices connected</item>
</plurals>
<!-- Number of devices currently syncing. -->
<plurals name="device_state_syncing">
<item quantity="one">• %d device syncing</item>
<item quantity="other">• %d devices syncing</item>
</plurals>
<!-- Number of devices with data that needs to be synced once connected. -->
<plurals name="device_state_pending">
<item quantity="one">• %d device pending sync</item>
<item quantity="other">• %d devices pending sync</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>
Expand Down
113 changes: 94 additions & 19 deletions stbridge/stbridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,11 @@ type SyncthingStatusReceiver interface {
starting int32,
)

OnConnectedDevicesUpdated(count int32)
OnDeviceStatesUpdated(
connected int32,
syncing int32,
pending int32,
)
}

// db.DB is internal, so it's unnameable and we can't create a function that
Expand Down Expand Up @@ -436,11 +440,28 @@ func dispatchFolderStates(
)
}

func dispatchConnectedDevices(
devicesConnected map[string]struct{},
type deviceStates struct {
connected map[string]struct{}
dirty map[string]map[string]struct{}
}

func dispatchDeviceStates(
deviceStates *deviceStates,
receiver SyncthingStatusReceiver,
) {
receiver.OnConnectedDevicesUpdated(int32(len(devicesConnected)))
connected := int32(len(deviceStates.connected))
syncing := int32(0)
pending := int32(0)

for deviceID, _ := range deviceStates.dirty {
if _, ok := deviceStates.connected[deviceID]; ok {
syncing += 1
} else {
pending += 1
}
}

receiver.OnDeviceStatesUpdated(connected, syncing, pending)
}

func eventLoop(
Expand All @@ -463,12 +484,16 @@ func eventLoop(
events.StateChanged |
events.DeviceConnected |
events.DeviceDisconnected |
events.FolderCompletion |
events.ConfigSaved,
)
defer sub.Unsubscribe()

devicesConnected := map[string]struct{}{}
folderStates := map[string]string{}
deviceStates := deviceStates{
connected: map[string]struct{}{},
dirty: map[string]map[string]struct{}{},
}

for {
select {
Expand Down Expand Up @@ -580,17 +605,46 @@ func eventLoop(
data := evt.Data.(map[string]string)
deviceID := data["id"]

devicesConnected[deviceID] = struct{}{}
deviceStates.connected[deviceID] = struct{}{}

dispatchConnectedDevices(devicesConnected, receiver)
dispatchDeviceStates(&deviceStates, receiver)

case events.DeviceDisconnected:
data := evt.Data.(map[string]string)
deviceID := data["id"]

delete(devicesConnected, deviceID)
delete(deviceStates.connected, deviceID)

dispatchDeviceStates(&deviceStates, receiver)

case events.FolderCompletion:
data := evt.Data.(map[string]interface{})
folderID := data["folder"].(string)
deviceID := data["device"].(string)

needBytes := data["needBytes"].(int64)
needItems := data["needItems"].(int)
needDeletes := data["needDeletes"].(int)

syncing := needBytes != 0 || needItems != 0 || needDeletes != 0

dispatchConnectedDevices(devicesConnected, receiver)
if syncing {
if _, ok := deviceStates.dirty[deviceID]; !ok {
deviceStates.dirty[deviceID] = map[string]struct{}{}
}
if _, ok := deviceStates.dirty[deviceID][folderID]; !ok {
deviceStates.dirty[deviceID][folderID] = struct{}{}
}
} else {
if folders, ok := deviceStates.dirty[deviceID]; ok {
delete(folders, folderID)
if len(folders) == 0 {
delete(deviceStates.dirty, deviceID)
}
}
}

dispatchDeviceStates(&deviceStates, receiver)

// When a folder is deleted, we need to manually clear out the
// corresponding conflicts because we will not receive deletion
Expand All @@ -605,23 +659,44 @@ func eventLoop(
conflictsInfo.folderPaths[folder.ID], _ = fs.ExpandTilde(folder.Path)
}

for key := range conflictsInfo.byFolder {
if _, ok := conflictsInfo.folderPaths[key]; !ok {
delete(conflictsInfo.byFolder, key)
for folderID := range conflictsInfo.byFolder {
if _, ok := conflictsInfo.folderPaths[folderID]; !ok {
delete(conflictsInfo.byFolder, folderID)
}
}

for key := range folderStates {
if _, ok := conflictsInfo.folderPaths[key]; !ok {
delete(folderStates, key)
dispatchConflicts(conflictsInfo, receiver)

for folderID := range folderStates {
if _, ok := conflictsInfo.folderPaths[folderID]; !ok {
delete(folderStates, folderID)
}
}

dispatchConflicts(conflictsInfo, 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.

devices := map[string]struct{}{}

for _, device := range cfg.Devices {
devices[device.DeviceID.String()] = struct{}{}
}

// We don't need to clean up deviceStates.connected because
// we'll always receive a disconnection event when connections
// are closed during deletion.

for deviceID, folders := range deviceStates.dirty {
for folderID := range folders {
if _, ok := conflictsInfo.folderPaths[folderID]; !ok {
delete(folders, folderID)
}
}
if _, ok := devices[deviceID]; !ok || len(folders) == 0 {
delete(deviceStates.dirty, deviceID)
}
}

dispatchDeviceStates(&deviceStates, receiver)

// A config.Wrapper is needed to determine if a restart is
// required. config.Configuration does not contain enough info.
Expand Down