diff --git a/app/src/main/java/com/chiller3/basicsync/Notifications.kt b/app/src/main/java/com/chiller3/basicsync/Notifications.kt index f889bbb6..ba8b981a 100644 --- a/app/src/main/java/com/chiller3/basicsync/Notifications.kt +++ b/app/src/main/java/com/chiller3/basicsync/Notifications.kt @@ -118,7 +118,7 @@ class Notifications(private val context: Context) { setOngoing(true) setOnlyAlertOnce(true) - if (runState.showFolderStates) { + if (state.showDetails && runState.showFolderStates) { setContentText(buildString { append(context.resources.getQuantityString( R.plurals.device_state_connected, diff --git a/app/src/main/java/com/chiller3/basicsync/Preferences.kt b/app/src/main/java/com/chiller3/basicsync/Preferences.kt index cc4c44cd..b214efa7 100644 --- a/app/src/main/java/com/chiller3/basicsync/Preferences.kt +++ b/app/src/main/java/com/chiller3/basicsync/Preferences.kt @@ -20,9 +20,10 @@ class Preferences(context: Context) { const val PREF_RESPECT_BATTERY_SAVER = "respect_battery_saver" const val PREF_RESPECT_AUTO_SYNC_DATA = "respect_auto_sync_data" const val PREF_KEEP_ALIVE = "keep_alive" + const val PREF_SHOW_DETAILS = "show_details" + const val PREF_SHOW_EXIT = "show_exit" const val PREF_REMOTE_CONTROL = "remote_control" const val PREF_ALLOW_AUTO_MODE = "allow_auto_mode" - const val PREF_SHOW_EXIT = "show_exit" const val PREF_START_ON_BOOT = "start_on_boot" const val PREF_REQUIRE_UNMETERED_NETWORK = "require_unmetered_network" const val PREF_NETWORK_ALLOW_WIFI = "network_allow_wifi" @@ -74,6 +75,14 @@ class Preferences(context: Context) { get() = prefs.getBoolean(PREF_KEEP_ALIVE, true) set(enabled) = prefs.edit { putBoolean(PREF_KEEP_ALIVE, enabled) } + var showDetails: Boolean + get() = prefs.getBoolean(PREF_SHOW_DETAILS, true) + set(enabled) = prefs.edit { putBoolean(PREF_SHOW_DETAILS, enabled) } + + var showExit: Boolean + get() = prefs.getBoolean(PREF_SHOW_EXIT, false) + set(enabled) = prefs.edit { putBoolean(PREF_SHOW_EXIT, enabled) } + var remoteControl: Boolean get() = prefs.getBoolean(PREF_REMOTE_CONTROL, false) set(enabled) = prefs.edit { putBoolean(PREF_REMOTE_CONTROL, enabled) } @@ -82,10 +91,6 @@ class Preferences(context: Context) { get() = prefs.getBoolean(PREF_ALLOW_AUTO_MODE, true) set(enabled) = prefs.edit { putBoolean(PREF_ALLOW_AUTO_MODE, enabled) } - var showExit: Boolean - get() = prefs.getBoolean(PREF_SHOW_EXIT, false) - set(enabled) = prefs.edit { putBoolean(PREF_SHOW_EXIT, enabled) } - var startOnBoot: Boolean get() = prefs.getBoolean(PREF_START_ON_BOOT, true) set(enabled) = prefs.edit { putBoolean(PREF_START_ON_BOOT, enabled) } diff --git a/app/src/main/java/com/chiller3/basicsync/settings/SettingsScreen.kt b/app/src/main/java/com/chiller3/basicsync/settings/SettingsScreen.kt index 048c5ba0..3dbb0648 100644 --- a/app/src/main/java/com/chiller3/basicsync/settings/SettingsScreen.kt +++ b/app/src/main/java/com/chiller3/basicsync/settings/SettingsScreen.kt @@ -108,9 +108,10 @@ fun SettingsScreen( val respectBatterySaver = remember(reloadPrefs) { prefs.respectBatterySaver } val respectAutoSyncData = remember(reloadPrefs) { prefs.respectAutoSyncData } val keepAlive = remember(reloadPrefs) { prefs.keepAlive } + val showDetails = remember(reloadPrefs) { prefs.showDetails } + val showExit = remember(reloadPrefs) { prefs.showExit } val remoteControl = remember(reloadPrefs) { prefs.remoteControl } val allowAutoMode = remember(reloadPrefs) { prefs.allowAutoMode } - val showExit = remember(reloadPrefs) { prefs.showExit } val startOnBoot = remember(reloadPrefs) { prefs.startOnBoot } val isDebugMode = remember(reloadPrefs) { prefs.isDebugMode } @@ -330,9 +331,10 @@ fun SettingsScreen( respectBatterySaver = respectBatterySaver, respectAutoSyncData = respectAutoSyncData, keepAlive = keepAlive, + showDetails = showDetails, + showExit = showExit, remoteControl = remoteControl, allowAutoMode = allowAutoMode, - showExit = showExit, startOnBoot = startOnBoot, isDebugMode = isDebugMode, onInhibitBatteryOptGrant = { @@ -440,6 +442,14 @@ fun SettingsScreen( prefs.keepAlive = enabled reloadPrefs++ }, + onShowDetailsChange = { enabled -> + prefs.showDetails = enabled + reloadPrefs++ + }, + onShowExitChange = { enabled -> + prefs.showExit = enabled + reloadPrefs++ + }, onRemoteControlChange = { enabled -> prefs.remoteControl = enabled reloadPrefs++ @@ -456,10 +466,6 @@ fun SettingsScreen( SyncthingService.start(context, action) }, - onShowExitChange = { enabled -> - prefs.showExit = enabled - reloadPrefs++ - }, onStartOnBootChange = { enabled -> prefs.startOnBoot = enabled reloadPrefs++ @@ -566,9 +572,10 @@ private fun SettingsContent( respectBatterySaver: Boolean, respectAutoSyncData: Boolean, keepAlive: Boolean, + showDetails: Boolean, + showExit: Boolean, remoteControl: Boolean, allowAutoMode: Boolean, - showExit: Boolean, startOnBoot: Boolean, isDebugMode: Boolean, onInhibitBatteryOptGrant: () -> Unit, @@ -590,9 +597,10 @@ private fun SettingsContent( onRespectAutoSyncDataChange: (Boolean) -> Unit, onSyncScheduleSettingsOpen: () -> Unit, onKeepAliveChange: (Boolean) -> Unit, + onShowDetailsChange: (Boolean) -> Unit, + onShowExitChange: (Boolean) -> Unit, onRemoteControlChange: (Boolean) -> Unit, onAllowAutoModeChange: (Boolean) -> Unit, - onShowExitChange: (Boolean) -> Unit, onStartOnBootChange: (Boolean) -> Unit, onDebugModeChange: (Boolean) -> Unit, onSourceRepoOpen: () -> Unit, @@ -844,6 +852,35 @@ private fun SettingsContent( ) } + item(key = "notifications") { + PreferenceCategory( + title = { Text(text = stringResource(R.string.pref_header_notifications)) }, + modifier = Modifier.animateItem(), + ) + } + + item(key = "show_details") { + SwitchPreference( + checked = showDetails, + onCheckedChange = onShowDetailsChange, + shapes = BetterSegmentedShapes.top(), + title = { Text(text = stringResource(R.string.pref_show_details_name)) }, + summary = { Text(text = stringResource(R.string.pref_show_details_desc)) }, + modifier = Modifier.animateItem(), + ) + } + + item(key = "show_exit") { + SwitchPreference( + checked = showExit, + onCheckedChange = onShowExitChange, + shapes = BetterSegmentedShapes.bottom(), + title = { Text(text = stringResource(R.string.pref_show_exit_name)) }, + summary = { Text(text = stringResource(R.string.pref_show_exit_desc)) }, + modifier = Modifier.animateItem(), + ) + } + item(key = "advanced") { PreferenceCategory( title = { Text(text = stringResource(R.string.pref_header_advanced)) }, @@ -873,17 +910,6 @@ private fun SettingsContent( ) } - item(key = "show_exit") { - SwitchPreference( - checked = showExit, - onCheckedChange = onShowExitChange, - shapes = BetterSegmentedShapes.middle(), - title = { Text(text = stringResource(R.string.pref_show_exit_name)) }, - summary = { Text(text = stringResource(R.string.pref_show_exit_desc)) }, - modifier = Modifier.animateItem(), - ) - } - item(key = "start_on_boot") { SwitchPreference( checked = startOnBoot, @@ -1016,6 +1042,7 @@ private fun PreviewSettingsScreen() { manualMode = false, allowAutoMode = true, preRunAction = null, + showDetails = true, showExit = false, folderStates = SyncthingService.FolderStates(), deviceStates = SyncthingService.DeviceStates(), @@ -1042,9 +1069,10 @@ private fun PreviewSettingsScreen() { respectBatterySaver = true, respectAutoSyncData = true, keepAlive = false, + showDetails = true, + showExit = false, remoteControl = false, allowAutoMode = true, - showExit = false, startOnBoot = true, isDebugMode = true, onInhibitBatteryOptGrant = {}, @@ -1066,9 +1094,10 @@ private fun PreviewSettingsScreen() { onRespectAutoSyncDataChange = {}, onSyncScheduleSettingsOpen = {}, onKeepAliveChange = {}, + onShowDetailsChange = {}, + onShowExitChange = {}, onRemoteControlChange = {}, onAllowAutoModeChange = {}, - onShowExitChange = {}, onStartOnBootChange = {}, onDebugModeChange = {}, onSourceRepoOpen = {}, diff --git a/app/src/main/java/com/chiller3/basicsync/syncthing/SyncthingService.kt b/app/src/main/java/com/chiller3/basicsync/syncthing/SyncthingService.kt index 7909d2be..a2f38404 100644 --- a/app/src/main/java/com/chiller3/basicsync/syncthing/SyncthingService.kt +++ b/app/src/main/java/com/chiller3/basicsync/syncthing/SyncthingService.kt @@ -52,6 +52,7 @@ class SyncthingService : Service(), SyncthingStatusReceiver, DeviceStateListener ) private val STATE_CHANGE_PREFS = arrayOf( Preferences.PREF_KEEP_ALIVE, + Preferences.PREF_SHOW_DETAILS, Preferences.PREF_SHOW_EXIT, ) @@ -174,10 +175,25 @@ class SyncthingService : Service(), SyncthingStatusReceiver, DeviceStateListener private val manualMode: Boolean, private val allowAutoMode: Boolean, private val preRunAction: PreRunAction?, + val showDetails: Boolean, private val showExit: Boolean, val folderStates: FolderStates, val deviceStates: DeviceStates, ) { + fun equivalent(prev: ServiceState?): Boolean = + prev != null + && keepAlive == prev.keepAlive + && blockedReasons == prev.blockedReasons + && isStarted == prev.isStarted + && isResumed == prev.isResumed + && manualMode == prev.manualMode + && allowAutoMode == prev.allowAutoMode + && preRunAction == prev.preRunAction + && showDetails == prev.showDetails + && showExit == prev.showExit + && (!showDetails || (folderStates == prev.folderStates + && deviceStates == prev.deviceStates)) + private val shouldResume: Boolean get() = blockedReasons.isEmpty() @@ -654,7 +670,7 @@ class SyncthingService : Service(), SyncthingStatusReceiver, DeviceStateListener return } - val notificationState = ServiceState( + val serviceState = ServiceState( keepAlive = prefs.keepAlive, blockedReasons = blockedReasons, isStarted = isStarted, @@ -662,43 +678,49 @@ class SyncthingService : Service(), SyncthingStatusReceiver, DeviceStateListener manualMode = prefs.isManualMode, allowAutoMode = prefs.allowAutoMode, preRunAction = currentPreRunAction, + showDetails = prefs.showDetails, showExit = prefs.showExit, folderStates = syncthingFolderStates, deviceStates = syncthingDeviceStates, ) - val wasChanged = notificationState != lastServiceState + val wasChanged = serviceState != lastServiceState if (wasChanged || forceShowNotification) { if (wasChanged) { - deviceStateTracker.updateBusyFolders(notificationState.folderStates) - deviceStateTracker.updateConnectedDevices(notificationState.deviceStates) + deviceStateTracker.updateBusyFolders(serviceState.folderStates) + deviceStateTracker.updateConnectedDevices(serviceState.deviceStates) val guiInfo = guiInfo - allListeners { it.onRunStateChanged(notificationState, guiInfo) } + allListeners { it.onRunStateChanged(serviceState, guiInfo) } } - val (id, notification) = notifications.createPersistentNotification(notificationState) val useLocation = deviceStateTracker.canUseLocation() - var type = 0 + val locationChanged = useLocation != lastUseLocation + val notificationChanged = !serviceState.equivalent(lastServiceState) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { - type = type or ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && useLocation) { - type = type or ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION - } + if (locationChanged || notificationChanged || forceShowNotification) { + val (id, notification) = notifications.createPersistentNotification(serviceState) + var type = 0 - ServiceCompat.startForeground(this, id, notification, type) - notifications.cancelOppositePersistentNotification(id) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + type = type or ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && useLocation) { + type = type or ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION + } + + ServiceCompat.startForeground(this, id, notification, type) + notifications.cancelOppositePersistentNotification(id) + } - if (lastUseLocation != useLocation) { + if (locationChanged) { deviceStateTracker.refreshNetworkState() lastUseLocation = useLocation } - lastServiceState = notificationState + lastServiceState = serviceState } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8c7157e2..764bb35f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -10,6 +10,8 @@ Configuration Run conditions + + Notifications Advanced @@ -100,6 +102,10 @@ Show Exit button Show an Exit button in the persistent notification. The app will automatically start again after a reboot or when receiving a remote control command. + + Detailed notifications + + Show the status of shared folders and connected devices in the persistent notification. Start on boot