From 753a8090dd3a95f75b672016b698c240178d1fbf Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 28 Apr 2026 16:09:54 +0200 Subject: [PATCH 1/5] feat(auto-upload): battery saver, batter optimization indication Signed-off-by: alperozturk96 --- .../java/com/owncloud/android/AbstractIT.java | 5 +++ .../owncloud/android/AbstractOnServerIT.java | 5 +++ .../java/com/owncloud/android/UploadIT.java | 10 +++++ .../android/files/services/FileUploaderIT.kt | 3 ++ .../client/device/PowerManagementService.kt | 5 +++ .../device/PowerManagementServiceImpl.kt | 7 ++++ .../component/AutoUploadWarningCardManager.kt | 42 +++++++++++++++++++ .../ui/activity/SyncedFoldersActivity.kt | 5 +++ .../android/ui/activity/UploadListActivity.kt | 5 +++ .../android/ui/adapter/SyncedFolderAdapter.kt | 7 ++-- .../adapter/uploadList/UploadListAdapter.kt | 10 ++--- ...to_upload_battery_saver_warning_banner.xml | 1 + app/src/main/res/values/strings.xml | 1 + 13 files changed, 95 insertions(+), 11 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/ui/component/AutoUploadWarningCardManager.kt diff --git a/app/src/androidTest/java/com/owncloud/android/AbstractIT.java b/app/src/androidTest/java/com/owncloud/android/AbstractIT.java index ac58ccc05fc9..7b1f2d615454 100644 --- a/app/src/androidTest/java/com/owncloud/android/AbstractIT.java +++ b/app/src/androidTest/java/com/owncloud/android/AbstractIT.java @@ -393,6 +393,11 @@ public Connectivity getConnectivity() { }; PowerManagementService powerManagementServiceMock = new PowerManagementService() { + @Override + public boolean isIgnoringOptimization() { + return true; + } + @NonNull @Override public BatteryStatus getBattery() { diff --git a/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java b/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java index 1b0e1b8d3c17..94bf2bddc287 100644 --- a/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java +++ b/app/src/androidTest/java/com/owncloud/android/AbstractOnServerIT.java @@ -225,6 +225,11 @@ public Connectivity getConnectivity() { }; PowerManagementService powerManagementServiceMock = new PowerManagementService() { + @Override + public boolean isIgnoringOptimization() { + return true; + } + @NonNull @Override public BatteryStatus getBattery() { diff --git a/app/src/androidTest/java/com/owncloud/android/UploadIT.java b/app/src/androidTest/java/com/owncloud/android/UploadIT.java index 8072bb5c1805..36bc91ce8c4a 100644 --- a/app/src/androidTest/java/com/owncloud/android/UploadIT.java +++ b/app/src/androidTest/java/com/owncloud/android/UploadIT.java @@ -78,6 +78,11 @@ public Connectivity getConnectivity() { }; private PowerManagementService powerManagementServiceMock = new PowerManagementService() { + @Override + public boolean isIgnoringOptimization() { + return true; + } + @Override public boolean isPowerSavingEnabled() { return false; @@ -226,6 +231,11 @@ public void testUploadOnChargingOnlyButNotCharging() { @Test public void testUploadOnChargingOnlyAndCharging() { PowerManagementService powerManagementServiceMock = new PowerManagementService() { + @Override + public boolean isIgnoringOptimization() { + return true; + } + @Override public boolean isPowerSavingEnabled() { return false; diff --git a/app/src/androidTest/java/com/owncloud/android/files/services/FileUploaderIT.kt b/app/src/androidTest/java/com/owncloud/android/files/services/FileUploaderIT.kt index 00c568d506ad..5367c66e988e 100644 --- a/app/src/androidTest/java/com/owncloud/android/files/services/FileUploaderIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/files/services/FileUploaderIT.kt @@ -43,6 +43,9 @@ abstract class FileUploaderIT : AbstractOnServerIT() { } private val powerManagementServiceMock: PowerManagementService = object : PowerManagementService { + override val isIgnoringOptimization: Boolean + get() = true + override val isPowerSavingEnabled: Boolean get() = false diff --git a/app/src/main/java/com/nextcloud/client/device/PowerManagementService.kt b/app/src/main/java/com/nextcloud/client/device/PowerManagementService.kt index 730ca4ec728a..5b51acaf0518 100644 --- a/app/src/main/java/com/nextcloud/client/device/PowerManagementService.kt +++ b/app/src/main/java/com/nextcloud/client/device/PowerManagementService.kt @@ -21,6 +21,11 @@ interface PowerManagementService { */ val isPowerSavingEnabled: Boolean + /** + * Checks app is excluded from battery optimization or not + */ + val isIgnoringOptimization: Boolean + /** * Checks current battery status using platform [android.os.BatteryManager] */ diff --git a/app/src/main/java/com/nextcloud/client/device/PowerManagementServiceImpl.kt b/app/src/main/java/com/nextcloud/client/device/PowerManagementServiceImpl.kt index 3c8d56c280de..3f72e8f61e22 100644 --- a/app/src/main/java/com/nextcloud/client/device/PowerManagementServiceImpl.kt +++ b/app/src/main/java/com/nextcloud/client/device/PowerManagementServiceImpl.kt @@ -11,6 +11,7 @@ import android.content.Intent import android.content.IntentFilter import android.os.BatteryManager import android.os.PowerManager +import androidx.core.content.ContextCompat.getSystemService import com.nextcloud.utils.extensions.registerBroadcastReceiver import com.owncloud.android.datamodel.ReceiverFlag @@ -27,6 +28,12 @@ internal class PowerManagementServiceImpl( } } + override val isIgnoringOptimization: Boolean + get() { + val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager + return powerManager.isIgnoringBatteryOptimizations(context.packageName) + } + override val isPowerSavingEnabled: Boolean get() { return platformPowerManager.isPowerSaveMode diff --git a/app/src/main/java/com/nextcloud/ui/component/AutoUploadWarningCardManager.kt b/app/src/main/java/com/nextcloud/ui/component/AutoUploadWarningCardManager.kt new file mode 100644 index 000000000000..e2685248fcbd --- /dev/null +++ b/app/src/main/java/com/nextcloud/ui/component/AutoUploadWarningCardManager.kt @@ -0,0 +1,42 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.ui.component + +import android.content.Context +import com.nextcloud.client.device.PowerManagementService +import com.nextcloud.client.di.Injectable +import com.nextcloud.utils.extensions.setVisibleIf +import com.owncloud.android.R +import com.owncloud.android.databinding.AutoUploadBatterySaverWarningBannerBinding +import com.owncloud.android.utils.theme.ViewThemeUtils +import javax.inject.Inject + +class AutoUploadWarningCardManager @Inject constructor( + private val powerManagementService: PowerManagementService, + private val viewThemeUtils: ViewThemeUtils, + private val context: Context +) : Injectable { + + fun bind(binding: AutoUploadBatterySaverWarningBannerBinding) { + val isBatterySaver = powerManagementService.isPowerSavingEnabled + val isIgnoringOptimization = powerManagementService.isIgnoringOptimization + + binding.root.setVisibleIf(isBatterySaver || isIgnoringOptimization) + + val messages = listOfNotNull( + if (isBatterySaver) context + .getString(R.string.auto_upload_battery_saver_mode_warning) else null, + if (isIgnoringOptimization) context + .getString(R.string.auto_upload_battery_ignore_optimization_mode_warning) else null + ) + + binding.message.text = messages.joinToString("\n") + + viewThemeUtils.material.themeCardView(binding.root) + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt index 9dd1d31c95a7..7fd2f28708d1 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt @@ -33,6 +33,7 @@ import com.nextcloud.client.jobs.MediaFoldersDetectionWork import com.nextcloud.client.jobs.NotificationWork import com.nextcloud.client.jobs.upload.FileUploadWorker import com.nextcloud.client.preferences.SubFolderRule +import com.nextcloud.ui.component.AutoUploadWarningCardManager import com.nextcloud.utils.BatteryOptimizationHelper import com.nextcloud.utils.extensions.getParcelableArgument import com.nextcloud.utils.extensions.isDialogFragmentReady @@ -152,6 +153,9 @@ class SyncedFoldersActivity : @Inject lateinit var appInfo: AppInfo + @Inject + lateinit var autoUploadWarningCardManager: AutoUploadWarningCardManager + lateinit var binding: SyncedFoldersLayoutBinding lateinit var adapter: SyncedFolderAdapter @@ -246,6 +250,7 @@ class SyncedFoldersActivity : val gridWidth = resources.getInteger(R.integer.media_grid_width) val lightVersion = resources.getBoolean(R.bool.syncedFolder_light) adapter = SyncedFolderAdapter( + autoUploadWarningCardManager, lifecycleScope, this, clock, diff --git a/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.kt index cb6c70581622..db03dae434d4 100755 --- a/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.kt @@ -28,6 +28,7 @@ import com.nextcloud.client.jobs.upload.FileUploadEventBroadcaster import com.nextcloud.client.jobs.upload.FileUploadHelper import com.nextcloud.client.jobs.utils.UploadErrorNotificationManager import com.nextcloud.client.utils.Throttler +import com.nextcloud.ui.component.AutoUploadWarningCardManager import com.nextcloud.utils.extensions.webDavParentPath import com.owncloud.android.R import com.owncloud.android.databinding.UploadListLayoutBinding @@ -72,6 +73,9 @@ class UploadListActivity : @Inject lateinit var uploadFileOperationFactory: UploadFileOperationFactory + @Inject + lateinit var autoUploadWarningCardManager: AutoUploadWarningCardManager + private var swipeListRefreshLayout: SwipeRefreshLayout? = null private var binding: UploadListLayoutBinding? = null @@ -108,6 +112,7 @@ class UploadListActivity : adapterHelper = UploadListAdapterHelper(this) uploadListAdapter = UploadListAdapter( this, + autoUploadWarningCardManager, uploadsStorageManager, userAccountManager, connectivityService, diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.kt index 050674feae5b..de9ea418dcbf 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.kt @@ -25,6 +25,7 @@ import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.client.core.Clock import com.nextcloud.client.device.PowerManagementService import com.nextcloud.client.network.ConnectivityService +import com.nextcloud.ui.component.AutoUploadWarningCardManager import com.nextcloud.utils.extensions.calculateScanInterval import com.nextcloud.utils.extensions.filterEnabledOrWithoutEnabledParent import com.nextcloud.utils.extensions.hasEnabledParent @@ -55,6 +56,7 @@ import java.util.concurrent.TimeUnit */ @Suppress("LongParameterList", "TooManyFunctions") class SyncedFolderAdapter( + private val autoUploadWarningCardManager: AutoUploadWarningCardManager, private val lifecycleScope: LifecycleCoroutineScope, private val context: Context, private val clock: Clock, @@ -264,10 +266,7 @@ class SyncedFolderAdapter( headerContainer.visibility = View.VISIBLE if (section == 0) { - autoUploadBatterySaverWarningCard.root.run { - setVisibleIf(powerManagementService.isPowerSavingEnabled) - viewThemeUtils.material.themeCardView(this) - } + autoUploadWarningCardManager.bind(autoUploadBatterySaverWarningCard) } val syncedFolder = filteredSyncFolderItems[section] diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/uploadList/UploadListAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/uploadList/UploadListAdapter.kt index 33b0a835cad1..ad801bb48b9d 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/uploadList/UploadListAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/uploadList/UploadListAdapter.kt @@ -25,6 +25,7 @@ import com.nextcloud.client.device.PowerManagementService import com.nextcloud.client.jobs.upload.FileUploadHelper import com.nextcloud.client.jobs.upload.FileUploadWorker import com.nextcloud.client.network.ConnectivityService +import com.nextcloud.ui.component.AutoUploadWarningCardManager import com.nextcloud.utils.extensions.getStatusText import com.nextcloud.utils.extensions.isLastResultConflictError import com.nextcloud.utils.extensions.setVisibleIf @@ -67,6 +68,7 @@ import java.util.function.Consumer ) class UploadListAdapter( private val activity: FileActivity, + private val autoUploadWarningCardManager: AutoUploadWarningCardManager, private val uploadsStorageManager: UploadsStorageManager, private val accountManager: UserAccountManager, private val connectivityService: ConnectivityService, @@ -102,7 +104,7 @@ class UploadListAdapter( bindHeaderTitle(headerViewHolder, group, section) bindHeaderActionButton(headerViewHolder, group) - bindHeaderBatterySaverWarning(headerViewHolder) + autoUploadWarningCardManager.bind(holder.binding.autoUploadBatterySaverWarningCard) bindHeaderActionClickListener(headerViewHolder, group) } @@ -130,12 +132,6 @@ class UploadListAdapter( holder.binding.uploadListAction.setImageResource(iconRes) } - private fun bindHeaderBatterySaverWarning(holder: HeaderViewHolder) { - holder.binding.autoUploadBatterySaverWarningCard.root - .setVisibleIf(powerManagementService.isPowerSavingEnabled) - viewThemeUtils.material.themeCardView(holder.binding.autoUploadBatterySaverWarningCard.root) - } - private fun bindHeaderActionClickListener(holder: HeaderViewHolder, group: UploadListSection) { holder.binding.uploadListAction.setOnClickListener { when (group.type) { diff --git a/app/src/main/res/layout/auto_upload_battery_saver_warning_banner.xml b/app/src/main/res/layout/auto_upload_battery_saver_warning_banner.xml index 83274c0eda75..f494d7ed3047 100644 --- a/app/src/main/res/layout/auto_upload_battery_saver_warning_banner.xml +++ b/app/src/main/res/layout/auto_upload_battery_saver_warning_banner.xml @@ -33,6 +33,7 @@ app:tint="@color/log_level_warning" /> Delete auto-upload folder? This will remove the folder and auto-upload configuration. Any unfinished uploads will be canceled. Auto-upload is paused because Battery Saver is on. + Auto-upload might not work reliably because battery optimization is enabled for the Nextcloud app. This folder is already included in the parent folder’s sync, which may cause duplicate uploads Sync anyway Sync duplication From a11b364135a1c6fb23bd516460ec414dd6fab7d7 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 28 Apr 2026 16:19:00 +0200 Subject: [PATCH 2/5] feat(auto-upload): manual di Signed-off-by: alperozturk96 --- .../nextcloud/ui/component/AutoUploadWarningCardManager.kt | 6 ++---- .../owncloud/android/ui/activity/SyncedFoldersActivity.kt | 2 +- .../com/owncloud/android/ui/activity/UploadListActivity.kt | 5 +---- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/nextcloud/ui/component/AutoUploadWarningCardManager.kt b/app/src/main/java/com/nextcloud/ui/component/AutoUploadWarningCardManager.kt index e2685248fcbd..702bed11e42a 100644 --- a/app/src/main/java/com/nextcloud/ui/component/AutoUploadWarningCardManager.kt +++ b/app/src/main/java/com/nextcloud/ui/component/AutoUploadWarningCardManager.kt @@ -9,18 +9,16 @@ package com.nextcloud.ui.component import android.content.Context import com.nextcloud.client.device.PowerManagementService -import com.nextcloud.client.di.Injectable import com.nextcloud.utils.extensions.setVisibleIf import com.owncloud.android.R import com.owncloud.android.databinding.AutoUploadBatterySaverWarningBannerBinding import com.owncloud.android.utils.theme.ViewThemeUtils -import javax.inject.Inject -class AutoUploadWarningCardManager @Inject constructor( +class AutoUploadWarningCardManager( private val powerManagementService: PowerManagementService, private val viewThemeUtils: ViewThemeUtils, private val context: Context -) : Injectable { +) { fun bind(binding: AutoUploadBatterySaverWarningBannerBinding) { val isBatterySaver = powerManagementService.isPowerSavingEnabled diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt index 7fd2f28708d1..f763b2efa49b 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt @@ -153,7 +153,6 @@ class SyncedFoldersActivity : @Inject lateinit var appInfo: AppInfo - @Inject lateinit var autoUploadWarningCardManager: AutoUploadWarningCardManager lateinit var binding: SyncedFoldersLayoutBinding @@ -167,6 +166,7 @@ class SyncedFoldersActivity : super.onCreate(savedInstanceState) binding = SyncedFoldersLayoutBinding.inflate(layoutInflater) setContentView(binding.root) + autoUploadWarningCardManager = AutoUploadWarningCardManager(powerManagementService, viewThemeUtils, this) if (intent != null && intent.extras != null) { val accountName = intent.extras!!.getString(NotificationWork.KEY_NOTIFICATION_ACCOUNT) val optionalUser = user diff --git a/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.kt index db03dae434d4..262c1525475b 100755 --- a/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.kt @@ -73,9 +73,6 @@ class UploadListActivity : @Inject lateinit var uploadFileOperationFactory: UploadFileOperationFactory - @Inject - lateinit var autoUploadWarningCardManager: AutoUploadWarningCardManager - private var swipeListRefreshLayout: SwipeRefreshLayout? = null private var binding: UploadListLayoutBinding? = null @@ -112,7 +109,7 @@ class UploadListActivity : adapterHelper = UploadListAdapterHelper(this) uploadListAdapter = UploadListAdapter( this, - autoUploadWarningCardManager, + AutoUploadWarningCardManager(powerManagementService, viewThemeUtils, this), uploadsStorageManager, userAccountManager, connectivityService, From 9d489d38d0c9bebe49249b134141fdbe980e5594 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 28 Apr 2026 16:47:38 +0200 Subject: [PATCH 3/5] feat(auto-upload): fix upload list layout and fix texts Signed-off-by: alperozturk96 --- .../component/AutoUploadWarningCardManager.kt | 26 ++++----- .../ui/activity/SyncedFoldersActivity.kt | 2 +- .../android/ui/activity/UploadListActivity.kt | 4 +- .../adapter/uploadList/UploadListAdapter.kt | 3 - ...to_upload_battery_saver_warning_banner.xml | 58 +++++++++++++++---- .../storage_permission_warning_banner.xml | 2 +- .../main/res/layout/upload_list_header.xml | 7 --- .../main/res/layout/upload_list_layout.xml | 16 ++++- app/src/main/res/values/strings.xml | 5 +- 9 files changed, 80 insertions(+), 43 deletions(-) diff --git a/app/src/main/java/com/nextcloud/ui/component/AutoUploadWarningCardManager.kt b/app/src/main/java/com/nextcloud/ui/component/AutoUploadWarningCardManager.kt index 702bed11e42a..7b320879696f 100644 --- a/app/src/main/java/com/nextcloud/ui/component/AutoUploadWarningCardManager.kt +++ b/app/src/main/java/com/nextcloud/ui/component/AutoUploadWarningCardManager.kt @@ -7,33 +7,33 @@ package com.nextcloud.ui.component -import android.content.Context +import android.view.View import com.nextcloud.client.device.PowerManagementService import com.nextcloud.utils.extensions.setVisibleIf -import com.owncloud.android.R import com.owncloud.android.databinding.AutoUploadBatterySaverWarningBannerBinding import com.owncloud.android.utils.theme.ViewThemeUtils class AutoUploadWarningCardManager( private val powerManagementService: PowerManagementService, - private val viewThemeUtils: ViewThemeUtils, - private val context: Context + private val viewThemeUtils: ViewThemeUtils ) { - fun bind(binding: AutoUploadBatterySaverWarningBannerBinding) { val isBatterySaver = powerManagementService.isPowerSavingEnabled val isIgnoringOptimization = powerManagementService.isIgnoringOptimization binding.root.setVisibleIf(isBatterySaver || isIgnoringOptimization) - val messages = listOfNotNull( - if (isBatterySaver) context - .getString(R.string.auto_upload_battery_saver_mode_warning) else null, - if (isIgnoringOptimization) context - .getString(R.string.auto_upload_battery_ignore_optimization_mode_warning) else null - ) - - binding.message.text = messages.joinToString("\n") + if (isBatterySaver && isIgnoringOptimization) { + binding.title.visibility = View.VISIBLE + binding.batterySaverReason.visibility = View.VISIBLE + binding.batteryOptimizationReason.visibility = View.VISIBLE + } else if (isBatterySaver) { + binding.title.visibility = View.VISIBLE + binding.batterySaverReason.visibility = View.VISIBLE + } else if (isIgnoringOptimization) { + binding.title.visibility = View.VISIBLE + binding.batteryOptimizationReason.visibility = View.VISIBLE + } viewThemeUtils.material.themeCardView(binding.root) } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt index f763b2efa49b..076e0e28da10 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt @@ -166,7 +166,7 @@ class SyncedFoldersActivity : super.onCreate(savedInstanceState) binding = SyncedFoldersLayoutBinding.inflate(layoutInflater) setContentView(binding.root) - autoUploadWarningCardManager = AutoUploadWarningCardManager(powerManagementService, viewThemeUtils, this) + autoUploadWarningCardManager = AutoUploadWarningCardManager(powerManagementService, viewThemeUtils) if (intent != null && intent.extras != null) { val accountName = intent.extras!!.getString(NotificationWork.KEY_NOTIFICATION_ACCOUNT) val optionalUser = user diff --git a/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.kt index 262c1525475b..3e6911d166a8 100755 --- a/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.kt @@ -109,7 +109,6 @@ class UploadListActivity : adapterHelper = UploadListAdapterHelper(this) uploadListAdapter = UploadListAdapter( this, - AutoUploadWarningCardManager(powerManagementService, viewThemeUtils, this), uploadsStorageManager, userAccountManager, connectivityService, @@ -119,6 +118,9 @@ class UploadListActivity : adapterHelper ) + val autoUploadWarningCardManager = AutoUploadWarningCardManager(powerManagementService, viewThemeUtils) + binding?.autoUploadBatterySaverWarningCard?.let { autoUploadWarningCardManager.bind(it) } + val lm = GridLayoutManager(this, 1) uploadListAdapter.setLayoutManager(lm) diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/uploadList/UploadListAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/uploadList/UploadListAdapter.kt index ad801bb48b9d..3808c21a1400 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/uploadList/UploadListAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/uploadList/UploadListAdapter.kt @@ -25,7 +25,6 @@ import com.nextcloud.client.device.PowerManagementService import com.nextcloud.client.jobs.upload.FileUploadHelper import com.nextcloud.client.jobs.upload.FileUploadWorker import com.nextcloud.client.network.ConnectivityService -import com.nextcloud.ui.component.AutoUploadWarningCardManager import com.nextcloud.utils.extensions.getStatusText import com.nextcloud.utils.extensions.isLastResultConflictError import com.nextcloud.utils.extensions.setVisibleIf @@ -68,7 +67,6 @@ import java.util.function.Consumer ) class UploadListAdapter( private val activity: FileActivity, - private val autoUploadWarningCardManager: AutoUploadWarningCardManager, private val uploadsStorageManager: UploadsStorageManager, private val accountManager: UserAccountManager, private val connectivityService: ConnectivityService, @@ -104,7 +102,6 @@ class UploadListAdapter( bindHeaderTitle(headerViewHolder, group, section) bindHeaderActionButton(headerViewHolder, group) - autoUploadWarningCardManager.bind(holder.binding.autoUploadBatterySaverWarningCard) bindHeaderActionClickListener(headerViewHolder, group) } diff --git a/app/src/main/res/layout/auto_upload_battery_saver_warning_banner.xml b/app/src/main/res/layout/auto_upload_battery_saver_warning_banner.xml index f494d7ed3047..616df3c5c854 100644 --- a/app/src/main/res/layout/auto_upload_battery_saver_warning_banner.xml +++ b/app/src/main/res/layout/auto_upload_battery_saver_warning_banner.xml @@ -5,8 +5,7 @@ ~ SPDX-FileCopyrightText: 2025 Alper Ozturk ~ SPDX-License-Identifier: AGPL-3.0-or-later --> - + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools"> - + android:orientation="vertical"> + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/storage_permission_warning_banner.xml b/app/src/main/res/layout/storage_permission_warning_banner.xml index 993114db9a0e..f42b5a4fd6a3 100644 --- a/app/src/main/res/layout/storage_permission_warning_banner.xml +++ b/app/src/main/res/layout/storage_permission_warning_banner.xml @@ -32,7 +32,7 @@ diff --git a/app/src/main/res/layout/upload_list_header.xml b/app/src/main/res/layout/upload_list_header.xml index 52f3d76065e8..5fbdc715844c 100755 --- a/app/src/main/res/layout/upload_list_header.xml +++ b/app/src/main/res/layout/upload_list_header.xml @@ -62,11 +62,4 @@ android:paddingBottom="@dimen/standard_half_padding" android:src="@drawable/ic_delete" app:tint="#757575" /> - - diff --git a/app/src/main/res/layout/upload_list_layout.xml b/app/src/main/res/layout/upload_list_layout.xml index 8dfbe1fb69d4..3a69b6643177 100755 --- a/app/src/main/res/layout/upload_list_layout.xml +++ b/app/src/main/res/layout/upload_list_layout.xml @@ -22,10 +22,20 @@ + + + android:layout_below="@id/auto_upload_battery_saver_warning_card"> + + android:layout_height="match_parent" + android:padding="@dimen/standard_half_padding" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6c3cd2d7bda0..c0762f04cde4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1483,8 +1483,9 @@ Open in %1$s Delete auto-upload folder? This will remove the folder and auto-upload configuration. Any unfinished uploads will be canceled. - Auto-upload is paused because Battery Saver is on. - Auto-upload might not work reliably because battery optimization is enabled for the Nextcloud app. + Auto-upload may be limited: + \t \u2022 Battery Saver is active and may pause uploads + \t \u2022 Nextcloud\'s background activity is limited by battery optimization This folder is already included in the parent folder’s sync, which may cause duplicate uploads Sync anyway Sync duplication From 1126d64980cc8b8730218652828c1930cef99ad7 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 28 Apr 2026 16:59:18 +0200 Subject: [PATCH 4/5] feat(auto-upload): fix sync folder layout Signed-off-by: alperozturk96 --- .../client/device/PowerManagementServiceImpl.kt | 1 - .../android/ui/activity/SyncedFoldersActivity.kt | 2 +- .../android/ui/adapter/SyncedFolderAdapter.kt | 5 ----- .../res/layout/synced_folders_item_header.xml | 16 ---------------- .../main/res/layout/synced_folders_layout.xml | 8 ++++++++ 5 files changed, 9 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/device/PowerManagementServiceImpl.kt b/app/src/main/java/com/nextcloud/client/device/PowerManagementServiceImpl.kt index 3f72e8f61e22..8d3c9ad89645 100644 --- a/app/src/main/java/com/nextcloud/client/device/PowerManagementServiceImpl.kt +++ b/app/src/main/java/com/nextcloud/client/device/PowerManagementServiceImpl.kt @@ -11,7 +11,6 @@ import android.content.Intent import android.content.IntentFilter import android.os.BatteryManager import android.os.PowerManager -import androidx.core.content.ContextCompat.getSystemService import com.nextcloud.utils.extensions.registerBroadcastReceiver import com.owncloud.android.datamodel.ReceiverFlag diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt index 076e0e28da10..a85aecf97449 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt @@ -250,7 +250,6 @@ class SyncedFoldersActivity : val gridWidth = resources.getInteger(R.integer.media_grid_width) val lightVersion = resources.getBoolean(R.bool.syncedFolder_light) adapter = SyncedFolderAdapter( - autoUploadWarningCardManager, lifecycleScope, this, clock, @@ -261,6 +260,7 @@ class SyncedFoldersActivity : powerManagementService, connectivityService ) + autoUploadWarningCardManager.bind(binding.autoUploadBatterySaverWarningCard) binding.emptyList.emptyListIcon.setImageResource(R.drawable.nav_synced_folders) viewThemeUtils.material.colorMaterialButtonPrimaryFilled(binding.emptyList.emptyListViewAction) val lm = GridLayoutManager(this, gridWidth) diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.kt index de9ea418dcbf..ebf7460b2cab 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.kt @@ -56,7 +56,6 @@ import java.util.concurrent.TimeUnit */ @Suppress("LongParameterList", "TooManyFunctions") class SyncedFolderAdapter( - private val autoUploadWarningCardManager: AutoUploadWarningCardManager, private val lifecycleScope: LifecycleCoroutineScope, private val context: Context, private val clock: Clock, @@ -265,10 +264,6 @@ class SyncedFolderAdapter( holder.binding.run { headerContainer.visibility = View.VISIBLE - if (section == 0) { - autoUploadWarningCardManager.bind(autoUploadBatterySaverWarningCard) - } - val syncedFolder = filteredSyncFolderItems[section] title.text = syncedFolder.folderName diff --git a/app/src/main/res/layout/synced_folders_item_header.xml b/app/src/main/res/layout/synced_folders_item_header.xml index 983c109f1f24..071ca508e3da 100644 --- a/app/src/main/res/layout/synced_folders_item_header.xml +++ b/app/src/main/res/layout/synced_folders_item_header.xml @@ -20,22 +20,6 @@ android:paddingStart="@dimen/standard_quarter_padding" android:paddingTop="@dimen/alternate_half_padding"> - - + + Date: Wed, 29 Apr 2026 08:30:35 +0200 Subject: [PATCH 5/5] feat(auto-upload): listen live changes Signed-off-by: alperozturk96 --- .../component/AutoUploadWarningCardManager.kt | 28 +++++++++++++++++++ .../ui/activity/SyncedFoldersActivity.kt | 11 ++++++-- .../android/ui/activity/UploadListActivity.kt | 14 ++++++++-- 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/nextcloud/ui/component/AutoUploadWarningCardManager.kt b/app/src/main/java/com/nextcloud/ui/component/AutoUploadWarningCardManager.kt index 7b320879696f..d900c09c8f07 100644 --- a/app/src/main/java/com/nextcloud/ui/component/AutoUploadWarningCardManager.kt +++ b/app/src/main/java/com/nextcloud/ui/component/AutoUploadWarningCardManager.kt @@ -7,6 +7,11 @@ package com.nextcloud.ui.component +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.os.PowerManager import android.view.View import com.nextcloud.client.device.PowerManagementService import com.nextcloud.utils.extensions.setVisibleIf @@ -37,4 +42,27 @@ class AutoUploadWarningCardManager( viewThemeUtils.material.themeCardView(binding.root) } + + // region listen power mode changes + private var binding: AutoUploadBatterySaverWarningBannerBinding? = null + + private val batterySaverReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (intent.action == PowerManager.ACTION_POWER_SAVE_MODE_CHANGED) { + binding?.let { bind(it) } + } + } + } + + fun register(context: Context, binding: AutoUploadBatterySaverWarningBannerBinding) { + this.binding = binding + bind(binding) + context.registerReceiver(batterySaverReceiver, IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)) + } + + fun unregister(context: Context) { + context.unregisterReceiver(batterySaverReceiver) + binding = null + } + // endregion } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt index a85aecf97449..ac7b3f30b165 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt @@ -153,7 +153,7 @@ class SyncedFoldersActivity : @Inject lateinit var appInfo: AppInfo - lateinit var autoUploadWarningCardManager: AutoUploadWarningCardManager + private var autoUploadWarningCardManager: AutoUploadWarningCardManager? = null lateinit var binding: SyncedFoldersLayoutBinding lateinit var adapter: SyncedFolderAdapter @@ -260,7 +260,9 @@ class SyncedFoldersActivity : powerManagementService, connectivityService ) - autoUploadWarningCardManager.bind(binding.autoUploadBatterySaverWarningCard) + autoUploadWarningCardManager?.bind(binding.autoUploadBatterySaverWarningCard) + autoUploadWarningCardManager?.register(this, binding.autoUploadBatterySaverWarningCard) + binding.emptyList.emptyListIcon.setImageResource(R.drawable.nav_synced_folders) viewThemeUtils.material.colorMaterialButtonPrimaryFilled(binding.emptyList.emptyListViewAction) val lm = GridLayoutManager(this, gridWidth) @@ -280,6 +282,11 @@ class SyncedFoldersActivity : } } + override fun onDestroy() { + super.onDestroy() + autoUploadWarningCardManager?.unregister(this) + } + /** * loads all media/synced folders, adds them to the recycler view adapter and shows the list. * diff --git a/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.kt index 3e6911d166a8..36958c31d101 100755 --- a/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.kt @@ -73,6 +73,8 @@ class UploadListActivity : @Inject lateinit var uploadFileOperationFactory: UploadFileOperationFactory + private var autoUploadWarningCardManager: AutoUploadWarningCardManager? = null + private var swipeListRefreshLayout: SwipeRefreshLayout? = null private var binding: UploadListLayoutBinding? = null @@ -88,6 +90,7 @@ class UploadListActivity : binding = UploadListLayoutBinding.inflate(layoutInflater) val binding = binding!! setContentView(binding.getRoot()) + autoUploadWarningCardManager = AutoUploadWarningCardManager(powerManagementService, viewThemeUtils) swipeListRefreshLayout = binding.swipeContainingList // this activity has no file really bound, it's for multiple accounts at the same time; should no inherit @@ -118,8 +121,10 @@ class UploadListActivity : adapterHelper ) - val autoUploadWarningCardManager = AutoUploadWarningCardManager(powerManagementService, viewThemeUtils) - binding?.autoUploadBatterySaverWarningCard?.let { autoUploadWarningCardManager.bind(it) } + binding?.autoUploadBatterySaverWarningCard?.let { + autoUploadWarningCardManager?.register(this, it) + autoUploadWarningCardManager?.bind(it) + } val lm = GridLayoutManager(this, 1) uploadListAdapter.setLayoutManager(lm) @@ -372,6 +377,11 @@ class UploadListActivity : } } + override fun onDestroy() { + super.onDestroy() + autoUploadWarningCardManager?.unregister(this) + } + companion object { private val TAG: String = UploadListActivity::class.java.getSimpleName()