mirror of
https://github.com/bitfireAT/davx5-ose
synced 2024-07-22 03:01:24 +00:00
Rewrite BatteryOptimizationsFragment
to Compose (#580)
* Migrated to Jetpack Compose Signed-off-by: Arnau Mora <arnyminerz@proton.me> * Added `observeBoolean` Signed-off-by: Arnau Mora <arnyminerz@proton.me> * Simplified settings interaction Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me> * Migrated to Jetpack Compose Signed-off-by: Arnau Mora <arnyminerz@proton.me> * Added `observeBoolean` Signed-off-by: Arnau Mora <arnyminerz@proton.me> * Simplified settings interaction Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me> * Use SafeAndroidUriHandler instead of UiUtils.launchUri * Removed animation for manufacturerWarning Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me> * Removed animation for manufacturerWarning Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me> * Added `getBooleanLive` Signed-off-by: Arnau Mora <arnyminerz@proton.me> * Using `getBooleanLive` Signed-off-by: Arnau Mora <arnyminerz@proton.me> * Moved UI definitions to file scope Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me> * Don't use specific times for waiting in tests * Renamed function Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me> * More exact naming --------- Signed-off-by: Arnau Mora <arnyminerz@proton.me> Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me> Co-authored-by: Ricki Hirner <hirner@bitfire.at>
This commit is contained in:
parent
6b1367d6dc
commit
be309e15b3
|
@ -4,10 +4,17 @@
|
|||
|
||||
package at.bitfire.davdroid.settings
|
||||
|
||||
import at.bitfire.davdroid.TestUtils.getOrAwaitValue
|
||||
import dagger.hilt.android.testing.HiltAndroidRule
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
@ -16,6 +23,12 @@ import javax.inject.Inject
|
|||
@HiltAndroidTest
|
||||
class SettingsManagerTest {
|
||||
|
||||
companion object {
|
||||
/** Use this setting to test SettingsManager methods. Will be removed after every test run. */
|
||||
const val SETTING_TEST = "test"
|
||||
}
|
||||
|
||||
|
||||
@get:Rule
|
||||
val hiltRule = HiltAndroidRule(this)
|
||||
|
||||
|
@ -26,16 +39,58 @@ class SettingsManagerTest {
|
|||
hiltRule.inject()
|
||||
}
|
||||
|
||||
@After
|
||||
fun removeTestSetting() {
|
||||
settingsManager.remove(SETTING_TEST)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testContainsKey_NotExisting() {
|
||||
fun test_containsKey_NotExisting() {
|
||||
assertFalse(settingsManager.containsKey("notExisting"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testContainsKey_Existing() {
|
||||
fun test_containsKey_Existing() {
|
||||
// provided by DefaultsProvider
|
||||
assertEquals(Settings.PROXY_TYPE_SYSTEM, settingsManager.getInt(Settings.PROXY_TYPE))
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun test_getBooleanLive_getValue() = runBlocking(Dispatchers.Main) { // observeForever can't be run in background thread
|
||||
val live = settingsManager.getBooleanLive(SETTING_TEST)
|
||||
assertNull(live.value)
|
||||
|
||||
// set value
|
||||
settingsManager.putBoolean(SETTING_TEST, true)
|
||||
assertTrue(live.getOrAwaitValue()!!)
|
||||
|
||||
// set another value
|
||||
live.value = false
|
||||
assertFalse(live.getOrAwaitValue()!!)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun test_ObserverCalledWhenValueChanges() {
|
||||
val value = CompletableDeferred<Int>()
|
||||
val observer = SettingsManager.OnChangeListener {
|
||||
value.complete(settingsManager.getInt(SETTING_TEST))
|
||||
}
|
||||
|
||||
try {
|
||||
settingsManager.addOnChangeListener(observer)
|
||||
settingsManager.putInt(SETTING_TEST, 123)
|
||||
|
||||
runBlocking {
|
||||
// wait until observer is called
|
||||
assertEquals(123, value.await())
|
||||
}
|
||||
|
||||
} finally {
|
||||
settingsManager.removeOnChangeListener(observer)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -7,7 +7,9 @@ package at.bitfire.davdroid.settings
|
|||
import android.content.Context
|
||||
import android.util.NoSuchPropertyException
|
||||
import androidx.annotation.AnyThread
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import at.bitfire.davdroid.log.Logger
|
||||
import at.bitfire.davdroid.settings.SettingsManager.OnChangeListener
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.EntryPoint
|
||||
|
@ -170,6 +172,38 @@ class SettingsManager internal constructor(
|
|||
fun remove(key: String) = putString(key, null)
|
||||
|
||||
|
||||
/*** LIVE DATA ***/
|
||||
|
||||
/**
|
||||
* Returns a [MutableLiveData] which is backed by the settings with the given key.
|
||||
* An observer must be added to the returned [MutableLiveData] to make it active.
|
||||
*/
|
||||
fun getBooleanLive(key: String) = object : MutableLiveData<Boolean?>() {
|
||||
private val preferenceChangeListener = OnChangeListener { updateValue() }
|
||||
|
||||
private fun updateValue() {
|
||||
value = getBooleanOrNull(key)
|
||||
}
|
||||
|
||||
// setValue is also called from postValue, so no need to override
|
||||
override fun setValue(value: Boolean?) {
|
||||
super.setValue(value)
|
||||
putBoolean(key, value)
|
||||
}
|
||||
|
||||
override fun onActive() {
|
||||
super.onActive()
|
||||
updateValue()
|
||||
addOnChangeListener(preferenceChangeListener)
|
||||
}
|
||||
|
||||
override fun onInactive() {
|
||||
super.onInactive()
|
||||
removeOnChangeListener(preferenceChangeListener)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*** HELPERS ***/
|
||||
|
||||
fun dump(writer: Writer) {
|
||||
|
@ -180,7 +214,7 @@ class SettingsManager internal constructor(
|
|||
}
|
||||
|
||||
|
||||
interface OnChangeListener {
|
||||
fun interface OnChangeListener {
|
||||
/**
|
||||
* Will be called when something has changed in a [SettingsProvider].
|
||||
* May run in worker thread!
|
||||
|
|
|
@ -16,8 +16,38 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
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.CompositionLocalProvider
|
||||
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.ComposeView
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.databinding.ObservableBoolean
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
|
@ -25,11 +55,11 @@ import androidx.lifecycle.MutableLiveData
|
|||
import at.bitfire.davdroid.App
|
||||
import at.bitfire.davdroid.BuildConfig
|
||||
import at.bitfire.davdroid.R
|
||||
import at.bitfire.davdroid.databinding.IntroBatteryOptimizationsBinding
|
||||
import at.bitfire.davdroid.settings.SettingsManager
|
||||
import at.bitfire.davdroid.ui.UiUtils
|
||||
import at.bitfire.davdroid.ui.intro.BatteryOptimizationsFragment.Model.Companion.HINT_AUTOSTART_PERMISSION
|
||||
import at.bitfire.davdroid.ui.intro.BatteryOptimizationsFragment.Model.Companion.HINT_BATTERY_OPTIMIZATIONS
|
||||
import at.bitfire.davdroid.ui.widget.SafeAndroidUriHandler
|
||||
import com.google.accompanist.themeadapter.material.MdcTheme
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
|
@ -48,38 +78,49 @@ class BatteryOptimizationsFragment: Fragment() {
|
|||
|
||||
private val ignoreBatteryOptimizationsResultLauncher =
|
||||
registerForActivityResult(IgnoreBatteryOptimizationsContract) {
|
||||
model.checkWhitelisted()
|
||||
model.checkBatteryOptimizations()
|
||||
}
|
||||
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
val binding = IntroBatteryOptimizationsBinding.inflate(inflater, container, false)
|
||||
binding.lifecycleOwner = viewLifecycleOwner
|
||||
binding.model = model
|
||||
return ComposeView(requireContext()).apply {
|
||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent {
|
||||
MdcTheme {
|
||||
val hintBatteryOptimizations by model.hintBatteryOptimizations.observeAsState()
|
||||
val shouldBeExempted by model.shouldBeExempted.observeAsState(false)
|
||||
val isExempted by model.isExempted.observeAsState(false)
|
||||
LaunchedEffect(shouldBeExempted, isExempted) {
|
||||
if (shouldBeExempted && !isExempted)
|
||||
ignoreBatteryOptimizationsResultLauncher.launch(BuildConfig.APPLICATION_ID)
|
||||
}
|
||||
|
||||
model.shouldBeWhitelisted.observe(viewLifecycleOwner) { shouldBeWhitelisted ->
|
||||
@SuppressLint("BatteryLife")
|
||||
if (shouldBeWhitelisted && !model.isWhitelisted.value!!)
|
||||
ignoreBatteryOptimizationsResultLauncher.launch(BuildConfig.APPLICATION_ID)
|
||||
val hintAutostartPermission by model.hintAutostartPermission.observeAsState()
|
||||
val uriHandler = SafeAndroidUriHandler(LocalContext.current)
|
||||
CompositionLocalProvider(LocalUriHandler provides uriHandler) {
|
||||
BatteryOptimizationsContent(
|
||||
dontShowBattery = hintBatteryOptimizations == false,
|
||||
onChangeDontShowBattery = {
|
||||
model.hintBatteryOptimizations.value = !it
|
||||
},
|
||||
isExempted = isExempted,
|
||||
shouldBeExempted = shouldBeExempted,
|
||||
onChangeShouldBeExempted = model.shouldBeExempted::postValue,
|
||||
dontShowAutostart = hintAutostartPermission == false,
|
||||
onChangeDontShowAutostart = {
|
||||
model.hintAutostartPermission.value = !it
|
||||
},
|
||||
manufacturerWarning = Model.manufacturerWarning
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
binding.batteryText.text = getString(R.string.intro_battery_text, getString(R.string.app_name))
|
||||
|
||||
binding.autostartHeading.text = getString(R.string.intro_autostart_title, WordUtils.capitalize(Build.MANUFACTURER))
|
||||
binding.autostartText.setText(R.string.intro_autostart_text)
|
||||
binding.autostartMoreInfo.setOnClickListener {
|
||||
UiUtils.launchUri(requireActivity(), App.homepageUrl(requireActivity()).buildUpon()
|
||||
.appendPath("faq").appendPath("synchronization-is-not-run-as-expected")
|
||||
.appendQueryParameter("manufacturer", Build.MANUFACTURER.lowercase(Locale.ROOT)).build())
|
||||
}
|
||||
|
||||
binding.infoLeaveUnchecked.text = getString(R.string.intro_leave_unchecked, getString(R.string.app_settings_reset_hints))
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
model.checkWhitelisted()
|
||||
model.checkBatteryOptimizations()
|
||||
}
|
||||
|
||||
|
||||
|
@ -127,41 +168,23 @@ class BatteryOptimizationsFragment: Fragment() {
|
|||
val manufacturerWarning =
|
||||
(evilManufacturers.contains(Build.MANUFACTURER.lowercase(Locale.ROOT)) || BuildConfig.DEBUG)
|
||||
|
||||
fun isWhitelisted(context: Context) =
|
||||
fun isExempted(context: Context) =
|
||||
context.getSystemService<PowerManager>()!!.isIgnoringBatteryOptimizations(BuildConfig.APPLICATION_ID)
|
||||
}
|
||||
|
||||
val shouldBeWhitelisted = MutableLiveData<Boolean>()
|
||||
val isWhitelisted = MutableLiveData<Boolean>()
|
||||
val dontShowBattery = object: ObservableBoolean() {
|
||||
override fun get() = settings.getBooleanOrNull(HINT_BATTERY_OPTIMIZATIONS) == false
|
||||
override fun set(dontShowAgain: Boolean) {
|
||||
if (dontShowAgain)
|
||||
settings.putBoolean(HINT_BATTERY_OPTIMIZATIONS, false)
|
||||
else
|
||||
settings.remove(HINT_BATTERY_OPTIMIZATIONS)
|
||||
notifyChange()
|
||||
}
|
||||
}
|
||||
val shouldBeExempted = MutableLiveData<Boolean>()
|
||||
val isExempted = MutableLiveData<Boolean>()
|
||||
val hintBatteryOptimizations = settings.getBooleanLive(HINT_BATTERY_OPTIMIZATIONS)
|
||||
|
||||
val dontShowAutostart = object: ObservableBoolean() {
|
||||
override fun get() = settings.getBooleanOrNull(HINT_AUTOSTART_PERMISSION) == false
|
||||
override fun set(dontShowAgain: Boolean) {
|
||||
if (dontShowAgain)
|
||||
settings.putBoolean(HINT_AUTOSTART_PERMISSION, false)
|
||||
else
|
||||
settings.remove(HINT_AUTOSTART_PERMISSION)
|
||||
notifyChange()
|
||||
}
|
||||
}
|
||||
val hintAutostartPermission = settings.getBooleanLive(HINT_AUTOSTART_PERMISSION)
|
||||
|
||||
fun checkWhitelisted() {
|
||||
val whitelisted = isWhitelisted(getApplication())
|
||||
isWhitelisted.value = whitelisted
|
||||
shouldBeWhitelisted.value = whitelisted
|
||||
fun checkBatteryOptimizations() {
|
||||
val exempted = isExempted(getApplication())
|
||||
isExempted.value = exempted
|
||||
shouldBeExempted.value = exempted
|
||||
|
||||
// if DAVx5 is whitelisted, always show a reminder as soon as it's not whitelisted anymore
|
||||
if (whitelisted)
|
||||
if (exempted)
|
||||
settings.remove(HINT_BATTERY_OPTIMIZATIONS)
|
||||
}
|
||||
|
||||
|
@ -200,7 +223,7 @@ class BatteryOptimizationsFragment: Fragment() {
|
|||
// 2a. evil manufacturer AND
|
||||
// 2b. "don't show anymore" has not been clicked
|
||||
if (
|
||||
(!Model.isWhitelisted(context) && settingsManager.getBooleanOrNull(HINT_BATTERY_OPTIMIZATIONS) != false) ||
|
||||
(!Model.isExempted(context) && settingsManager.getBooleanOrNull(HINT_BATTERY_OPTIMIZATIONS) != false) ||
|
||||
(Model.manufacturerWarning && settingsManager.getBooleanOrNull(HINT_AUTOSTART_PERMISSION) != false)
|
||||
)
|
||||
100
|
||||
|
@ -210,4 +233,162 @@ class BatteryOptimizationsFragment: Fragment() {
|
|||
override fun create() = BatteryOptimizationsFragment()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, showSystemUi = true)
|
||||
@Composable
|
||||
private fun BatteryOptimizationsContent_Preview() {
|
||||
MdcTheme {
|
||||
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 context = LocalContext.current
|
||||
val uriHandler = LocalUriHandler.current
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Card {
|
||||
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.caption,
|
||||
modifier = Modifier
|
||||
.clickable { onChangeDontShowBattery(!dontShowBattery) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (manufacturerWarning) {
|
||||
Card(
|
||||
modifier = Modifier.padding(top = 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(
|
||||
App.homepageUrl(context)
|
||||
.buildUpon()
|
||||
.appendPath("faq")
|
||||
.appendPath("synchronization-is-not-run-as-expected")
|
||||
.appendQueryParameter(
|
||||
"manufacturer",
|
||||
Build.MANUFACTURER.lowercase(Locale.ROOT)
|
||||
)
|
||||
.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.caption,
|
||||
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)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(90.dp))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,159 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<data>
|
||||
<import type="android.view.View" />
|
||||
<variable name="model" type="at.bitfire.davdroid.ui.intro.BatteryOptimizationsFragment.Model" />
|
||||
</data>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?android:attr/colorBackground"
|
||||
android:paddingBottom="@dimen/appintro2_bottombar_height">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="@dimen/activity_margin">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:contentPadding="@dimen/card_padding">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/batteryHeading"
|
||||
style="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/intro_battery_title"
|
||||
android:textAlignment="viewStart"
|
||||
app:layout_constraintBottom_toTopOf="@id/batteryStatus"
|
||||
app:layout_constraintEnd_toStartOf="@id/batterySwitch"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/batteryStatus"
|
||||
style="@style/TextAppearance.MaterialComponents.Subtitle1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{model.whitelisted ? @string/intro_battery_whitelisted : @string/intro_battery_not_whitelisted}"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
app:layout_constraintBottom_toTopOf="@id/batteryText"
|
||||
app:layout_constraintEnd_toStartOf="@id/batterySwitch"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/batteryHeading" />
|
||||
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
android:id="@+id/batterySwitch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="@={model.shouldBeWhitelisted}"
|
||||
android:clickable="@{!model.whitelisted}"
|
||||
android:enabled="@{!model.dontShowBattery}"
|
||||
app:layout_constraintBottom_toBottomOf="@id/batteryStatus"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/batteryHeading"
|
||||
app:layout_constraintTop_toTopOf="@id/batteryHeading" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/batteryText"
|
||||
style="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/card_margin_title_text"
|
||||
android:text="@string/intro_battery_text"
|
||||
android:textAlignment="viewStart"
|
||||
app:layout_constraintTop_toBottomOf="@id/batteryStatus" />
|
||||
|
||||
<CheckBox
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:checked="@={model.dontShowBattery}"
|
||||
android:enabled="@{!model.isWhitelisted()}"
|
||||
android:text="@string/intro_battery_dont_show"
|
||||
android:textAlignment="viewStart"
|
||||
android:visibility="@{model.isWhitelisted() ? View.GONE : View.VISIBLE}"
|
||||
app:layout_constraintTop_toBottomOf="@id/batteryText" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:visibility="@{model.Companion.manufacturerWarning ? View.VISIBLE : View.GONE}"
|
||||
app:contentPadding="@dimen/card_padding">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/autostartHeading"
|
||||
style="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/intro_autostart_title"
|
||||
android:textAlignment="viewStart"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/autostartText"
|
||||
style="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/card_margin_title_text"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="@string/intro_autostart_text"
|
||||
android:textAlignment="viewStart"
|
||||
app:layout_constraintBottom_toTopOf="@id/autostartMoreInfo"
|
||||
app:layout_constraintTop_toBottomOf="@id/autostartHeading" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/autostartMoreInfo"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/intro_more_info"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/autostartDontShow"
|
||||
app:layout_constraintTop_toBottomOf="@id/autostartText" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/autostartDontShow"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="@={model.dontShowAutostart}"
|
||||
android:text="@string/intro_autostart_dont_show"
|
||||
android:textAlignment="viewStart"
|
||||
app:layout_constraintTop_toBottomOf="@id/autostartMoreInfo" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/infoLeaveUnchecked"
|
||||
style="@style/TextAppearance.MaterialComponents.Body2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/intro_leave_unchecked"
|
||||
android:textAlignment="viewStart" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
</layout>
|
Loading…
Reference in a new issue