From e683c4bbed866a8b90c48353502a704c0be8e76b Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 28 Nov 2025 15:22:01 +0100 Subject: [PATCH] fix: battery optimization check Signed-off-by: alperozturk --- .../utils/BatteryOptimizationHelper.kt | 46 ++++++++++++ .../ui/activity/SyncedFoldersActivity.kt | 70 ++++++++----------- 2 files changed, 75 insertions(+), 41 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/utils/BatteryOptimizationHelper.kt diff --git a/app/src/main/java/com/nextcloud/utils/BatteryOptimizationHelper.kt b/app/src/main/java/com/nextcloud/utils/BatteryOptimizationHelper.kt new file mode 100644 index 000000000000..eb4add0298c1 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/BatteryOptimizationHelper.kt @@ -0,0 +1,46 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils + +import android.annotation.SuppressLint +import android.content.Context +import android.content.Intent +import android.os.PowerManager +import android.provider.Settings +import androidx.core.net.toUri +import com.owncloud.android.lib.common.utils.Log_OC + +object BatteryOptimizationHelper { + + private const val TAG = "BatteryOptimizationHelper" + + fun isBatteryOptimizationEnabled(context: Context): Boolean { + val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager + return !pm.isIgnoringBatteryOptimizations(context.packageName) + } + + @Suppress("TooGenericExceptionCaught") + @SuppressLint("BatteryLife") + fun openBatteryOptimizationSettings(context: Context) { + try { + val intent = Intent( + Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, + "package:${context.packageName}".toUri() + ) + + if (intent.resolveActivity(context.packageManager) != null) { + context.startActivity(intent) + } else { + // Fallback to generic battery optimization settings + context.startActivity(Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS)) + } + } catch (e: Exception) { + Log_OC.d(TAG, "open battery optimization settings: ", e) + } + } +} 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 96a853af2a40..d470bfeec7b3 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 @@ -14,15 +14,12 @@ import android.content.Intent import android.content.pm.PackageManager import android.os.Bundle import android.os.Looper -import android.os.PowerManager -import android.provider.Settings import android.text.TextUtils import android.view.Menu import android.view.MenuItem import android.view.View import androidx.annotation.VisibleForTesting import androidx.appcompat.app.AlertDialog -import androidx.core.net.toUri import androidx.drawerlayout.widget.DrawerLayout import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope @@ -36,10 +33,10 @@ 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.utils.BatteryOptimizationHelper import com.nextcloud.utils.extensions.getParcelableArgument import com.nextcloud.utils.extensions.isDialogFragmentReady import com.nextcloud.utils.extensions.setVisibleIf -import com.owncloud.android.BuildConfig import com.owncloud.android.MainApp import com.owncloud.android.R import com.owncloud.android.databinding.StoragePermissionWarningBannerBinding @@ -576,7 +573,7 @@ class SyncedFoldersActivity : } if (syncedFolderDisplayItem.isEnabled) { backgroundJobManager.startAutoUploadImmediately(syncedFolderDisplayItem, overridePowerSaving = false) - showBatteryOptimizationInfo() + showBatteryOptimizationDialogIfNeeded() } } @@ -710,7 +707,7 @@ class SyncedFoldersActivity : } dialogFragment = null if (syncedFolder.isEnabled) { - showBatteryOptimizationInfo() + showBatteryOptimizationDialogIfNeeded() } } @@ -834,44 +831,35 @@ class SyncedFoldersActivity : } } - private fun showBatteryOptimizationInfo() { - if (checkIfBatteryOptimizationEnabled()) { - val alertDialogBuilder = MaterialAlertDialogBuilder(this, R.style.Theme_ownCloud_Dialog) - .setTitle(getString(R.string.battery_optimization_title)) - .setMessage(getString(R.string.battery_optimization_message)) - .setPositiveButton(getString(R.string.battery_optimization_disable)) { _, _ -> - // show instant upload - @SuppressLint("BatteryLife") - val intent = Intent( - Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, - ("package:" + BuildConfig.APPLICATION_ID).toUri() - ) - if (intent.resolveActivity(packageManager) != null) { - startActivity(intent) - } - } - .setNeutralButton(getString(R.string.battery_optimization_close)) { dialog, _ -> dialog.dismiss() } - .setIcon(R.drawable.ic_battery_alert) - if (lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) { - val alertDialog = alertDialogBuilder.show() - viewThemeUtils.platform.colorTextButtons( - alertDialog.getButton(AlertDialog.BUTTON_POSITIVE), - alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL) - ) - } + private fun showBatteryOptimizationDialogIfNeeded() { + if (!BatteryOptimizationHelper.isBatteryOptimizationEnabled(this)) { + Log_OC.d(TAG, "battery optimization is disabled") + return } + + showBatteryOptimizationDialog() } - /** - * Check if battery optimization is enabled. If unknown, fallback to true. - * - * @return true if battery optimization is enabled - */ - private fun checkIfBatteryOptimizationEnabled(): Boolean { - val powerManager = getSystemService(POWER_SERVICE) as PowerManager? - return when { - powerManager != null -> !powerManager.isIgnoringBatteryOptimizations(BuildConfig.APPLICATION_ID) - else -> !appInfo.isDebugBuild + private fun showBatteryOptimizationDialog() { + if (!lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) { + Log_OC.w(TAG, "Activity not resumed, skipping battery dialog") + return } + + val dialog = MaterialAlertDialogBuilder(this, R.style.Theme_ownCloud_Dialog) + .setTitle(R.string.battery_optimization_title) + .setMessage(R.string.battery_optimization_message) + .setPositiveButton(R.string.battery_optimization_disable) { _, _ -> + BatteryOptimizationHelper.openBatteryOptimizationSettings(this) + } + .setNeutralButton(R.string.battery_optimization_close, null) + .setIcon(R.drawable.ic_battery_alert) + + val alertDialog = dialog.show() + + viewThemeUtils.platform.colorTextButtons( + alertDialog.getButton(AlertDialog.BUTTON_POSITIVE), + alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL) + ) } }