Add Android backup roaming control
This commit is contained in:
@@ -48,8 +48,10 @@ public abstract class PreferenceManager {
|
|||||||
public static final String PREF__CAMERA_VIDEO_UPLOADS_ENABLED = "enable_video_uploads";
|
public static final String PREF__CAMERA_VIDEO_UPLOADS_ENABLED = "enable_video_uploads";
|
||||||
public static final String PREF__CAMERA_PICTURE_UPLOADS_WIFI_ONLY = "picture_uploads_on_wifi";
|
public static final String PREF__CAMERA_PICTURE_UPLOADS_WIFI_ONLY = "picture_uploads_on_wifi";
|
||||||
public static final String PREF__CAMERA_PICTURE_UPLOADS_CHARGING_ONLY = "picture_uploads_on_charging";
|
public static final String PREF__CAMERA_PICTURE_UPLOADS_CHARGING_ONLY = "picture_uploads_on_charging";
|
||||||
|
public static final String PREF__CAMERA_PICTURE_UPLOADS_ALLOW_ROAMING = "picture_uploads_allow_roaming";
|
||||||
public static final String PREF__CAMERA_VIDEO_UPLOADS_WIFI_ONLY = "video_uploads_on_wifi";
|
public static final String PREF__CAMERA_VIDEO_UPLOADS_WIFI_ONLY = "video_uploads_on_wifi";
|
||||||
public static final String PREF__CAMERA_VIDEO_UPLOADS_CHARGING_ONLY = "video_uploads_on_charging";
|
public static final String PREF__CAMERA_VIDEO_UPLOADS_CHARGING_ONLY = "video_uploads_on_charging";
|
||||||
|
public static final String PREF__CAMERA_VIDEO_UPLOADS_ALLOW_ROAMING = "video_uploads_allow_roaming";
|
||||||
public static final String PREF__CAMERA_PICTURE_UPLOADS_PATH = "picture_uploads_path";
|
public static final String PREF__CAMERA_PICTURE_UPLOADS_PATH = "picture_uploads_path";
|
||||||
public static final String PREF__CAMERA_VIDEO_UPLOADS_PATH = "video_uploads_path";
|
public static final String PREF__CAMERA_VIDEO_UPLOADS_PATH = "video_uploads_path";
|
||||||
public static final String PREF__CAMERA_PICTURE_UPLOADS_BEHAVIOUR = "picture_uploads_behaviour";
|
public static final String PREF__CAMERA_PICTURE_UPLOADS_BEHAVIOUR = "picture_uploads_behaviour";
|
||||||
|
|||||||
+14
@@ -41,6 +41,7 @@ import androidx.preference.PreferenceFragmentCompat
|
|||||||
import androidx.preference.SwitchPreferenceCompat
|
import androidx.preference.SwitchPreferenceCompat
|
||||||
import eu.qsfera.android.R
|
import eu.qsfera.android.R
|
||||||
import eu.qsfera.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_ACCOUNT_NAME
|
import eu.qsfera.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_ACCOUNT_NAME
|
||||||
|
import eu.qsfera.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_ALLOW_ROAMING
|
||||||
import eu.qsfera.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_BEHAVIOUR
|
import eu.qsfera.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_BEHAVIOUR
|
||||||
import eu.qsfera.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_CHARGING_ONLY
|
import eu.qsfera.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_CHARGING_ONLY
|
||||||
import eu.qsfera.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_ENABLED
|
import eu.qsfera.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_ENABLED
|
||||||
@@ -68,6 +69,7 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() {
|
|||||||
private var prefEnablePictureUploads: SwitchPreferenceCompat? = null
|
private var prefEnablePictureUploads: SwitchPreferenceCompat? = null
|
||||||
private var prefPictureUploadsPath: Preference? = null
|
private var prefPictureUploadsPath: Preference? = null
|
||||||
private var prefPictureUploadsOnWifi: CheckBoxPreference? = null
|
private var prefPictureUploadsOnWifi: CheckBoxPreference? = null
|
||||||
|
private var prefPictureUploadsAllowRoaming: CheckBoxPreference? = null
|
||||||
private var prefPictureUploadsOnCharging: CheckBoxPreference? = null
|
private var prefPictureUploadsOnCharging: CheckBoxPreference? = null
|
||||||
private var prefPictureUploadsSourcePath: Preference? = null
|
private var prefPictureUploadsSourcePath: Preference? = null
|
||||||
private var prefPictureUploadsClearSourcePaths: Preference? = null
|
private var prefPictureUploadsClearSourcePaths: Preference? = null
|
||||||
@@ -100,6 +102,7 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() {
|
|||||||
prefEnablePictureUploads = findPreference(PREF__CAMERA_PICTURE_UPLOADS_ENABLED)
|
prefEnablePictureUploads = findPreference(PREF__CAMERA_PICTURE_UPLOADS_ENABLED)
|
||||||
prefPictureUploadsPath = findPreference(PREF__CAMERA_PICTURE_UPLOADS_PATH)
|
prefPictureUploadsPath = findPreference(PREF__CAMERA_PICTURE_UPLOADS_PATH)
|
||||||
prefPictureUploadsOnWifi = findPreference(PREF__CAMERA_PICTURE_UPLOADS_WIFI_ONLY)
|
prefPictureUploadsOnWifi = findPreference(PREF__CAMERA_PICTURE_UPLOADS_WIFI_ONLY)
|
||||||
|
prefPictureUploadsAllowRoaming = findPreference(PREF__CAMERA_PICTURE_UPLOADS_ALLOW_ROAMING)
|
||||||
prefPictureUploadsOnCharging = findPreference(PREF__CAMERA_PICTURE_UPLOADS_CHARGING_ONLY)
|
prefPictureUploadsOnCharging = findPreference(PREF__CAMERA_PICTURE_UPLOADS_CHARGING_ONLY)
|
||||||
prefPictureUploadsSourcePath = findPreference(PREF__CAMERA_PICTURE_UPLOADS_SOURCE)
|
prefPictureUploadsSourcePath = findPreference(PREF__CAMERA_PICTURE_UPLOADS_SOURCE)
|
||||||
prefPictureUploadsClearSourcePaths = findPreference(PREF_PICTURE_UPLOADS_CLEAR_SOURCE_PATHS)
|
prefPictureUploadsClearSourcePaths = findPreference(PREF_PICTURE_UPLOADS_CLEAR_SOURCE_PATHS)
|
||||||
@@ -157,6 +160,8 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() {
|
|||||||
prefPictureUploadsSourcePath?.summary = getSourcePathsSummary(sourcePaths)
|
prefPictureUploadsSourcePath?.summary = getSourcePathsSummary(sourcePaths)
|
||||||
prefPictureUploadsClearSourcePaths?.isEnabled = sourcePaths.isNotEmpty()
|
prefPictureUploadsClearSourcePaths?.isEnabled = sourcePaths.isNotEmpty()
|
||||||
prefPictureUploadsOnWifi?.isChecked = it.wifiOnly
|
prefPictureUploadsOnWifi?.isChecked = it.wifiOnly
|
||||||
|
prefPictureUploadsAllowRoaming?.isChecked = it.allowRoaming
|
||||||
|
prefPictureUploadsAllowRoaming?.isEnabled = !it.wifiOnly
|
||||||
prefPictureUploadsOnCharging?.isChecked = it.chargingOnly
|
prefPictureUploadsOnCharging?.isChecked = it.chargingOnly
|
||||||
prefPictureUploadsBehaviour?.value = it.behavior.name
|
prefPictureUploadsBehaviour?.value = it.behavior.name
|
||||||
prefPictureUploadsLastSync?.summary = formatLastSyncSummary(it.lastSyncTimestamp)
|
prefPictureUploadsLastSync?.summary = formatLastSyncSummary(it.lastSyncTimestamp)
|
||||||
@@ -243,6 +248,13 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() {
|
|||||||
prefPictureUploadsOnWifi?.setOnPreferenceChangeListener { _, newValue ->
|
prefPictureUploadsOnWifi?.setOnPreferenceChangeListener { _, newValue ->
|
||||||
newValue as Boolean
|
newValue as Boolean
|
||||||
picturesViewModel.useWifiOnly(newValue)
|
picturesViewModel.useWifiOnly(newValue)
|
||||||
|
prefPictureUploadsAllowRoaming?.isEnabled = !newValue
|
||||||
|
newValue
|
||||||
|
}
|
||||||
|
|
||||||
|
prefPictureUploadsAllowRoaming?.setOnPreferenceChangeListener { _, newValue ->
|
||||||
|
newValue as Boolean
|
||||||
|
picturesViewModel.allowRoaming(newValue)
|
||||||
newValue
|
newValue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,6 +289,7 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() {
|
|||||||
}
|
}
|
||||||
prefPictureUploadsPath?.isEnabled = value
|
prefPictureUploadsPath?.isEnabled = value
|
||||||
prefPictureUploadsOnWifi?.isEnabled = value
|
prefPictureUploadsOnWifi?.isEnabled = value
|
||||||
|
prefPictureUploadsAllowRoaming?.isEnabled = value && prefPictureUploadsOnWifi?.isChecked != true
|
||||||
prefPictureUploadsOnCharging?.isEnabled = value
|
prefPictureUploadsOnCharging?.isEnabled = value
|
||||||
prefPictureUploadsSourcePath?.isEnabled = value
|
prefPictureUploadsSourcePath?.isEnabled = value
|
||||||
prefPictureUploadsClearSourcePaths?.isEnabled = value && picturesViewModel.getPictureUploadsSourcePaths().isNotEmpty()
|
prefPictureUploadsClearSourcePaths?.isEnabled = value && picturesViewModel.getPictureUploadsSourcePaths().isNotEmpty()
|
||||||
@@ -291,6 +304,7 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() {
|
|||||||
prefPictureUploadsSourcePath?.summary = getString(R.string.prefs_camera_upload_source_paths_empty)
|
prefPictureUploadsSourcePath?.summary = getString(R.string.prefs_camera_upload_source_paths_empty)
|
||||||
prefPictureUploadsClearSourcePaths?.isEnabled = false
|
prefPictureUploadsClearSourcePaths?.isEnabled = false
|
||||||
prefPictureUploadsOnWifi?.isChecked = false
|
prefPictureUploadsOnWifi?.isChecked = false
|
||||||
|
prefPictureUploadsAllowRoaming?.isChecked = false
|
||||||
prefPictureUploadsOnCharging?.isChecked = false
|
prefPictureUploadsOnCharging?.isChecked = false
|
||||||
prefPictureUploadsBehaviour?.value = UploadBehavior.COPY.name
|
prefPictureUploadsBehaviour?.value = UploadBehavior.COPY.name
|
||||||
prefPictureUploadsLastSync?.summary = getString(R.string.prefs_camera_upload_last_sync_never)
|
prefPictureUploadsLastSync?.summary = getString(R.string.prefs_camera_upload_last_sync_never)
|
||||||
|
|||||||
+12
@@ -112,6 +112,16 @@ class SettingsPictureUploadsViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun allowRoaming(allowRoaming: Boolean) {
|
||||||
|
viewModelScope.launch(coroutinesDispatcherProvider.io) {
|
||||||
|
savePictureUploadsConfigurationUseCase(
|
||||||
|
SavePictureUploadsConfigurationUseCase.Params(
|
||||||
|
composePictureUploadsConfiguration(allowRoaming = allowRoaming)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun useChargingOnly(chargingOnly: Boolean) {
|
fun useChargingOnly(chargingOnly: Boolean) {
|
||||||
viewModelScope.launch(coroutinesDispatcherProvider.io) {
|
viewModelScope.launch(coroutinesDispatcherProvider.io) {
|
||||||
savePictureUploadsConfigurationUseCase(
|
savePictureUploadsConfigurationUseCase(
|
||||||
@@ -211,6 +221,7 @@ class SettingsPictureUploadsViewModel(
|
|||||||
accountName: String? = _pictureUploads.value?.accountName,
|
accountName: String? = _pictureUploads.value?.accountName,
|
||||||
uploadPath: String? = _pictureUploads.value?.uploadPath,
|
uploadPath: String? = _pictureUploads.value?.uploadPath,
|
||||||
wifiOnly: Boolean? = _pictureUploads.value?.wifiOnly,
|
wifiOnly: Boolean? = _pictureUploads.value?.wifiOnly,
|
||||||
|
allowRoaming: Boolean? = _pictureUploads.value?.allowRoaming,
|
||||||
chargingOnly: Boolean? = _pictureUploads.value?.chargingOnly,
|
chargingOnly: Boolean? = _pictureUploads.value?.chargingOnly,
|
||||||
sourcePath: String? = _pictureUploads.value?.sourcePath,
|
sourcePath: String? = _pictureUploads.value?.sourcePath,
|
||||||
behavior: UploadBehavior? = _pictureUploads.value?.behavior,
|
behavior: UploadBehavior? = _pictureUploads.value?.behavior,
|
||||||
@@ -222,6 +233,7 @@ class SettingsPictureUploadsViewModel(
|
|||||||
sourcePath = sourcePath.orEmpty(),
|
sourcePath = sourcePath.orEmpty(),
|
||||||
uploadPath = uploadPath ?: PREF__CAMERA_UPLOADS_DEFAULT_PATH,
|
uploadPath = uploadPath ?: PREF__CAMERA_UPLOADS_DEFAULT_PATH,
|
||||||
wifiOnly = wifiOnly ?: true,
|
wifiOnly = wifiOnly ?: true,
|
||||||
|
allowRoaming = allowRoaming ?: false,
|
||||||
chargingOnly = chargingOnly ?: false,
|
chargingOnly = chargingOnly ?: false,
|
||||||
lastSyncTimestamp = timestamp ?: initialSyncTimestamp,
|
lastSyncTimestamp = timestamp ?: initialSyncTimestamp,
|
||||||
name = _pictureUploads.value?.name ?: pictureUploadsName,
|
name = _pictureUploads.value?.name ?: pictureUploadsName,
|
||||||
|
|||||||
+14
@@ -42,6 +42,7 @@ import androidx.preference.SwitchPreferenceCompat
|
|||||||
import eu.qsfera.android.R
|
import eu.qsfera.android.R
|
||||||
import eu.qsfera.android.db.PreferenceManager
|
import eu.qsfera.android.db.PreferenceManager
|
||||||
import eu.qsfera.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_ACCOUNT_NAME
|
import eu.qsfera.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_ACCOUNT_NAME
|
||||||
|
import eu.qsfera.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_ALLOW_ROAMING
|
||||||
import eu.qsfera.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_BEHAVIOUR
|
import eu.qsfera.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_BEHAVIOUR
|
||||||
import eu.qsfera.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_CHARGING_ONLY
|
import eu.qsfera.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_CHARGING_ONLY
|
||||||
import eu.qsfera.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_ENABLED
|
import eu.qsfera.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_ENABLED
|
||||||
@@ -68,6 +69,7 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() {
|
|||||||
private var prefEnableVideoUploads: SwitchPreferenceCompat? = null
|
private var prefEnableVideoUploads: SwitchPreferenceCompat? = null
|
||||||
private var prefVideoUploadsPath: Preference? = null
|
private var prefVideoUploadsPath: Preference? = null
|
||||||
private var prefVideoUploadsOnWifi: CheckBoxPreference? = null
|
private var prefVideoUploadsOnWifi: CheckBoxPreference? = null
|
||||||
|
private var prefVideoUploadsAllowRoaming: CheckBoxPreference? = null
|
||||||
private var prefVideoUploadsOnCharging: CheckBoxPreference? = null
|
private var prefVideoUploadsOnCharging: CheckBoxPreference? = null
|
||||||
private var prefVideoUploadsSourcePath: Preference? = null
|
private var prefVideoUploadsSourcePath: Preference? = null
|
||||||
private var prefVideoUploadsClearSourcePaths: Preference? = null
|
private var prefVideoUploadsClearSourcePaths: Preference? = null
|
||||||
@@ -100,6 +102,7 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() {
|
|||||||
prefEnableVideoUploads = findPreference(PREF__CAMERA_VIDEO_UPLOADS_ENABLED)
|
prefEnableVideoUploads = findPreference(PREF__CAMERA_VIDEO_UPLOADS_ENABLED)
|
||||||
prefVideoUploadsPath = findPreference(PREF__CAMERA_VIDEO_UPLOADS_PATH)
|
prefVideoUploadsPath = findPreference(PREF__CAMERA_VIDEO_UPLOADS_PATH)
|
||||||
prefVideoUploadsOnWifi = findPreference(PREF__CAMERA_VIDEO_UPLOADS_WIFI_ONLY)
|
prefVideoUploadsOnWifi = findPreference(PREF__CAMERA_VIDEO_UPLOADS_WIFI_ONLY)
|
||||||
|
prefVideoUploadsAllowRoaming = findPreference(PREF__CAMERA_VIDEO_UPLOADS_ALLOW_ROAMING)
|
||||||
prefVideoUploadsOnCharging = findPreference(PREF__CAMERA_VIDEO_UPLOADS_CHARGING_ONLY)
|
prefVideoUploadsOnCharging = findPreference(PREF__CAMERA_VIDEO_UPLOADS_CHARGING_ONLY)
|
||||||
prefVideoUploadsSourcePath = findPreference(PREF__CAMERA_VIDEO_UPLOADS_SOURCE)
|
prefVideoUploadsSourcePath = findPreference(PREF__CAMERA_VIDEO_UPLOADS_SOURCE)
|
||||||
prefVideoUploadsClearSourcePaths = findPreference(PREF_VIDEO_UPLOADS_CLEAR_SOURCE_PATHS)
|
prefVideoUploadsClearSourcePaths = findPreference(PREF_VIDEO_UPLOADS_CLEAR_SOURCE_PATHS)
|
||||||
@@ -155,6 +158,8 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() {
|
|||||||
prefVideoUploadsSourcePath?.summary = getSourcePathsSummary(sourcePaths)
|
prefVideoUploadsSourcePath?.summary = getSourcePathsSummary(sourcePaths)
|
||||||
prefVideoUploadsClearSourcePaths?.isEnabled = sourcePaths.isNotEmpty()
|
prefVideoUploadsClearSourcePaths?.isEnabled = sourcePaths.isNotEmpty()
|
||||||
prefVideoUploadsOnWifi?.isChecked = it.wifiOnly
|
prefVideoUploadsOnWifi?.isChecked = it.wifiOnly
|
||||||
|
prefVideoUploadsAllowRoaming?.isChecked = it.allowRoaming
|
||||||
|
prefVideoUploadsAllowRoaming?.isEnabled = !it.wifiOnly
|
||||||
prefVideoUploadsOnCharging?.isChecked = it.chargingOnly
|
prefVideoUploadsOnCharging?.isChecked = it.chargingOnly
|
||||||
prefVideoUploadsBehaviour?.value = it.behavior.name
|
prefVideoUploadsBehaviour?.value = it.behavior.name
|
||||||
prefVideoUploadsLastSync?.summary = formatLastSyncSummary(it.lastSyncTimestamp)
|
prefVideoUploadsLastSync?.summary = formatLastSyncSummary(it.lastSyncTimestamp)
|
||||||
@@ -241,6 +246,13 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() {
|
|||||||
prefVideoUploadsOnWifi?.setOnPreferenceChangeListener { _, newValue ->
|
prefVideoUploadsOnWifi?.setOnPreferenceChangeListener { _, newValue ->
|
||||||
newValue as Boolean
|
newValue as Boolean
|
||||||
videosViewModel.useWifiOnly(newValue)
|
videosViewModel.useWifiOnly(newValue)
|
||||||
|
prefVideoUploadsAllowRoaming?.isEnabled = !newValue
|
||||||
|
newValue
|
||||||
|
}
|
||||||
|
|
||||||
|
prefVideoUploadsAllowRoaming?.setOnPreferenceChangeListener { _, newValue ->
|
||||||
|
newValue as Boolean
|
||||||
|
videosViewModel.allowRoaming(newValue)
|
||||||
newValue
|
newValue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,6 +287,7 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() {
|
|||||||
}
|
}
|
||||||
prefVideoUploadsPath?.isEnabled = value
|
prefVideoUploadsPath?.isEnabled = value
|
||||||
prefVideoUploadsOnWifi?.isEnabled = value
|
prefVideoUploadsOnWifi?.isEnabled = value
|
||||||
|
prefVideoUploadsAllowRoaming?.isEnabled = value && prefVideoUploadsOnWifi?.isChecked != true
|
||||||
prefVideoUploadsOnCharging?.isEnabled = value
|
prefVideoUploadsOnCharging?.isEnabled = value
|
||||||
prefVideoUploadsSourcePath?.isEnabled = value
|
prefVideoUploadsSourcePath?.isEnabled = value
|
||||||
prefVideoUploadsClearSourcePaths?.isEnabled = value && videosViewModel.getVideoUploadsSourcePaths().isNotEmpty()
|
prefVideoUploadsClearSourcePaths?.isEnabled = value && videosViewModel.getVideoUploadsSourcePaths().isNotEmpty()
|
||||||
@@ -289,6 +302,7 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() {
|
|||||||
prefVideoUploadsSourcePath?.summary = getString(R.string.prefs_camera_upload_source_paths_empty)
|
prefVideoUploadsSourcePath?.summary = getString(R.string.prefs_camera_upload_source_paths_empty)
|
||||||
prefVideoUploadsClearSourcePaths?.isEnabled = false
|
prefVideoUploadsClearSourcePaths?.isEnabled = false
|
||||||
prefVideoUploadsOnWifi?.isChecked = false
|
prefVideoUploadsOnWifi?.isChecked = false
|
||||||
|
prefVideoUploadsAllowRoaming?.isChecked = false
|
||||||
prefVideoUploadsOnCharging?.isChecked = false
|
prefVideoUploadsOnCharging?.isChecked = false
|
||||||
prefVideoUploadsBehaviour?.value = UploadBehavior.COPY.name
|
prefVideoUploadsBehaviour?.value = UploadBehavior.COPY.name
|
||||||
prefVideoUploadsLastSync?.summary = getString(R.string.prefs_camera_upload_last_sync_never)
|
prefVideoUploadsLastSync?.summary = getString(R.string.prefs_camera_upload_last_sync_never)
|
||||||
|
|||||||
+12
@@ -112,6 +112,16 @@ class SettingsVideoUploadsViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun allowRoaming(allowRoaming: Boolean) {
|
||||||
|
viewModelScope.launch(coroutinesDispatcherProvider.io) {
|
||||||
|
saveVideoUploadsConfigurationUseCase(
|
||||||
|
SaveVideoUploadsConfigurationUseCase.Params(
|
||||||
|
composeVideoUploadsConfiguration(allowRoaming = allowRoaming)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun useChargingOnly(chargingOnly: Boolean) {
|
fun useChargingOnly(chargingOnly: Boolean) {
|
||||||
viewModelScope.launch(coroutinesDispatcherProvider.io) {
|
viewModelScope.launch(coroutinesDispatcherProvider.io) {
|
||||||
saveVideoUploadsConfigurationUseCase(
|
saveVideoUploadsConfigurationUseCase(
|
||||||
@@ -211,6 +221,7 @@ class SettingsVideoUploadsViewModel(
|
|||||||
accountName: String? = _videoUploads.value?.accountName,
|
accountName: String? = _videoUploads.value?.accountName,
|
||||||
uploadPath: String? = _videoUploads.value?.uploadPath,
|
uploadPath: String? = _videoUploads.value?.uploadPath,
|
||||||
wifiOnly: Boolean? = _videoUploads.value?.wifiOnly,
|
wifiOnly: Boolean? = _videoUploads.value?.wifiOnly,
|
||||||
|
allowRoaming: Boolean? = _videoUploads.value?.allowRoaming,
|
||||||
chargingOnly: Boolean? = _videoUploads.value?.chargingOnly,
|
chargingOnly: Boolean? = _videoUploads.value?.chargingOnly,
|
||||||
sourcePath: String? = _videoUploads.value?.sourcePath,
|
sourcePath: String? = _videoUploads.value?.sourcePath,
|
||||||
behavior: UploadBehavior? = _videoUploads.value?.behavior,
|
behavior: UploadBehavior? = _videoUploads.value?.behavior,
|
||||||
@@ -223,6 +234,7 @@ class SettingsVideoUploadsViewModel(
|
|||||||
sourcePath = sourcePath.orEmpty(),
|
sourcePath = sourcePath.orEmpty(),
|
||||||
uploadPath = uploadPath ?: PREF__CAMERA_UPLOADS_DEFAULT_PATH,
|
uploadPath = uploadPath ?: PREF__CAMERA_UPLOADS_DEFAULT_PATH,
|
||||||
wifiOnly = wifiOnly ?: true,
|
wifiOnly = wifiOnly ?: true,
|
||||||
|
allowRoaming = allowRoaming ?: false,
|
||||||
chargingOnly = chargingOnly ?: false,
|
chargingOnly = chargingOnly ?: false,
|
||||||
lastSyncTimestamp = timestamp ?: initialSyncTimestamp,
|
lastSyncTimestamp = timestamp ?: initialSyncTimestamp,
|
||||||
name = _videoUploads.value?.name ?: videoUploadsName,
|
name = _videoUploads.value?.name ?: videoUploadsName,
|
||||||
|
|||||||
+11
-1
@@ -58,7 +58,7 @@ class UploadFileFromContentUriUseCase(
|
|||||||
.putString(UploadFileFromContentUriWorker.KEY_PARAM_CONTENT_URI, params.contentUri.toString())
|
.putString(UploadFileFromContentUriWorker.KEY_PARAM_CONTENT_URI, params.contentUri.toString())
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
val networkRequired = if (params.wifiOnly) NetworkType.UNMETERED else NetworkType.CONNECTED
|
val networkRequired = getRequiredNetworkType(params.wifiOnly, params.allowRoaming)
|
||||||
val constraints = Constraints.Builder()
|
val constraints = Constraints.Builder()
|
||||||
.setRequiredNetworkType(networkRequired)
|
.setRequiredNetworkType(networkRequired)
|
||||||
.setRequiresCharging(params.chargingOnly)
|
.setRequiresCharging(params.chargingOnly)
|
||||||
@@ -109,6 +109,16 @@ class UploadFileFromContentUriUseCase(
|
|||||||
val uploadPath: String,
|
val uploadPath: String,
|
||||||
val uploadIdInStorageManager: Long,
|
val uploadIdInStorageManager: Long,
|
||||||
val wifiOnly: Boolean,
|
val wifiOnly: Boolean,
|
||||||
|
val allowRoaming: Boolean = true,
|
||||||
val chargingOnly: Boolean,
|
val chargingOnly: Boolean,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun getRequiredNetworkType(wifiOnly: Boolean, allowRoaming: Boolean): NetworkType =
|
||||||
|
when {
|
||||||
|
wifiOnly -> NetworkType.UNMETERED
|
||||||
|
allowRoaming -> NetworkType.CONNECTED
|
||||||
|
else -> NetworkType.NOT_ROAMING
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -195,6 +195,7 @@ class AutomaticUploadsWorker(
|
|||||||
accountName = folderBackUpConfiguration.accountName,
|
accountName = folderBackUpConfiguration.accountName,
|
||||||
uploadId = uploadId,
|
uploadId = uploadId,
|
||||||
wifiOnly = folderBackUpConfiguration.wifiOnly,
|
wifiOnly = folderBackUpConfiguration.wifiOnly,
|
||||||
|
allowRoaming = folderBackUpConfiguration.allowRoaming,
|
||||||
chargingOnly = folderBackUpConfiguration.chargingOnly
|
chargingOnly = folderBackUpConfiguration.chargingOnly
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -309,6 +310,7 @@ class AutomaticUploadsWorker(
|
|||||||
accountName: String,
|
accountName: String,
|
||||||
uploadId: Long,
|
uploadId: Long,
|
||||||
wifiOnly: Boolean,
|
wifiOnly: Boolean,
|
||||||
|
allowRoaming: Boolean,
|
||||||
chargingOnly: Boolean
|
chargingOnly: Boolean
|
||||||
) {
|
) {
|
||||||
val lastModifiedInSeconds = (lastModified / 1000L).toString()
|
val lastModifiedInSeconds = (lastModified / 1000L).toString()
|
||||||
@@ -322,6 +324,7 @@ class AutomaticUploadsWorker(
|
|||||||
uploadPath = uploadPath,
|
uploadPath = uploadPath,
|
||||||
uploadIdInStorageManager = uploadId,
|
uploadIdInStorageManager = uploadId,
|
||||||
wifiOnly = wifiOnly,
|
wifiOnly = wifiOnly,
|
||||||
|
allowRoaming = allowRoaming,
|
||||||
chargingOnly = chargingOnly
|
chargingOnly = chargingOnly
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -561,6 +561,8 @@
|
|||||||
<string name="prefs_camera_upload_source_paths_empty">Папки телефона не выбраны</string>
|
<string name="prefs_camera_upload_source_paths_empty">Папки телефона не выбраны</string>
|
||||||
<string name="confirmation_clear_camera_upload_sources_title">Убрать выбранные папки</string>
|
<string name="confirmation_clear_camera_upload_sources_title">Убрать выбранные папки</string>
|
||||||
<string name="confirmation_clear_camera_upload_sources_message">Копирование перестанет проверять папки телефона, пока вы снова не добавите папку.</string>
|
<string name="confirmation_clear_camera_upload_sources_message">Копирование перестанет проверять папки телефона, пока вы снова не добавите папку.</string>
|
||||||
|
<string name="prefs_camera_upload_allow_roaming">Разрешить копирование в роуминге</string>
|
||||||
|
<string name="prefs_camera_upload_allow_roaming_summary">Применяется только если копирование только по Wi-Fi выключено</string>
|
||||||
<string name="prefs_camera_upload_behaviour_dialog_title">Исходный файл после копирования</string>
|
<string name="prefs_camera_upload_behaviour_dialog_title">Исходный файл после копирования</string>
|
||||||
<string name="prefs_camera_upload_behaviour_title">Исходный файл после копирования</string>
|
<string name="prefs_camera_upload_behaviour_title">Исходный файл после копирования</string>
|
||||||
<string name="prefs_camera_upload_last_sync_title">Последняя проверка</string>
|
<string name="prefs_camera_upload_last_sync_title">Последняя проверка</string>
|
||||||
|
|||||||
@@ -599,6 +599,8 @@
|
|||||||
<string name="prefs_camera_upload_source_paths_empty">No phone folders selected</string>
|
<string name="prefs_camera_upload_source_paths_empty">No phone folders selected</string>
|
||||||
<string name="confirmation_clear_camera_upload_sources_title">Remove selected folders</string>
|
<string name="confirmation_clear_camera_upload_sources_title">Remove selected folders</string>
|
||||||
<string name="confirmation_clear_camera_upload_sources_message">Backup will stop scanning phone folders until you add a folder again.</string>
|
<string name="confirmation_clear_camera_upload_sources_message">Backup will stop scanning phone folders until you add a folder again.</string>
|
||||||
|
<string name="prefs_camera_upload_allow_roaming">Allow backup while roaming</string>
|
||||||
|
<string name="prefs_camera_upload_allow_roaming_summary">Only applies when Wi-Fi-only backup is off</string>
|
||||||
<string name="prefs_camera_upload_behaviour_dialog_title">Original file after backup</string>
|
<string name="prefs_camera_upload_behaviour_dialog_title">Original file after backup</string>
|
||||||
<string name="prefs_camera_upload_behaviour_title">Original file after backup</string>
|
<string name="prefs_camera_upload_behaviour_title">Original file after backup</string>
|
||||||
<string name="prefs_camera_upload_last_sync_title">Last backup scan</string>
|
<string name="prefs_camera_upload_last_sync_title">Last backup scan</string>
|
||||||
|
|||||||
@@ -64,6 +64,11 @@
|
|||||||
app:iconSpaceReserved="false"
|
app:iconSpaceReserved="false"
|
||||||
app:key="picture_uploads_on_wifi"
|
app:key="picture_uploads_on_wifi"
|
||||||
app:title="@string/prefs_camera_picture_upload_on_wifi" />
|
app:title="@string/prefs_camera_picture_upload_on_wifi" />
|
||||||
|
<CheckBoxPreference
|
||||||
|
app:iconSpaceReserved="false"
|
||||||
|
app:key="picture_uploads_allow_roaming"
|
||||||
|
app:summary="@string/prefs_camera_upload_allow_roaming_summary"
|
||||||
|
app:title="@string/prefs_camera_upload_allow_roaming" />
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
app:iconSpaceReserved="false"
|
app:iconSpaceReserved="false"
|
||||||
app:key="picture_uploads_on_charging"
|
app:key="picture_uploads_on_charging"
|
||||||
|
|||||||
@@ -62,6 +62,11 @@
|
|||||||
app:iconSpaceReserved="false"
|
app:iconSpaceReserved="false"
|
||||||
app:key="video_uploads_on_wifi"
|
app:key="video_uploads_on_wifi"
|
||||||
app:title="@string/prefs_camera_video_upload_on_wifi" />
|
app:title="@string/prefs_camera_video_upload_on_wifi" />
|
||||||
|
<CheckBoxPreference
|
||||||
|
app:iconSpaceReserved="false"
|
||||||
|
app:key="video_uploads_allow_roaming"
|
||||||
|
app:summary="@string/prefs_camera_upload_allow_roaming_summary"
|
||||||
|
app:title="@string/prefs_camera_upload_allow_roaming" />
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
app:iconSpaceReserved="false"
|
app:iconSpaceReserved="false"
|
||||||
app:key="video_uploads_on_charging"
|
app:key="video_uploads_on_charging"
|
||||||
|
|||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
package eu.qsfera.android.usecases.transfers.uploads
|
||||||
|
|
||||||
|
import androidx.work.NetworkType
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class UploadFileFromContentUriUseCaseTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `network type is unmetered when wifi only is enabled`() {
|
||||||
|
val networkType = UploadFileFromContentUriUseCase.getRequiredNetworkType(
|
||||||
|
wifiOnly = true,
|
||||||
|
allowRoaming = false
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(NetworkType.UNMETERED, networkType)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `network type is connected when mobile data and roaming are allowed`() {
|
||||||
|
val networkType = UploadFileFromContentUriUseCase.getRequiredNetworkType(
|
||||||
|
wifiOnly = false,
|
||||||
|
allowRoaming = true
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(NetworkType.CONNECTED, networkType)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `network type avoids roaming when mobile data is allowed without roaming`() {
|
||||||
|
val networkType = UploadFileFromContentUriUseCase.getRequiredNetworkType(
|
||||||
|
wifiOnly = false,
|
||||||
|
allowRoaming = false
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(NetworkType.NOT_ROAMING, networkType)
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -31,7 +31,7 @@ public class ProviderMeta {
|
|||||||
|
|
||||||
public static final String DB_NAME = "filelist";
|
public static final String DB_NAME = "filelist";
|
||||||
public static final String NEW_DB_NAME = "qsfera_database";
|
public static final String NEW_DB_NAME = "qsfera_database";
|
||||||
public static final int DB_VERSION = 49;
|
public static final int DB_VERSION = 50;
|
||||||
|
|
||||||
private ProviderMeta() {
|
private ProviderMeta() {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ import eu.qsfera.android.data.migrations.MIGRATION_41_42
|
|||||||
import eu.qsfera.android.data.migrations.MIGRATION_42_43
|
import eu.qsfera.android.data.migrations.MIGRATION_42_43
|
||||||
import eu.qsfera.android.data.migrations.MIGRATION_47_48
|
import eu.qsfera.android.data.migrations.MIGRATION_47_48
|
||||||
import eu.qsfera.android.data.migrations.MIGRATION_48_49
|
import eu.qsfera.android.data.migrations.MIGRATION_48_49
|
||||||
|
import eu.qsfera.android.data.migrations.MIGRATION_49_50
|
||||||
import eu.qsfera.android.data.sharing.shares.db.OCShareDao
|
import eu.qsfera.android.data.sharing.shares.db.OCShareDao
|
||||||
import eu.qsfera.android.data.sharing.shares.db.OCShareEntity
|
import eu.qsfera.android.data.sharing.shares.db.OCShareEntity
|
||||||
import eu.qsfera.android.data.spaces.db.SpaceSpecialEntity
|
import eu.qsfera.android.data.spaces.db.SpaceSpecialEntity
|
||||||
@@ -127,7 +128,8 @@ abstract class QSferaDatabase : RoomDatabase() {
|
|||||||
MIGRATION_41_42,
|
MIGRATION_41_42,
|
||||||
MIGRATION_42_43,
|
MIGRATION_42_43,
|
||||||
MIGRATION_47_48,
|
MIGRATION_47_48,
|
||||||
MIGRATION_48_49)
|
MIGRATION_48_49,
|
||||||
|
MIGRATION_49_50)
|
||||||
.build()
|
.build()
|
||||||
INSTANCE = instance
|
INSTANCE = instance
|
||||||
instance
|
instance
|
||||||
|
|||||||
+2
@@ -68,6 +68,7 @@ class OCLocalFolderBackupDataSource(
|
|||||||
sourcePath = sourcePath,
|
sourcePath = sourcePath,
|
||||||
uploadPath = uploadPath,
|
uploadPath = uploadPath,
|
||||||
wifiOnly = wifiOnly,
|
wifiOnly = wifiOnly,
|
||||||
|
allowRoaming = allowRoaming,
|
||||||
chargingOnly = chargingOnly,
|
chargingOnly = chargingOnly,
|
||||||
name = name,
|
name = name,
|
||||||
lastSyncTimestamp = lastSyncTimestamp,
|
lastSyncTimestamp = lastSyncTimestamp,
|
||||||
@@ -83,6 +84,7 @@ class OCLocalFolderBackupDataSource(
|
|||||||
sourcePath = sourcePath,
|
sourcePath = sourcePath,
|
||||||
uploadPath = uploadPath,
|
uploadPath = uploadPath,
|
||||||
wifiOnly = wifiOnly,
|
wifiOnly = wifiOnly,
|
||||||
|
allowRoaming = allowRoaming,
|
||||||
chargingOnly = chargingOnly,
|
chargingOnly = chargingOnly,
|
||||||
lastSyncTimestamp = lastSyncTimestamp,
|
lastSyncTimestamp = lastSyncTimestamp,
|
||||||
name = name,
|
name = name,
|
||||||
|
|||||||
+1
@@ -29,6 +29,7 @@ data class FolderBackUpEntity(
|
|||||||
val sourcePath: String,
|
val sourcePath: String,
|
||||||
val uploadPath: String,
|
val uploadPath: String,
|
||||||
val wifiOnly: Boolean,
|
val wifiOnly: Boolean,
|
||||||
|
val allowRoaming: Boolean,
|
||||||
val chargingOnly: Boolean,
|
val chargingOnly: Boolean,
|
||||||
val name: String,
|
val name: String,
|
||||||
val lastSyncTimestamp: Long,
|
val lastSyncTimestamp: Long,
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ class CameraUploadsMigrationToRoom(val sharedPreferencesProvider: SharedPreferen
|
|||||||
behavior = getBehaviorForPreference(PREF__CAMERA_PICTURE_UPLOADS_BEHAVIOUR),
|
behavior = getBehaviorForPreference(PREF__CAMERA_PICTURE_UPLOADS_BEHAVIOUR),
|
||||||
lastSyncTimestamp = timestamp,
|
lastSyncTimestamp = timestamp,
|
||||||
name = pictureUploadsName,
|
name = pictureUploadsName,
|
||||||
|
allowRoaming = false,
|
||||||
chargingOnly = false,
|
chargingOnly = false,
|
||||||
spaceId = null,
|
spaceId = null,
|
||||||
)
|
)
|
||||||
@@ -71,6 +72,7 @@ class CameraUploadsMigrationToRoom(val sharedPreferencesProvider: SharedPreferen
|
|||||||
behavior = getBehaviorForPreference(PREF__CAMERA_VIDEO_UPLOADS_BEHAVIOUR),
|
behavior = getBehaviorForPreference(PREF__CAMERA_VIDEO_UPLOADS_BEHAVIOUR),
|
||||||
lastSyncTimestamp = timestamp,
|
lastSyncTimestamp = timestamp,
|
||||||
name = videoUploadsName,
|
name = videoUploadsName,
|
||||||
|
allowRoaming = false,
|
||||||
chargingOnly = false,
|
chargingOnly = false,
|
||||||
spaceId = null,
|
spaceId = null,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package eu.qsfera.android.data.migrations
|
||||||
|
|
||||||
|
import androidx.room.migration.Migration
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
|
import eu.qsfera.android.data.ProviderMeta.ProviderTableMeta.FOLDER_BACKUP_TABLE_NAME
|
||||||
|
|
||||||
|
val MIGRATION_49_50 = object : Migration(49, 50) {
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
database.execSQL("ALTER TABLE $FOLDER_BACKUP_TABLE_NAME ADD COLUMN allowRoaming INTEGER NOT NULL DEFAULT '0'")
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
@@ -25,6 +25,7 @@ data class FolderBackUpConfiguration(
|
|||||||
val sourcePath: String,
|
val sourcePath: String,
|
||||||
val uploadPath: String,
|
val uploadPath: String,
|
||||||
val wifiOnly: Boolean,
|
val wifiOnly: Boolean,
|
||||||
|
val allowRoaming: Boolean,
|
||||||
val chargingOnly: Boolean,
|
val chargingOnly: Boolean,
|
||||||
val lastSyncTimestamp: Long,
|
val lastSyncTimestamp: Long,
|
||||||
val name: String,
|
val name: String,
|
||||||
|
|||||||
+2
@@ -31,6 +31,7 @@ val OC_BACKUP = FolderBackUpConfiguration(
|
|||||||
sourcePath = "/Photos",
|
sourcePath = "/Photos",
|
||||||
uploadPath = "/Photos",
|
uploadPath = "/Photos",
|
||||||
wifiOnly = true,
|
wifiOnly = true,
|
||||||
|
allowRoaming = false,
|
||||||
chargingOnly = true,
|
chargingOnly = true,
|
||||||
lastSyncTimestamp = 1542628397,
|
lastSyncTimestamp = 1542628397,
|
||||||
name = "",
|
name = "",
|
||||||
@@ -43,6 +44,7 @@ val OC_BACKUP_ENTITY = FolderBackUpEntity(
|
|||||||
sourcePath = "/Photos",
|
sourcePath = "/Photos",
|
||||||
uploadPath = "/Photos",
|
uploadPath = "/Photos",
|
||||||
wifiOnly = true,
|
wifiOnly = true,
|
||||||
|
allowRoaming = false,
|
||||||
chargingOnly = true,
|
chargingOnly = true,
|
||||||
lastSyncTimestamp = 1542628397,
|
lastSyncTimestamp = 1542628397,
|
||||||
name = "",
|
name = "",
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ Date: 2026-06-08.
|
|||||||
- Android auto-upload validates persisted read permission and `DocumentFile.canRead()` before scanning SAF folders.
|
- Android auto-upload validates persisted read permission and `DocumentFile.canRead()` before scanning SAF folders.
|
||||||
- Android immediate auto-upload scheduling no longer blocks the caller while waiting for WorkManager state.
|
- Android immediate auto-upload scheduling no longer blocks the caller while waiting for WorkManager state.
|
||||||
- Android new auto-upload configurations default to Wi-Fi-only transfers for photos and videos.
|
- Android new auto-upload configurations default to Wi-Fi-only transfers for photos and videos.
|
||||||
|
- Android auto-upload now exposes a no-roaming control for photo and video backup, stores it in Room schema version `50`, and maps mobile backup without roaming to WorkManager `NetworkType.NOT_ROAMING`.
|
||||||
- Server `qsfera_full` compose files now require explicit admin, Keycloak, and S3/MinIO secrets instead of demo defaults.
|
- Server `qsfera_full` compose files now require explicit admin, Keycloak, and S3/MinIO secrets instead of demo defaults.
|
||||||
- Server `qsfera_full/env.production.example` provides a tracked production-safe starting point with TLS validation enabled and demo users disabled.
|
- Server `qsfera_full/env.production.example` provides a tracked production-safe starting point with TLS validation enabled and demo users disabled.
|
||||||
- Server `qsfera_full/validate-production-env.sh` checks production `.env` invariants before compose validation: TLS validation enabled, demo users disabled, pinned production image, required domains, required secrets, and no public-production MinIO.
|
- Server `qsfera_full/validate-production-env.sh` checks production `.env` invariants before compose validation: TLS validation enabled, demo users disabled, pinned production image, required domains, required secrets, and no public-production MinIO.
|
||||||
@@ -73,7 +74,7 @@ Date: 2026-06-08.
|
|||||||
## Remaining P1
|
## Remaining P1
|
||||||
|
|
||||||
- Add Google Photos-style backup status UX: overall status, per-item status, queued/paused/permanent-failure states, and low-storage messaging.
|
- Add Google Photos-style backup status UX: overall status, per-item status, queued/paused/permanent-failure states, and low-storage messaging.
|
||||||
- Add mobile-data controls comparable to Google Photos/Yandex Disk beyond the Wi-Fi-only default: mobile-data limit, no-video-over-mobile-data, and no-roaming options.
|
- Add mobile-data controls comparable to Google Photos/Yandex Disk beyond the Wi-Fi-only default: mobile-data limit and no-video-over-mobile-data options.
|
||||||
- Store auto-upload sync cursors per source folder rather than one timestamp for all folders.
|
- Store auto-upload sync cursors per source folder rather than one timestamp for all folders.
|
||||||
- Add production monitoring runbook and compose verification for metrics, tracing, alerts, backups, and restore drills.
|
- Add production monitoring runbook and compose verification for metrics, tracing, alerts, backups, and restore drills.
|
||||||
- Move Docker build cache and/or Docker data-root planning for the Pi to `/mnt/data`, or add documented retention, so future server image rebuilds cannot fill the root filesystem again.
|
- Move Docker build cache and/or Docker data-root planning for the Pi to `/mnt/data`, or add documented retention, so future server image rebuilds cannot fill the root filesystem again.
|
||||||
|
|||||||
Reference in New Issue
Block a user