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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ The app is intentionally kept very basic so that the project is easy to maintain
* BasicSync does not and will never have ads or telemetry of its own.
* Syncthing has opt-in telemetry and asks for approval before any data it sent.
* Syncthing's crash reporting is disabled because BasicSync integrates it in a way that is not supported upstream.
* `READ_SYNC_SETTINGS`
* Used for detecting if Android's auto-sync data setting is enabled.
* `ACCESS_NETWORK_STATE`
* Used for detecting when the device is connected to the network and if the network is unmetered.
* `MANAGE_EXTERNAL_STORAGE` (Android >=11), `READ_EXTERNAL_STORAGE`/`WRITE_EXTERNAL_STORAGE` (Android <11)
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29" />

<!-- For checking if auto-sync data setting is enabled. -->
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />

<!-- For monitoring network connection state for network-based run conditions. -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Expand Down
5 changes: 5 additions & 0 deletions app/src/main/java/com/chiller3/basicsync/Preferences.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class Preferences(context: Context) {
const val PREF_RUN_ON_BATTERY = "run_on_battery"
const val PREF_MIN_BATTERY_LEVEL = "min_battery_level"
const val PREF_RESPECT_BATTERY_SAVER = "respect_battery_saver"
const val PREF_RESPECT_AUTO_SYNC_DATA = "respect_auto_sync_data"
const val PREF_SYNC_SCHEDULE = "sync_schedule"
const val PREF_KEEP_ALIVE = "keep_alive"

Expand Down Expand Up @@ -83,6 +84,10 @@ class Preferences(context: Context) {
get() = prefs.getBoolean(PREF_RESPECT_BATTERY_SAVER, true)
set(enabled) = prefs.edit { putBoolean(PREF_RESPECT_BATTERY_SAVER, enabled) }

var respectAutoSyncData: Boolean
get() = prefs.getBoolean(PREF_RESPECT_AUTO_SYNC_DATA, true)
set(enabled) = prefs.edit { putBoolean(PREF_RESPECT_AUTO_SYNC_DATA, enabled) }

var keepAlive: Boolean
get() = prefs.getBoolean(PREF_KEEP_ALIVE, true)
set(enabled) = prefs.edit { putBoolean(PREF_KEEP_ALIVE, enabled) }
Expand Down
75 changes: 50 additions & 25 deletions app/src/main/java/com/chiller3/basicsync/syncthing/DeviceState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
package com.chiller3.basicsync.syncthing

import android.content.BroadcastReceiver
import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.SharedPreferences
import android.content.SyncStatusObserver
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
Expand Down Expand Up @@ -48,6 +50,7 @@ data class DeviceState(
val isPluggedIn: Boolean = false,
val batteryLevel: Int = 0,
val isBatterySaver: Boolean = false,
val isAutoSyncData: Boolean = false,
val isInTimeWindow: Boolean = false,
val proxyInfo: ProxyInfo = ProxyInfo("", ""),
) {
Expand All @@ -64,6 +67,7 @@ data class DeviceState(
Preferences.PREF_RUN_ON_BATTERY,
Preferences.PREF_MIN_BATTERY_LEVEL,
Preferences.PREF_RESPECT_BATTERY_SAVER,
Preferences.PREF_RESPECT_AUTO_SYNC_DATA,
)

fun normalizeSsid(ssid: String): String? =
Expand Down Expand Up @@ -128,6 +132,11 @@ data class DeviceState(
return false
}

if (prefs.respectAutoSyncData && !isAutoSyncData) {
Log.d(TAG, "Blocked due to auto-sync data status")
return false
}

if (!isInTimeWindow) {
Log.d(TAG, "Blocked due to execution time window")
return false
Expand Down Expand Up @@ -300,33 +309,15 @@ class DeviceStateTracker(private val context: Context) :
}
}

private val proxyChangeReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val proxyInfo = getProxyInfo()

Log.d(TAG, "Proxy settings changed: $proxyInfo")
private val autoSyncObserver = SyncStatusObserver {
val canSync = ContentResolver.getMasterSyncAutomatically()

state = state.copy(proxyInfo = proxyInfo)
}
}

private fun registerNetworkCallback() {
if (registeredNetworkCallback != null) {
throw IllegalStateException("Network callback already registered")
}
Log.d(TAG, "Auto-sync data changed: $canSync")

val networkCallback = networkCallback
connectivityManager.registerDefaultNetworkCallback(networkCallback)
registeredNetworkCallback = networkCallback
state = state.copy(isAutoSyncData = canSync)
}

private fun unregisterNetworkCallback() {
val callback = registeredNetworkCallback
?: throw IllegalStateException("No network callback registered")

connectivityManager.unregisterNetworkCallback(callback)
registeredNetworkCallback = null
}
private var autoSyncHandle: Any? = null

private val timeWindowCallback = object : Runnable {
override fun run() {
Expand Down Expand Up @@ -361,6 +352,34 @@ class DeviceStateTracker(private val context: Context) :
}
}

private val proxyChangeReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val proxyInfo = getProxyInfo()

Log.d(TAG, "Proxy settings changed: $proxyInfo")

state = state.copy(proxyInfo = proxyInfo)
}
}

private fun registerNetworkCallback() {
if (registeredNetworkCallback != null) {
throw IllegalStateException("Network callback already registered")
}

val networkCallback = networkCallback
connectivityManager.registerDefaultNetworkCallback(networkCallback)
registeredNetworkCallback = networkCallback
}

private fun unregisterNetworkCallback() {
val callback = registeredNetworkCallback
?: throw IllegalStateException("No network callback registered")

connectivityManager.unregisterNetworkCallback(callback)
registeredNetworkCallback = null
}

override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
when (key) {
Preferences.PREF_SYNC_SCHEDULE,
Expand Down Expand Up @@ -388,11 +407,16 @@ class DeviceStateTracker(private val context: Context) :
batterySaverReceiver,
IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED),
)
autoSyncHandle = ContentResolver.addStatusChangeListener(
ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS,
autoSyncObserver,
)
autoSyncObserver.onStatusChanged(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS)
timeWindowCallback.run()
context.registerReceiver(
proxyChangeReceiver,
IntentFilter(Proxy.PROXY_CHANGE_ACTION),
)
timeWindowCallback.run()

this.listener = listener

Expand All @@ -408,8 +432,9 @@ class DeviceStateTracker(private val context: Context) :
unregisterNetworkCallback()
context.unregisterReceiver(batteryStatusReceiver)
context.unregisterReceiver(batterySaverReceiver)
context.unregisterReceiver(proxyChangeReceiver)
ContentResolver.removeStatusChangeListener(autoSyncHandle)
handler.removeCallbacks(timeWindowCallback)
context.unregisterReceiver(proxyChangeReceiver)

this.listener = null
}
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
<string name="pref_run_on_battery_desc">Run when the device is on battery power and the battery level is at least %1$d%%. Syncthing always runs when plugged in.</string>
<string name="pref_respect_battery_saver_name">Respect battery saver mode</string>
<string name="pref_respect_battery_saver_desc">Only run when Android\'s battery saver mode is disabled.</string>
<string name="pref_respect_auto_sync_data_name">Respect auto-sync data setting</string>
<string name="pref_respect_auto_sync_data_desc">Only run when Android\'s auto-sync data setting is enabled.</string>
<string name="pref_sync_schedule_name">Sync on time schedule</string>
<string name="pref_sync_schedule_desc">Sync for %2$s during every window of %1$s.</string>
<string name="pref_keep_alive_name">Keep Syncthing alive</string>
Expand Down
7 changes: 7 additions & 0 deletions app/src/main/res/xml/preferences_root.xml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,13 @@
app:summary="@string/pref_respect_battery_saver_desc"
app:iconSpaceReserved="false" />

<SwitchPreferenceCompat
app:key="respect_auto_sync_data"
app:defaultValue="true"
app:title="@string/pref_respect_auto_sync_data_name"
app:summary="@string/pref_respect_auto_sync_data_desc"
app:iconSpaceReserved="false" />

<com.chiller3.basicsync.view.SplitSwitchPreference
app:key="sync_schedule"
app:title="@string/pref_sync_schedule_name"
Expand Down