Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions app/src/main/java/com/nextcloud/utils/BatteryOptimizationHelper.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2025 Alper Ozturk <[email protected]>
* 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)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -576,7 +573,7 @@ class SyncedFoldersActivity :
}
if (syncedFolderDisplayItem.isEnabled) {
backgroundJobManager.startAutoUploadImmediately(syncedFolderDisplayItem, overridePowerSaving = false)
showBatteryOptimizationInfo()
showBatteryOptimizationDialogIfNeeded()
}
}

Expand Down Expand Up @@ -710,7 +707,7 @@ class SyncedFoldersActivity :
}
dialogFragment = null
if (syncedFolder.isEnabled) {
showBatteryOptimizationInfo()
showBatteryOptimizationDialogIfNeeded()
}
}

Expand Down Expand Up @@ -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)
)
}
}
Loading