Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite BatteryOptimizationsPage to M3 #747

Merged
merged 20 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from 6 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
Original file line number Diff line number Diff line change
Expand Up @@ -14,45 +14,18 @@ import android.os.Build
import android.os.PowerManager
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContract
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Card
import androidx.compose.material.Checkbox
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedButton
import androidx.compose.material.Switch
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.core.content.getSystemService
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel
import at.bitfire.davdroid.BuildConfig
import at.bitfire.davdroid.Constants
import at.bitfire.davdroid.Constants.withStatParams
import at.bitfire.davdroid.R
import at.bitfire.davdroid.settings.SettingsManager
import at.bitfire.davdroid.ui.M2Theme
import at.bitfire.davdroid.ui.intro.BatteryOptimizationsPage.Model.Companion.HINT_AUTOSTART_PERMISSION
import at.bitfire.davdroid.ui.intro.BatteryOptimizationsPage.Model.Companion.HINT_BATTERY_OPTIMIZATIONS
import at.bitfire.davdroid.util.PermissionUtils
Expand All @@ -62,10 +35,9 @@ import dagger.hilt.InstallIn
import dagger.hilt.android.EntryPointAccessors
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.launch
import org.apache.commons.text.WordUtils
import java.util.Locale
import javax.inject.Inject
import kotlinx.coroutines.launch

class BatteryOptimizationsPage: IntroPage {

Expand Down Expand Up @@ -93,38 +65,34 @@ class BatteryOptimizationsPage: IntroPage {

@Composable
override fun ComposePage() {
IntroPage_FromModel()
BatteryOptimizationsPageContent()
}

@Composable
private fun IntroPage_FromModel(
private fun BatteryOptimizationsPageContent(
ArnyminerZ marked this conversation as resolved.
Show resolved Hide resolved
model: Model = viewModel()
) {
val ignoreBatteryOptimizationsResultLauncher = rememberLauncherForActivityResult(IgnoreBatteryOptimizationsContract) {
model.checkBatteryOptimizations()
}

val hintBatteryOptimizations by model.hintBatteryOptimizations.collectAsStateWithLifecycle(false)
val shouldBeExempted by model.shouldBeExempted.observeAsState(false)
val isExempted by model.isExempted.observeAsState(false)
val shouldBeExempted = model.shouldBeExempted
val isExempted = model.isExempted
LaunchedEffect(shouldBeExempted, isExempted) {
if (shouldBeExempted && !isExempted)
ignoreBatteryOptimizationsResultLauncher.launch(BuildConfig.APPLICATION_ID)
}

val hintAutostartPermission by model.hintAutostartPermission.collectAsStateWithLifecycle(false)
BatteryOptimizationsContent(
BatteryOptimizationsPageContent(
dontShowBattery = hintBatteryOptimizations == false,
onChangeDontShowBattery = {
model.settings.putBoolean(HINT_BATTERY_OPTIMIZATIONS, !it)
},
onChangeDontShowBattery = model::updateHintBatteryOptimizations,
isExempted = isExempted,
shouldBeExempted = shouldBeExempted,
onChangeShouldBeExempted = model.shouldBeExempted::postValue,
onChangeShouldBeExempted = model::updateShouldBeExempted,
dontShowAutostart = hintAutostartPermission == false,
onChangeDontShowAutostart = {
model.settings.putBoolean(HINT_AUTOSTART_PERMISSION, !it)
},
onChangeDontShowAutostart = model::updateHintAutostartPermission,
manufacturerWarning = Model.manufacturerWarning
)
}
Expand All @@ -133,7 +101,7 @@ class BatteryOptimizationsPage: IntroPage {
@HiltViewModel
class Model @Inject constructor(
ArnyminerZ marked this conversation as resolved.
Show resolved Hide resolved
val context: Application,
val settings: SettingsManager
private val settings: SettingsManager
): ViewModel() {

companion object {
Expand Down Expand Up @@ -178,8 +146,11 @@ class BatteryOptimizationsPage: IntroPage {
context.getSystemService<PowerManager>()!!.isIgnoringBatteryOptimizations(BuildConfig.APPLICATION_ID)
}

val shouldBeExempted = MutableLiveData<Boolean>()
val isExempted = MutableLiveData<Boolean>()
var shouldBeExempted by mutableStateOf(true)
private set
var isExempted by mutableStateOf(false)
ArnyminerZ marked this conversation as resolved.
Show resolved Hide resolved
private set

val hintBatteryOptimizations = settings.getBooleanFlow(HINT_BATTERY_OPTIMIZATIONS)

val hintAutostartPermission = settings.getBooleanFlow(HINT_AUTOSTART_PERMISSION)
Expand All @@ -194,14 +165,26 @@ class BatteryOptimizationsPage: IntroPage {

fun checkBatteryOptimizations() {
val exempted = isExempted(context)
isExempted.value = exempted
shouldBeExempted.value = exempted
isExempted = exempted
shouldBeExempted = exempted

// if DAVx5 is whitelisted, always show a reminder as soon as it's not whitelisted anymore
if (exempted)
settings.remove(HINT_BATTERY_OPTIMIZATIONS)
}

fun updateShouldBeExempted(value: Boolean) {
shouldBeExempted = value
}

fun updateHintBatteryOptimizations(value: Boolean) {
settings.putBoolean(HINT_BATTERY_OPTIMIZATIONS, value)
}

fun updateHintAutostartPermission(value: Boolean) {
settings.putBoolean(HINT_AUTOSTART_PERMISSION, value)
}

}


Expand All @@ -220,165 +203,3 @@ class BatteryOptimizationsPage: IntroPage {
}

}

@Preview(showBackground = true, showSystemUi = true)
@Composable
private fun BatteryOptimizationsContent_Preview() {
M2Theme {
BatteryOptimizationsContent(
dontShowBattery = true,
onChangeDontShowBattery = {},
isExempted = false,
shouldBeExempted = true,
onChangeShouldBeExempted = {},
dontShowAutostart = false,
onChangeDontShowAutostart = {},
manufacturerWarning = true
)
}
}

@Composable
private fun BatteryOptimizationsContent(
dontShowBattery: Boolean,
onChangeDontShowBattery: (Boolean) -> Unit,
isExempted: Boolean,
shouldBeExempted: Boolean,
onChangeShouldBeExempted: (Boolean) -> Unit,
dontShowAutostart: Boolean,
onChangeDontShowAutostart: (Boolean) -> Unit,
manufacturerWarning: Boolean
) {
val uriHandler = LocalUriHandler.current

Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
) {
Card(
modifier = Modifier.padding(8.dp)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = stringResource(R.string.intro_battery_title),
style = MaterialTheme.typography.h6,
modifier = Modifier.weight(1f)
)
Switch(
checked = shouldBeExempted,
onCheckedChange = {
// Only accept click events if not whitelisted
if (!isExempted) {
onChangeShouldBeExempted(it)
}
},
enabled = !dontShowBattery
)
}
Text(
text = stringResource(
R.string.intro_battery_text,
stringResource(R.string.app_name)
),
style = MaterialTheme.typography.body1,
modifier = Modifier.padding(top = 12.dp)
)
AnimatedVisibility(visible = !isExempted) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Checkbox(
checked = dontShowBattery,
onCheckedChange = onChangeDontShowBattery,
enabled = !isExempted
)
Text(
text = stringResource(R.string.intro_battery_dont_show),
style = MaterialTheme.typography.body2,
modifier = Modifier
.clickable { onChangeDontShowBattery(!dontShowBattery) }
)
}
}
}
}
if (manufacturerWarning) {
Card(
modifier = Modifier
.padding(8.dp)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Text(
text = stringResource(
R.string.intro_autostart_title,
WordUtils.capitalize(Build.MANUFACTURER)
),
style = MaterialTheme.typography.h6,
modifier = Modifier.fillMaxWidth()
)
Text(
text = stringResource(R.string.intro_autostart_text),
style = MaterialTheme.typography.body1,
modifier = Modifier.padding(top = 12.dp)
)
OutlinedButton(
onClick = {
uriHandler.openUri(
Constants.HOMEPAGE_URL.buildUpon()
.appendPath(Constants.HOMEPAGE_PATH_FAQ)
.appendPath(Constants.HOMEPAGE_PATH_FAQ_SYNC_NOT_RUN)
.appendQueryParameter(
"manufacturer",
Build.MANUFACTURER.lowercase(Locale.ROOT)
)
.withStatParams("BatteryOptimizationsPage")
.build().toString()
)
}
) {
Text(stringResource(R.string.intro_more_info))
}
Row(
verticalAlignment = Alignment.CenterVertically
) {
Checkbox(
checked = dontShowAutostart,
onCheckedChange = onChangeDontShowAutostart
)
Text(
text = stringResource(R.string.intro_autostart_dont_show),
style = MaterialTheme.typography.body2,
modifier = Modifier
.clickable { onChangeDontShowAutostart(!dontShowAutostart) }
)
}
}
}
}
Text(
text = stringResource(
R.string.intro_leave_unchecked,
stringResource(R.string.app_settings_reset_hints)
),
style = MaterialTheme.typography.body2,
modifier = Modifier
.padding(top = 8.dp)
.padding(horizontal = 16.dp)
)
Spacer(modifier = Modifier.height(90.dp))
}

}
Loading
Loading