diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts
index a1391d3d8..895829ad7 100644
--- a/composeApp/build.gradle.kts
+++ b/composeApp/build.gradle.kts
@@ -53,7 +53,6 @@ kotlin {
// This makes instrumented tests depend on commonTest and androidUnitTest sources
sourceSetTree.set(KotlinSourceSetTree.test)
}
- instrumentedTestVariant { }
}
iosX64()
diff --git a/composeApp/src/androidInstrumentedTest/kotlin/org/ooni/probe/uitesting/OnboardingTest.kt b/composeApp/src/androidInstrumentedTest/kotlin/org/ooni/probe/uitesting/OnboardingTest.kt
index cf1075fff..62b319a9d 100644
--- a/composeApp/src/androidInstrumentedTest/kotlin/org/ooni/probe/uitesting/OnboardingTest.kt
+++ b/composeApp/src/androidInstrumentedTest/kotlin/org/ooni/probe/uitesting/OnboardingTest.kt
@@ -46,7 +46,7 @@ class OnboardingTest {
clickOnText("True")
wait { onNodeWithText("Automated testing").isDisplayed() }
- clickOnTag("Yes-AutoTest")
+ clickOnTag("No-AutoTest")
wait { onNodeWithText("Crash Reporting").isDisplayed() }
clickOnTag("Yes-CrashReporting")
@@ -63,7 +63,7 @@ class OnboardingTest {
}
assertEquals(
- true,
+ false,
preferences.getValueByKey(SettingsKey.AUTOMATED_TESTING_ENABLED).first(),
)
assertEquals(true, preferences.getValueByKey(SettingsKey.SEND_CRASH).first())
diff --git a/composeApp/src/androidMain/AndroidManifest.xml b/composeApp/src/androidMain/AndroidManifest.xml
index 8a2afc484..1a6034df9 100644
--- a/composeApp/src/androidMain/AndroidManifest.xml
+++ b/composeApp/src/androidMain/AndroidManifest.xml
@@ -6,6 +6,7 @@
+
= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
@@ -65,6 +70,7 @@ class AndroidApplication : Application() {
LocaleList.forLanguageTags(getString(R.string.supported_languages)),
)
}
+ registerActivityLifecycleCallbacks(mainActivityLifecycleCallbacks)
}
private val platformInfo by lazy {
@@ -194,4 +200,14 @@ class AndroidApplication : Application() {
false
}
}
+
+ private val batteryOptimization by lazy {
+ AndroidBatteryOptimization(
+ powerManager = getSystemService(PowerManager::class.java),
+ packageName = packageName,
+ requestCall = { callback ->
+ mainActivityLifecycleCallbacks.activity?.requestIgnoreBatteryOptimization(callback)
+ },
+ )
+ }
}
diff --git a/composeApp/src/androidMain/kotlin/org/ooni/probe/MainActivity.kt b/composeApp/src/androidMain/kotlin/org/ooni/probe/MainActivity.kt
index 15f572b54..157eee80e 100644
--- a/composeApp/src/androidMain/kotlin/org/ooni/probe/MainActivity.kt
+++ b/composeApp/src/androidMain/kotlin/org/ooni/probe/MainActivity.kt
@@ -1,10 +1,15 @@
package org.ooni.probe
+import android.annotation.SuppressLint
+import android.content.Context
import android.content.Intent
+import android.net.Uri
import android.os.Bundle
+import android.provider.Settings
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
+import androidx.activity.result.contract.ActivityResultContract
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@@ -60,4 +65,28 @@ class MainActivity : ComponentActivity() {
}
}
}
+
+ // Battery Optimization
+
+ private var ignoreBatteryOptimizationCallback: (() -> Unit)? = null
+
+ private val ignoreBatteryOptimizationContract =
+ registerForActivityResult(object : ActivityResultContract() {
+ @SuppressLint("BatteryLife")
+ override fun createIntent(
+ context: Context,
+ input: Unit,
+ ) = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
+ .setData(Uri.parse("package:$packageName"))
+
+ override fun parseResult(
+ resultCode: Int,
+ intent: Intent?,
+ ) {}
+ }) { ignoreBatteryOptimizationCallback?.invoke() }
+
+ fun requestIgnoreBatteryOptimization(callback: () -> Unit) {
+ ignoreBatteryOptimizationCallback = callback
+ ignoreBatteryOptimizationContract.launch(Unit)
+ }
}
diff --git a/composeApp/src/androidMain/kotlin/org/ooni/probe/MainActivityLifecycleCallbacks.kt b/composeApp/src/androidMain/kotlin/org/ooni/probe/MainActivityLifecycleCallbacks.kt
new file mode 100644
index 000000000..0a42c6701
--- /dev/null
+++ b/composeApp/src/androidMain/kotlin/org/ooni/probe/MainActivityLifecycleCallbacks.kt
@@ -0,0 +1,34 @@
+package org.ooni.probe
+
+import android.app.Activity
+import android.app.Application.ActivityLifecycleCallbacks
+import android.os.Bundle
+
+class MainActivityLifecycleCallbacks : ActivityLifecycleCallbacks {
+ var activity: MainActivity? = null
+ private set
+
+ override fun onActivityCreated(
+ activity: Activity,
+ savedInstanceState: Bundle?,
+ ) {
+ this.activity = activity as? MainActivity
+ }
+
+ override fun onActivityStarted(activity: Activity) {}
+
+ override fun onActivityResumed(activity: Activity) {}
+
+ override fun onActivityPaused(activity: Activity) {}
+
+ override fun onActivityStopped(activity: Activity) {}
+
+ override fun onActivitySaveInstanceState(
+ activity: Activity,
+ outState: Bundle,
+ ) {}
+
+ override fun onActivityDestroyed(activity: Activity) {
+ this.activity = null
+ }
+}
diff --git a/composeApp/src/androidMain/kotlin/org/ooni/probe/config/AndroidBatteryOptimization.kt b/composeApp/src/androidMain/kotlin/org/ooni/probe/config/AndroidBatteryOptimization.kt
new file mode 100644
index 000000000..592196cc8
--- /dev/null
+++ b/composeApp/src/androidMain/kotlin/org/ooni/probe/config/AndroidBatteryOptimization.kt
@@ -0,0 +1,20 @@
+package org.ooni.probe.config
+
+import android.os.PowerManager
+
+class AndroidBatteryOptimization(
+ private val powerManager: PowerManager,
+ private val packageName: String,
+ private val requestCall: (() -> Unit) -> Unit,
+) : BatteryOptimization {
+ override val isSupported = true
+
+ override val isIgnoring: Boolean
+ get() = powerManager.isIgnoringBatteryOptimizations(packageName)
+
+ override fun requestIgnore(onResponse: (Boolean) -> Unit) {
+ requestCall {
+ onResponse(isIgnoring)
+ }
+ }
+}
diff --git a/composeApp/src/commonMain/composeResources/values/strings-common.xml b/composeApp/src/commonMain/composeResources/values/strings-common.xml
index 91f3c117d..7f4351d81 100644
--- a/composeApp/src/commonMain/composeResources/values/strings-common.xml
+++ b/composeApp/src/commonMain/composeResources/values/strings-common.xml
@@ -224,6 +224,8 @@
Are you sure?
Your URLs will not be saved when you leave this screen. Are you sure you want to leave this screen?
+ OONI Probe cannot run automatically without battery optimization. Do you want to try again?
+
Back
refresh
diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/config/BatteryOptimization.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/config/BatteryOptimization.kt
new file mode 100644
index 000000000..b39696258
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/config/BatteryOptimization.kt
@@ -0,0 +1,12 @@
+package org.ooni.probe.config
+
+interface BatteryOptimization {
+ val isSupported: Boolean get() = false
+
+ val isIgnoring: Boolean
+ get() = throw IllegalStateException("Battery Optimization not supported")
+
+ fun requestIgnore(onResponse: (Boolean) -> Unit) {
+ throw IllegalStateException("Battery Optimization not supported")
+ }
+}
diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt
index 3a1c5fd11..a86ec9036 100644
--- a/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt
+++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt
@@ -22,6 +22,7 @@ import org.ooni.engine.NetworkTypeFinder
import org.ooni.engine.OonimkallBridge
import org.ooni.engine.TaskEventMapper
import org.ooni.probe.Database
+import org.ooni.probe.config.BatteryOptimization
import org.ooni.probe.data.disk.DeleteFiles
import org.ooni.probe.data.disk.DeleteFilesOkio
import org.ooni.probe.data.disk.ReadFile
@@ -111,6 +112,7 @@ class Dependencies(
val fetchDescriptorUpdate: suspend (List?) -> Unit,
val localeDirection: (() -> LayoutDirection)? = null,
private val shareFile: (FileSharing) -> Boolean,
+ private val batteryOptimization: BatteryOptimization,
) {
// Common
@@ -426,6 +428,7 @@ class Dependencies(
platformInfo = platformInfo,
preferenceRepository = preferenceRepository,
launchUrl = { launchUrl(it, null) },
+ batteryOptimization = batteryOptimization,
)
fun proxyViewModel(onBack: () -> Unit) = ProxyViewModel(onBack, preferenceRepository)
diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/onboarding/OnboardingScreen.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/onboarding/OnboardingScreen.kt
index d23844d42..d282a9947 100644
--- a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/onboarding/OnboardingScreen.kt
+++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/onboarding/OnboardingScreen.kt
@@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars
@@ -20,11 +19,10 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
-import androidx.compose.foundation.pager.HorizontalPager
-import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.LocalContentColor
@@ -34,7 +32,6 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -48,6 +45,7 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.DialogProperties
import co.touchlab.kermit.Logger
import dev.icerock.moko.permissions.DeniedAlwaysException
import dev.icerock.moko.permissions.DeniedException
@@ -58,8 +56,11 @@ import dev.icerock.moko.permissions.compose.PermissionsControllerFactory
import dev.icerock.moko.permissions.compose.rememberPermissionsControllerFactory
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
+import ooniprobe.composeapp.generated.resources.Modal_Autorun_BatteryOptimization
+import ooniprobe.composeapp.generated.resources.Modal_Cancel
import ooniprobe.composeapp.generated.resources.Modal_EnableNotifications_Paragraph
import ooniprobe.composeapp.generated.resources.Modal_EnableNotifications_Title
+import ooniprobe.composeapp.generated.resources.Modal_OK
import ooniprobe.composeapp.generated.resources.Onboarding_AutomatedTesting_Paragraph
import ooniprobe.composeapp.generated.resources.Onboarding_AutomatedTesting_Title
import ooniprobe.composeapp.generated.resources.Onboarding_Crash_Button_No
@@ -97,54 +98,44 @@ fun OnboardingScreen(
state: OnboardingViewModel.State,
onEvent: (OnboardingViewModel.Event) -> Unit,
) {
- val pagerState = rememberPagerState(0, pageCount = { state.stepList.size })
- LaunchedEffect(state.stepIndex) {
- pagerState.animateScrollToPage(state.stepIndex)
- }
var showQuiz by remember { mutableStateOf(false) }
Box {
- HorizontalPager(
- state = pagerState,
- userScrollEnabled = false,
- modifier = Modifier.fillMaxSize(),
- ) { stepIndex ->
- val step = state.stepList[state.stepIndex]
- Surface(
- color = step.surfaceColor,
- contentColor = LocalCustomColors.current.onOnboarding,
+ Surface(
+ color = state.step.surfaceColor,
+ contentColor = LocalCustomColors.current.onOnboarding,
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxHeight()
+ .padding(WindowInsets.navigationBars.asPaddingValues())
+ .padding(bottom = 48.dp),
) {
- Column(
- modifier = Modifier
- .fillMaxHeight()
- .padding(WindowInsets.navigationBars.asPaddingValues())
- .padding(bottom = 48.dp),
- ) {
- when (state.stepList[stepIndex]) {
- OnboardingViewModel.Step.WhatIs ->
- WhatIsStep(onEvent)
-
- OnboardingViewModel.Step.HeadsUp ->
- HeadsUpStep(
- onEvent = onEvent,
- onShowQuiz = { showQuiz = true },
- )
-
- OnboardingViewModel.Step.AutomatedTesting ->
- AutomatedTestingStep(onEvent)
-
- OnboardingViewModel.Step.CrashReporting ->
- CrashReportingStep(onEvent)
-
- OnboardingViewModel.Step.RequestNotificationPermission ->
- RequestPermissionStep(onEvent)
-
- OnboardingViewModel.Step.DefaultSettings ->
- DefaultSettingsStep(onEvent)
- }
+ when (state.step) {
+ OnboardingViewModel.Step.WhatIs ->
+ WhatIsStep(onEvent)
+
+ OnboardingViewModel.Step.HeadsUp ->
+ HeadsUpStep(
+ onEvent = onEvent,
+ onShowQuiz = { showQuiz = true },
+ )
+
+ is OnboardingViewModel.Step.AutomatedTesting ->
+ AutomatedTestingStep(state.step.showBatteryOptimizationDialog, onEvent)
+
+ OnboardingViewModel.Step.CrashReporting ->
+ CrashReportingStep(onEvent)
+
+ OnboardingViewModel.Step.RequestNotificationPermission ->
+ RequestPermissionStep(onEvent)
+
+ OnboardingViewModel.Step.DefaultSettings ->
+ DefaultSettingsStep(onEvent)
}
}
}
+
Row(
Modifier
.wrapContentHeight()
@@ -153,11 +144,11 @@ fun OnboardingScreen(
.padding(WindowInsets.navigationBars.asPaddingValues()),
verticalAlignment = Alignment.CenterVertically,
) {
- repeat(pagerState.pageCount) { index ->
+ repeat(state.totalSteps) { index ->
if (index != 0) {
Box(
modifier = Modifier
- .alpha(if (pagerState.currentPage >= index) 1f else 0.33f)
+ .alpha(if (state.stepIndex >= index) 1f else 0.33f)
.background(LocalCustomColors.current.onOnboarding)
.height(2.dp)
.width(36.dp),
@@ -165,7 +156,7 @@ fun OnboardingScreen(
}
Box(
modifier = Modifier
- .alpha(if (pagerState.currentPage >= index) 1f else 0.33f)
+ .alpha(if (state.stepIndex >= index) 1f else 0.33f)
.padding(1.dp)
.clip(CircleShape)
.background(LocalCustomColors.current.onOnboarding)
@@ -245,7 +236,10 @@ fun ColumnScope.HeadsUpStep(
}
@Composable
-fun ColumnScope.AutomatedTestingStep(onEvent: (OnboardingViewModel.Event) -> Unit) {
+fun ColumnScope.AutomatedTestingStep(
+ showBatteryOptimizationDialog: Boolean,
+ onEvent: (OnboardingViewModel.Event) -> Unit,
+) {
OnboardingImage(OrganizationConfig.onboardingImages.image3)
OnboardingTitle(Res.string.Onboarding_AutomatedTesting_Title)
Column(
@@ -263,15 +257,37 @@ fun ColumnScope.AutomatedTestingStep(onEvent: (OnboardingViewModel.Event) -> Uni
onClick = { onEvent(OnboardingViewModel.Event.AutoTestNoClicked) },
modifier = Modifier
.padding(horizontal = 8.dp)
- .weight(1f),
+ .weight(1f)
+ .testTag("No-AutoTest"),
)
OnboardingMainButton(
text = Res.string.Onboarding_Crash_Button_Yes,
onClick = { onEvent(OnboardingViewModel.Event.AutoTestYesClicked) },
modifier = Modifier
.padding(horizontal = 8.dp)
- .weight(1f)
- .testTag("Yes-AutoTest"),
+ .weight(1f),
+ )
+ }
+
+ if (showBatteryOptimizationDialog) {
+ AlertDialog(
+ onDismissRequest = { },
+ text = { Text(stringResource(Res.string.Modal_Autorun_BatteryOptimization)) },
+ confirmButton = {
+ TextButton(onClick = {
+ onEvent(OnboardingViewModel.Event.BatteryOptimizationOkClicked)
+ }) {
+ Text(stringResource(Res.string.Modal_OK))
+ }
+ },
+ dismissButton = {
+ TextButton(onClick = {
+ onEvent(OnboardingViewModel.Event.BatteryOptimizationCancelClicked)
+ }) {
+ Text(stringResource(Res.string.Modal_Cancel))
+ }
+ },
+ properties = DialogProperties(dismissOnClickOutside = false, dismissOnBackPress = false),
)
}
}
diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/onboarding/OnboardingViewModel.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/onboarding/OnboardingViewModel.kt
index b04c06458..46ce4b38d 100644
--- a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/onboarding/OnboardingViewModel.kt
+++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/onboarding/OnboardingViewModel.kt
@@ -9,10 +9,12 @@ import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+import org.ooni.probe.config.BatteryOptimization
import org.ooni.probe.data.models.SettingsKey
import org.ooni.probe.data.repositories.PreferenceRepository
import org.ooni.probe.shared.PlatformInfo
-import org.ooni.probe.ui.dashboard.DashboardViewModel.Event
class OnboardingViewModel(
private val goToDashboard: () -> Unit,
@@ -20,16 +22,28 @@ class OnboardingViewModel(
platformInfo: PlatformInfo,
private val preferenceRepository: PreferenceRepository,
private val launchUrl: (String) -> Unit,
+ private val batteryOptimization: BatteryOptimization,
) : ViewModel() {
private val events = MutableSharedFlow(extraBufferCapacity = 1)
+ private val stepList = listOfNotNull(
+ Step.WhatIs,
+ Step.HeadsUp,
+ Step.AutomatedTesting(false),
+ Step.CrashReporting,
+ if (platformInfo.needsToRequestNotificationsPermission) {
+ Step.RequestNotificationPermission
+ } else {
+ null
+ },
+ Step.DefaultSettings,
+ )
+
private val _state = MutableStateFlow(
State(
- stepList = if (platformInfo.needsToRequestNotificationsPermission) {
- Step.entries
- } else {
- Step.entries - Step.RequestNotificationPermission
- },
+ step = stepList[0],
+ stepIndex = 0,
+ totalSteps = stepList.size,
),
)
val state = _state.asStateFlow()
@@ -50,14 +64,34 @@ class OnboardingViewModel(
events.filterIsInstance(),
)
.onEach { event ->
+ val enableAutoTest = event == Event.AutoTestYesClicked
+
preferenceRepository.setValueByKey(
SettingsKey.AUTOMATED_TESTING_ENABLED,
- event == Event.AutoTestYesClicked,
+ enableAutoTest,
)
- moveToNextStep()
+
+ if (enableAutoTest &&
+ batteryOptimization.isSupported &&
+ !batteryOptimization.isIgnoring
+ ) {
+ requestIgnoreBatteryOptimization()
+ } else {
+ moveToNextStep()
+ }
}
.launchIn(viewModelScope)
+ events
+ .filterIsInstance()
+ .onEach { requestIgnoreBatteryOptimization() }
+ .launchIn(viewModelScope)
+
+ events
+ .filterIsInstance()
+ .onEach { moveToNextStep() }
+ .launchIn(viewModelScope)
+
merge(
events.filterIsInstance(),
events.filterIsInstance(),
@@ -94,26 +128,48 @@ class OnboardingViewModel(
private suspend fun moveToNextStep() {
val state = _state.value
- if (state.stepIndex < state.stepList.size - 1) {
- _state.value = state.copy(stepIndex = state.stepIndex + 1)
+ if (state.stepIndex < stepList.size - 1) {
+ val newIndex = state.stepIndex + 1
+ _state.value = state.copy(
+ step = stepList[newIndex],
+ stepIndex = newIndex,
+ )
} else {
preferenceRepository.setValueByKey(SettingsKey.FIRST_RUN, false)
goToDashboard()
}
}
+ private fun requestIgnoreBatteryOptimization() {
+ batteryOptimization.requestIgnore { isIgnoring ->
+ if (isIgnoring) {
+ viewModelScope.launch { moveToNextStep() }
+ } else {
+ _state.update {
+ it.copy(step = Step.AutomatedTesting(true))
+ }
+ }
+ }
+ }
+
data class State(
- val stepIndex: Int = 0,
- val stepList: List,
+ val step: Step,
+ val stepIndex: Int,
+ val totalSteps: Int,
)
- enum class Step {
- WhatIs,
- HeadsUp,
- AutomatedTesting,
- CrashReporting,
- RequestNotificationPermission,
- DefaultSettings,
+ sealed interface Step {
+ data object WhatIs : Step
+
+ data object HeadsUp : Step
+
+ data class AutomatedTesting(val showBatteryOptimizationDialog: Boolean) : Step
+
+ data object CrashReporting : Step
+
+ data object RequestNotificationPermission : Step
+
+ data object DefaultSettings : Step
}
sealed interface Event {
@@ -125,6 +181,10 @@ class OnboardingViewModel(
data object AutoTestNoClicked : Event
+ data object BatteryOptimizationOkClicked : Event
+
+ data object BatteryOptimizationCancelClicked : Event
+
data object CrashReportingYesClicked : Event
data object CrashReportingNoClicked : Event
diff --git a/composeApp/src/iosMain/kotlin/org/ooni/probe/SetupDependencies.kt b/composeApp/src/iosMain/kotlin/org/ooni/probe/SetupDependencies.kt
index a1ee59363..48273d9e9 100644
--- a/composeApp/src/iosMain/kotlin/org/ooni/probe/SetupDependencies.kt
+++ b/composeApp/src/iosMain/kotlin/org/ooni/probe/SetupDependencies.kt
@@ -13,6 +13,7 @@ import kotlinx.coroutines.launch
import org.ooni.engine.NetworkTypeFinder
import org.ooni.engine.OonimkallBridge
import org.ooni.probe.background.OperationsManager
+import org.ooni.probe.config.BatteryOptimization
import org.ooni.probe.config.OrganizationConfig
import org.ooni.probe.data.models.AutoRunParameters
import org.ooni.probe.data.models.DeepLink
@@ -77,6 +78,7 @@ class SetupDependencies(
fetchDescriptorUpdate = ::fetchDescriptorUpdate,
localeDirection = ::localeDirection,
shareFile = ::shareFile,
+ batteryOptimization = object : BatteryOptimization {},
)
private val operationsManager = OperationsManager(dependencies)