From 18a686f98cff076f9cdb596e834ea4139f8a4aaa Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 28 Apr 2026 16:09:54 +0200 Subject: [PATCH 01/15] 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 09fafceb8c13..9e53b504955a 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 a7b2c3c776bb..20cf1cdbca92 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 a1fd2ca22073fec6f8bb7677e82851df31ca1070 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 28 Apr 2026 16:19:00 +0200 Subject: [PATCH 02/15] 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 39859235a595ce09933efc091006ac10037c1dce Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 28 Apr 2026 16:47:38 +0200 Subject: [PATCH 03/15] 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 20cf1cdbca92..1f60d8558591 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 0a30f7b62bab..74d8001d0e0a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1485,8 +1485,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 e521f2417ce565a3e71edf853052c606c6079658 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 28 Apr 2026 16:59:18 +0200 Subject: [PATCH 04/15] 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 05/15] 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() From e446a8f5d9a6b037fb8b07c806ce7c900661e610 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 5 May 2026 11:25:38 +0200 Subject: [PATCH 06/15] add redirection actions Signed-off-by: alperozturk96 --- .../device/PowerManagementServiceImpl.kt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) 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 8d3c9ad89645..9b03c65c05a0 100644 --- a/app/src/main/java/com/nextcloud/client/device/PowerManagementServiceImpl.kt +++ b/app/src/main/java/com/nextcloud/client/device/PowerManagementServiceImpl.kt @@ -6,11 +6,14 @@ */ package com.nextcloud.client.device +import android.annotation.SuppressLint import android.content.Context import android.content.Intent import android.content.IntentFilter import android.os.BatteryManager import android.os.PowerManager +import android.provider.Settings +import androidx.core.net.toUri import com.nextcloud.utils.extensions.registerBroadcastReceiver import com.owncloud.android.datamodel.ReceiverFlag @@ -27,6 +30,23 @@ internal class PowerManagementServiceImpl( } } + /** + * Opens page for OS's battery saver screen. + */ + fun openBatterySaverPage() { + context.startActivity(Intent(Settings.ACTION_BATTERY_SAVER_SETTINGS)) + } + + /** + * Shows dialog to allow background usage for app. + */ + @SuppressLint("BatteryLife") + fun showIgnoreBatteryOptimizationDialog() { + val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) + intent.data = "package:${context.packageName}".toUri() + context.startActivity(intent) + } + override val isIgnoringOptimization: Boolean get() { val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager From 688fddb2b40e8a66307d5b3cbcbe3a9d0a0784ea Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Thu, 7 May 2026 13:35:08 +0200 Subject: [PATCH 07/15] wip Signed-off-by: alperozturk96 --- .../device/PowerManagementServiceImpl.kt | 20 --- .../component/AutoUploadWarningCardManager.kt | 45 +++-- .../ui/activity/SyncedFoldersActivity.kt | 2 +- .../android/ui/activity/UploadListActivity.kt | 2 +- ...to_upload_battery_saver_warning_banner.xml | 155 ++++++++++++------ app/src/main/res/values/strings.xml | 7 +- 6 files changed, 141 insertions(+), 90 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 9b03c65c05a0..8d3c9ad89645 100644 --- a/app/src/main/java/com/nextcloud/client/device/PowerManagementServiceImpl.kt +++ b/app/src/main/java/com/nextcloud/client/device/PowerManagementServiceImpl.kt @@ -6,14 +6,11 @@ */ package com.nextcloud.client.device -import android.annotation.SuppressLint import android.content.Context import android.content.Intent import android.content.IntentFilter import android.os.BatteryManager import android.os.PowerManager -import android.provider.Settings -import androidx.core.net.toUri import com.nextcloud.utils.extensions.registerBroadcastReceiver import com.owncloud.android.datamodel.ReceiverFlag @@ -30,23 +27,6 @@ internal class PowerManagementServiceImpl( } } - /** - * Opens page for OS's battery saver screen. - */ - fun openBatterySaverPage() { - context.startActivity(Intent(Settings.ACTION_BATTERY_SAVER_SETTINGS)) - } - - /** - * Shows dialog to allow background usage for app. - */ - @SuppressLint("BatteryLife") - fun showIgnoreBatteryOptimizationDialog() { - val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) - intent.data = "package:${context.packageName}".toUri() - context.startActivity(intent) - } - override val isIgnoringOptimization: Boolean get() { val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager 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 d900c09c8f07..10919a55e424 100644 --- a/app/src/main/java/com/nextcloud/ui/component/AutoUploadWarningCardManager.kt +++ b/app/src/main/java/com/nextcloud/ui/component/AutoUploadWarningCardManager.kt @@ -7,18 +7,22 @@ package com.nextcloud.ui.component +import android.annotation.SuppressLint import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter import android.os.PowerManager +import android.provider.Settings import android.view.View +import androidx.core.net.toUri import com.nextcloud.client.device.PowerManagementService import com.nextcloud.utils.extensions.setVisibleIf import com.owncloud.android.databinding.AutoUploadBatterySaverWarningBannerBinding import com.owncloud.android.utils.theme.ViewThemeUtils class AutoUploadWarningCardManager( + private val context: Context, private val powerManagementService: PowerManagementService, private val viewThemeUtils: ViewThemeUtils ) { @@ -28,19 +32,21 @@ class AutoUploadWarningCardManager( binding.root.setVisibleIf(isBatterySaver || isIgnoringOptimization) - 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 + if (isBatterySaver) { + viewThemeUtils.material.themeCardView(binding.batterySaverLayout) + binding.batterySaverLayout.visibility = View.VISIBLE + binding.batterySaverButton.setOnClickListener { + openBatterySaverPage() + } } - viewThemeUtils.material.themeCardView(binding.root) + if (isIgnoringOptimization) { + viewThemeUtils.material.themeCardView(binding.backgroundActivityLimitedLayout) + binding.backgroundActivityLimitedLayout.visibility = View.VISIBLE + binding.backgroundActivityLimitedButton.setOnClickListener { + showIgnoreBatteryOptimizationDialog() + } + } } // region listen power mode changes @@ -65,4 +71,21 @@ class AutoUploadWarningCardManager( binding = null } // endregion + + /** + * Opens page for OS's battery saver screen. + */ + private fun openBatterySaverPage() { + context.startActivity(Intent(Settings.ACTION_BATTERY_SAVER_SETTINGS)) + } + + /** + * Shows dialog to allow background usage for app. + */ + @SuppressLint("BatteryLife") + private fun showIgnoreBatteryOptimizationDialog() { + val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) + intent.data = "package:${context.packageName}".toUri() + context.startActivity(intent) + } } 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 ac7b3f30b165..c94751c21323 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) + autoUploadWarningCardManager = AutoUploadWarningCardManager(this, 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 36958c31d101..85db292cbc36 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 @@ -90,7 +90,7 @@ class UploadListActivity : binding = UploadListLayoutBinding.inflate(layoutInflater) val binding = binding!! setContentView(binding.getRoot()) - autoUploadWarningCardManager = AutoUploadWarningCardManager(powerManagementService, viewThemeUtils) + autoUploadWarningCardManager = AutoUploadWarningCardManager(this, powerManagementService, viewThemeUtils) swipeListRefreshLayout = binding.swipeContainingList // this activity has no file really bound, it's for multiple accounts at the same time; should no inherit 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 616df3c5c854..4287c6e66bce 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,75 +5,122 @@ ~ SPDX-FileCopyrightText: 2025 Alper Ozturk ~ SPDX-License-Identifier: AGPL-3.0-or-later --> - + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:visibility="gone" + tools:visibility="visible" + android:orientation="vertical"> - - - + android:layout_marginTop="@dimen/standard_half_margin" + android:layout_marginHorizontal="@dimen/standard_half_margin" + android:background="@drawable/rounded_rect_8dp" + android:backgroundTint="@color/task_container" + android:visibility="gone" + tools:visibility="visible" + android:elevation="4dp"> + android:orientation="vertical" + android:padding="@dimen/standard_padding"> - + android:gravity="center_vertical" + android:orientation="horizontal"> + + + + + - + android:layout_gravity="end" + android:layout_marginTop="12dp" + android:textAllCaps="false" + android:text="@string/auto_upload_battery_saver_action" /> + + - + + + + + android:gravity="center_vertical" + android:orientation="horizontal"> - + + + + - - \ No newline at end of file + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 74d8001d0e0a..46ac3a5e0863 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1484,10 +1484,11 @@ This folder is best viewed in %1$s. Open in %1$s Delete auto-upload folder? + Battery Saver is active and may pause uploads + Nextcloud\'s background activity is limited by battery optimization + Open battery saver settings + Allow This will remove the folder and auto-upload configuration. Any unfinished uploads will be canceled. - 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 af34391f6523756dfbac0342b2f1505aed5e7544 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Thu, 7 May 2026 13:42:08 +0200 Subject: [PATCH 08/15] wip Signed-off-by: alperozturk96 --- .../ui/component/AutoUploadWarningCardManager.kt | 8 ++++++-- .../layout/auto_upload_battery_saver_warning_banner.xml | 4 ++-- 2 files changed, 8 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 10919a55e424..413b96cbc3cf 100644 --- a/app/src/main/java/com/nextcloud/ui/component/AutoUploadWarningCardManager.kt +++ b/app/src/main/java/com/nextcloud/ui/component/AutoUploadWarningCardManager.kt @@ -30,7 +30,7 @@ class AutoUploadWarningCardManager( val isBatterySaver = powerManagementService.isPowerSavingEnabled val isIgnoringOptimization = powerManagementService.isIgnoringOptimization - binding.root.setVisibleIf(isBatterySaver || isIgnoringOptimization) + binding.root.setVisibleIf(isBatterySaver || !isIgnoringOptimization) if (isBatterySaver) { viewThemeUtils.material.themeCardView(binding.batterySaverLayout) @@ -38,14 +38,18 @@ class AutoUploadWarningCardManager( binding.batterySaverButton.setOnClickListener { openBatterySaverPage() } + } else { + binding.batterySaverLayout.visibility = View.GONE } - if (isIgnoringOptimization) { + if (!isIgnoringOptimization) { viewThemeUtils.material.themeCardView(binding.backgroundActivityLimitedLayout) binding.backgroundActivityLimitedLayout.visibility = View.VISIBLE binding.backgroundActivityLimitedButton.setOnClickListener { showIgnoreBatteryOptimizationDialog() } + } else { + binding.backgroundActivityLimitedLayout.visibility = View.GONE } } 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 4287c6e66bce..4434bb2b20cd 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 @@ -78,14 +78,14 @@ android:layout_marginHorizontal="@dimen/standard_half_margin" android:background="@drawable/rounded_rect_8dp" android:backgroundTint="@color/task_container" + android:visibility="gone" + tools:visibility="visible" android:elevation="4dp"> Date: Thu, 7 May 2026 13:45:28 +0200 Subject: [PATCH 09/15] wip Signed-off-by: alperozturk96 --- .../owncloud/android/ui/activity/SyncedFoldersActivity.kt | 2 +- .../owncloud/android/ui/activity/UploadListActivity.kt | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) 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 c94751c21323..b8354bf9af6b 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 @@ -211,6 +211,7 @@ class SyncedFoldersActivity : override fun onResume() { super.onResume() highlightNavigationViewItem(menuItemId) + autoUploadWarningCardManager?.bind(binding.autoUploadBatterySaverWarningCard) } fun setupStoragePermissionWarningBanner() { @@ -260,7 +261,6 @@ class SyncedFoldersActivity : powerManagementService, connectivityService ) - autoUploadWarningCardManager?.bind(binding.autoUploadBatterySaverWarningCard) autoUploadWarningCardManager?.register(this, binding.autoUploadBatterySaverWarningCard) binding.emptyList.emptyListIcon.setImageResource(R.drawable.nav_synced_folders) 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 85db292cbc36..7aeaaf6faf34 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 @@ -123,7 +123,6 @@ class UploadListActivity : binding?.autoUploadBatterySaverWarningCard?.let { autoUploadWarningCardManager?.register(this, it) - autoUploadWarningCardManager?.bind(it) } val lm = GridLayoutManager(this, 1) @@ -265,6 +264,13 @@ class UploadListActivity : } } + override fun onResume() { + super.onResume() + binding?.autoUploadBatterySaverWarningCard?.let { + autoUploadWarningCardManager?.bind(it) + } + } + override fun onRemoteOperationFinish(operation: RemoteOperation<*>?, result: RemoteOperationResult<*>) { if (operation !is CheckCurrentCredentialsOperation) { super.onRemoteOperationFinish(operation, result) From 4fee5c55cf0d55fb93eea9e0cbe18ad90cd707c5 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Thu, 7 May 2026 13:48:07 +0200 Subject: [PATCH 10/15] wip Signed-off-by: alperozturk96 --- .../res/layout/auto_upload_battery_saver_warning_banner.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 4434bb2b20cd..ecef4c5986a0 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 @@ -44,7 +44,7 @@ android:layout_width="@dimen/standard_icon_size" android:layout_height="@dimen/standard_icon_size" android:contentDescription="@null" - android:src="@drawable/ic_warning" + android:src="@drawable/ic_battery_alert" app:tint="@color/log_level_warning" /> Date: Thu, 7 May 2026 13:53:06 +0200 Subject: [PATCH 11/15] wip Signed-off-by: alperozturk96 --- ...oadWarningCardManager.kt => UploadWarningCard.kt} | 10 +++++----- .../android/ui/activity/SyncedFoldersActivity.kt | 12 ++++++------ .../android/ui/activity/UploadListActivity.kt | 12 ++++++------ .../android/ui/adapter/SyncedFolderAdapter.kt | 1 - app/src/main/res/layout/synced_folders_layout.xml | 2 +- app/src/main/res/layout/upload_list_layout.xml | 2 +- ...er_warning_banner.xml => upload_warning_card.xml} | 0 7 files changed, 19 insertions(+), 20 deletions(-) rename app/src/main/java/com/nextcloud/ui/component/{AutoUploadWarningCardManager.kt => UploadWarningCard.kt} (89%) rename app/src/main/res/layout/{auto_upload_battery_saver_warning_banner.xml => upload_warning_card.xml} (100%) diff --git a/app/src/main/java/com/nextcloud/ui/component/AutoUploadWarningCardManager.kt b/app/src/main/java/com/nextcloud/ui/component/UploadWarningCard.kt similarity index 89% rename from app/src/main/java/com/nextcloud/ui/component/AutoUploadWarningCardManager.kt rename to app/src/main/java/com/nextcloud/ui/component/UploadWarningCard.kt index 413b96cbc3cf..423f20235840 100644 --- a/app/src/main/java/com/nextcloud/ui/component/AutoUploadWarningCardManager.kt +++ b/app/src/main/java/com/nextcloud/ui/component/UploadWarningCard.kt @@ -18,15 +18,15 @@ import android.view.View import androidx.core.net.toUri import com.nextcloud.client.device.PowerManagementService import com.nextcloud.utils.extensions.setVisibleIf -import com.owncloud.android.databinding.AutoUploadBatterySaverWarningBannerBinding +import com.owncloud.android.databinding.UploadWarningCardBinding import com.owncloud.android.utils.theme.ViewThemeUtils -class AutoUploadWarningCardManager( +class UploadWarningCard( private val context: Context, private val powerManagementService: PowerManagementService, private val viewThemeUtils: ViewThemeUtils ) { - fun bind(binding: AutoUploadBatterySaverWarningBannerBinding) { + fun bind(binding: UploadWarningCardBinding) { val isBatterySaver = powerManagementService.isPowerSavingEnabled val isIgnoringOptimization = powerManagementService.isIgnoringOptimization @@ -54,7 +54,7 @@ class AutoUploadWarningCardManager( } // region listen power mode changes - private var binding: AutoUploadBatterySaverWarningBannerBinding? = null + private var binding: UploadWarningCardBinding? = null private val batterySaverReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { @@ -64,7 +64,7 @@ class AutoUploadWarningCardManager( } } - fun register(context: Context, binding: AutoUploadBatterySaverWarningBannerBinding) { + fun register(context: Context, binding: UploadWarningCardBinding) { this.binding = binding bind(binding) context.registerReceiver(batterySaverReceiver, IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)) 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 b8354bf9af6b..ad2d9f4b4a7e 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,7 +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.ui.component.UploadWarningCard import com.nextcloud.utils.BatteryOptimizationHelper import com.nextcloud.utils.extensions.getParcelableArgument import com.nextcloud.utils.extensions.isDialogFragmentReady @@ -153,7 +153,7 @@ class SyncedFoldersActivity : @Inject lateinit var appInfo: AppInfo - private var autoUploadWarningCardManager: AutoUploadWarningCardManager? = null + private var uploadWarningCard: UploadWarningCard? = null lateinit var binding: SyncedFoldersLayoutBinding lateinit var adapter: SyncedFolderAdapter @@ -166,7 +166,7 @@ class SyncedFoldersActivity : super.onCreate(savedInstanceState) binding = SyncedFoldersLayoutBinding.inflate(layoutInflater) setContentView(binding.root) - autoUploadWarningCardManager = AutoUploadWarningCardManager(this, powerManagementService, viewThemeUtils) + uploadWarningCard = UploadWarningCard(this, powerManagementService, viewThemeUtils) if (intent != null && intent.extras != null) { val accountName = intent.extras!!.getString(NotificationWork.KEY_NOTIFICATION_ACCOUNT) val optionalUser = user @@ -211,7 +211,7 @@ class SyncedFoldersActivity : override fun onResume() { super.onResume() highlightNavigationViewItem(menuItemId) - autoUploadWarningCardManager?.bind(binding.autoUploadBatterySaverWarningCard) + uploadWarningCard?.bind(binding.autoUploadBatterySaverWarningCard) } fun setupStoragePermissionWarningBanner() { @@ -261,7 +261,7 @@ class SyncedFoldersActivity : powerManagementService, connectivityService ) - autoUploadWarningCardManager?.register(this, binding.autoUploadBatterySaverWarningCard) + uploadWarningCard?.register(this, binding.autoUploadBatterySaverWarningCard) binding.emptyList.emptyListIcon.setImageResource(R.drawable.nav_synced_folders) viewThemeUtils.material.colorMaterialButtonPrimaryFilled(binding.emptyList.emptyListViewAction) @@ -284,7 +284,7 @@ class SyncedFoldersActivity : override fun onDestroy() { super.onDestroy() - autoUploadWarningCardManager?.unregister(this) + uploadWarningCard?.unregister(this) } /** 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 7aeaaf6faf34..5755eca72413 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,7 +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.ui.component.UploadWarningCard import com.nextcloud.utils.extensions.webDavParentPath import com.owncloud.android.R import com.owncloud.android.databinding.UploadListLayoutBinding @@ -73,7 +73,7 @@ class UploadListActivity : @Inject lateinit var uploadFileOperationFactory: UploadFileOperationFactory - private var autoUploadWarningCardManager: AutoUploadWarningCardManager? = null + private var uploadWarningCard: UploadWarningCard? = null private var swipeListRefreshLayout: SwipeRefreshLayout? = null private var binding: UploadListLayoutBinding? = null @@ -90,7 +90,7 @@ class UploadListActivity : binding = UploadListLayoutBinding.inflate(layoutInflater) val binding = binding!! setContentView(binding.getRoot()) - autoUploadWarningCardManager = AutoUploadWarningCardManager(this, powerManagementService, viewThemeUtils) + uploadWarningCard = UploadWarningCard(this, powerManagementService, viewThemeUtils) swipeListRefreshLayout = binding.swipeContainingList // this activity has no file really bound, it's for multiple accounts at the same time; should no inherit @@ -122,7 +122,7 @@ class UploadListActivity : ) binding?.autoUploadBatterySaverWarningCard?.let { - autoUploadWarningCardManager?.register(this, it) + uploadWarningCard?.register(this, it) } val lm = GridLayoutManager(this, 1) @@ -267,7 +267,7 @@ class UploadListActivity : override fun onResume() { super.onResume() binding?.autoUploadBatterySaverWarningCard?.let { - autoUploadWarningCardManager?.bind(it) + uploadWarningCard?.bind(it) } } @@ -385,7 +385,7 @@ class UploadListActivity : override fun onDestroy() { super.onDestroy() - autoUploadWarningCardManager?.unregister(this) + uploadWarningCard?.unregister(this) } companion object { 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 ebf7460b2cab..126af0068702 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,7 +25,6 @@ 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 diff --git a/app/src/main/res/layout/synced_folders_layout.xml b/app/src/main/res/layout/synced_folders_layout.xml index 11c1de553be6..d0da597e6f7b 100644 --- a/app/src/main/res/layout/synced_folders_layout.xml +++ b/app/src/main/res/layout/synced_folders_layout.xml @@ -32,7 +32,7 @@ Date: Wed, 13 May 2026 11:26:44 +0200 Subject: [PATCH 12/15] Update app/src/main/res/values/strings.xml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Tobias Kaminsky Signed-off-by: Alper Öztürk <67455295+alperozturk96@users.noreply.github.com> --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 46ac3a5e0863..6778b4d25d51 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1484,7 +1484,7 @@ This folder is best viewed in %1$s. Open in %1$s Delete auto-upload folder? - Battery Saver is active and may pause uploads + Battery saver is active and may pause uploads. Nextcloud\'s background activity is limited by battery optimization Open battery saver settings Allow From 82b45b0a1e18c79f87ac853ead90125b5cd3666f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alper=20=C3=96zt=C3=BCrk?= <67455295+alperozturk96@users.noreply.github.com> Date: Wed, 13 May 2026 11:26:51 +0200 Subject: [PATCH 13/15] Update app/src/main/res/values/strings.xml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Tobias Kaminsky Signed-off-by: Alper Öztürk <67455295+alperozturk96@users.noreply.github.com> --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6778b4d25d51..f81ea9b40d2d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1485,7 +1485,7 @@ Open in %1$s Delete auto-upload folder? Battery saver is active and may pause uploads. - Nextcloud\'s background activity is limited by battery optimization + Nextcloud\'s background activity is limited by battery optimization. Open battery saver settings Allow This will remove the folder and auto-upload configuration. Any unfinished uploads will be canceled. From 4cd3df76da9eefe2b5d3dd12ff43d9218a5fd4ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alper=20=C3=96zt=C3=BCrk?= <67455295+alperozturk96@users.noreply.github.com> Date: Wed, 13 May 2026 11:26:58 +0200 Subject: [PATCH 14/15] Update app/src/main/res/values/strings.xml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Tobias Kaminsky Signed-off-by: Alper Öztürk <67455295+alperozturk96@users.noreply.github.com> --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f81ea9b40d2d..2c677214a33c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1487,7 +1487,7 @@ Battery saver is active and may pause uploads. Nextcloud\'s background activity is limited by battery optimization. Open battery saver settings - Allow + Allow background activity This will remove the folder and auto-upload configuration. Any unfinished uploads will be canceled. This folder is already included in the parent folder’s sync, which may cause duplicate uploads Sync anyway From 183b834d9815ffa39cb988efbed95fb17d269515 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 13 May 2026 11:27:50 +0200 Subject: [PATCH 15/15] wip Signed-off-by: alperozturk96 --- app/src/main/res/layout/upload_warning_card.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/layout/upload_warning_card.xml b/app/src/main/res/layout/upload_warning_card.xml index ecef4c5986a0..4155c8bcaaaa 100644 --- a/app/src/main/res/layout/upload_warning_card.xml +++ b/app/src/main/res/layout/upload_warning_card.xml @@ -98,7 +98,7 @@ android:layout_width="@dimen/standard_icon_size" android:layout_height="@dimen/standard_icon_size" android:contentDescription="@null" - android:src="@drawable/ic_history" + android:src="@drawable/ic_sync" app:tint="@color/log_level_warning" />