Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit af084fb5 authored by Ricki Hirner's avatar Ricki Hirner Committed by GitHub
Browse files

Add "Backups reminder" intro screen (#1937)

* Add BackupsPage to intro UI

* Add BackupsPage UI

- Implement BackupsPage composable with UI elements
- Add strings for backups reminder and acceptance

* Add to "Reset hints"

* Add another paragraph

* Update backup screen hints

- Update backups reminder text
- Remove redundant versioning note
- Update acceptance message
parent 47685e66
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import at.bitfire.davdroid.repository.PreferenceRepository
import at.bitfire.davdroid.settings.Settings
import at.bitfire.davdroid.settings.SettingsManager
import at.bitfire.davdroid.sync.TasksAppManager
import at.bitfire.davdroid.ui.intro.BackupsPage
import at.bitfire.davdroid.ui.intro.BatteryOptimizationsPageModel
import at.bitfire.davdroid.ui.intro.OpenSourcePage
import at.bitfire.davdroid.util.PermissionUtils
@@ -33,6 +34,7 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import javax.inject.Inject

@HiltViewModel
@@ -98,11 +100,12 @@ class AppSettingsModel @Inject constructor(
        UiUtils.updateTheme(context)
    }

    fun resetHints() {
    fun resetHints() = runBlocking(ioDispatcher) {
        settings.remove(BackupsPage.Model.SETTING_BACKUPS_ACCEPTED)
        settings.remove(BatteryOptimizationsPageModel.HINT_BATTERY_OPTIMIZATIONS)
        settings.remove(BatteryOptimizationsPageModel.HINT_AUTOSTART_PERMISSION)
        settings.remove(TasksModel.HINT_OPENTASKS_NOT_INSTALLED)
        settings.remove(OpenSourcePage.Model.SETTING_NEXT_DONATION_POPUP)
        settings.remove(TasksModel.HINT_OPENTASKS_NOT_INSTALLED)
    }


+135 −0
Original line number Diff line number Diff line
/*
 * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
 */

package at.bitfire.davdroid.ui.intro

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Backup
import androidx.compose.material3.Checkbox
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import at.bitfire.davdroid.R
import at.bitfire.davdroid.settings.SettingsManager
import at.bitfire.davdroid.ui.AppTheme
import at.bitfire.davdroid.ui.composable.CardWithImage
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject

class BackupsPage @Inject constructor(
    val settingsManager: SettingsManager
): IntroPage() {

    override fun getShowPolicy(): ShowPolicy =
        if (Model.backupsAccepted(settingsManager))
            ShowPolicy.DONT_SHOW
        else
            ShowPolicy.SHOW_ALWAYS

    @Composable
    override fun ComposePage() {
        val model = hiltViewModel<Model>()
        val accepted by model.backupsAcceptedFlow.collectAsStateWithLifecycle(false)
        BackupsPage(
            accepted = accepted,
            updateAccepted = model::setBackupsAccepted
        )
    }


    @HiltViewModel
    class Model @Inject constructor(
        private val settings: SettingsManager
    ): ViewModel() {

        val backupsAcceptedFlow = settings.getBooleanFlow(SETTING_BACKUPS_ACCEPTED, false)

        fun setBackupsAccepted(accepted: Boolean) {
            settings.putBoolean(SETTING_BACKUPS_ACCEPTED, accepted)
        }

        companion object {

            /** boolean setting (default: false) */
            const val SETTING_BACKUPS_ACCEPTED = "intro_backups_accepted"

            fun backupsAccepted(settingsManager: SettingsManager): Boolean =
                settingsManager.getBooleanOrNull(SETTING_BACKUPS_ACCEPTED) ?: false

        }

    }

}

@Composable
fun BackupsPage(
    accepted: Boolean,
    updateAccepted: (Boolean) -> Unit
) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .verticalScroll(rememberScrollState())
            .padding(8.dp)
    ) {
        CardWithImage(
            title = stringResource(R.string.intro_backups_title),
            icon = Icons.Outlined.Backup,
            modifier = Modifier.padding(vertical = 8.dp)
        ) {
            Text(
                text = stringResource(R.string.intro_backups_important),
                modifier = Modifier.padding(bottom = 8.dp)
            )
            Text(
                text = stringResource(R.string.intro_backups_something_wrong, stringResource(R.string.app_name))
            )

            Row(
                verticalAlignment = Alignment.CenterVertically,
                modifier = Modifier.padding(top = 8.dp, bottom = 16.dp)
            ) {
                Checkbox(
                    checked = accepted,
                    onCheckedChange = updateAccepted
                )
                Text(
                    text = stringResource(R.string.intro_backups_accept),
                    style = MaterialTheme.typography.bodyLarge,
                    modifier = Modifier
                        .clickable { updateAccepted(!accepted) }
                        .padding(start = 8.dp)
                )
            }
        }
    }
}

@Preview
@Composable
fun BackupsPagePreview() {
    AppTheme {
        BackupsPage(
            accepted = true,
            updateAccepted = {}
        )
    }
}
 No newline at end of file
+4 −0
Original line number Diff line number Diff line
@@ -58,6 +58,10 @@
    <string name="intro_tasks_tasks_org_info"><![CDATA[Some features <a href="https://www.davx5.com/faq/tasks/advanced-task-features">are not supported</a>.]]></string>
    <string name="intro_tasks_no_app_store">No app store available</string>
    <string name="intro_tasks_dont_show">I don\'t need tasks support.*</string>
    <string name="intro_backups_title">Backups reminder</string>
    <string name="intro_backups_important">It\'s important to back up your data (including contacts and calendars) regularly to prevent potential data loss.</string>
    <string name="intro_backups_something_wrong">Something can always go wrong, whether on the server, within %s, or in other apps, which for instance might accidentally delete all calendar entries. (The deletions would then be synchronized to the server.)</string>
    <string name="intro_backups_accept">I already have a working backup strategy or I don\'t need one.</string>
    <string name="intro_open_source_title">Open-source software</string>
    <string name="intro_open_source_text">We\'re happy that you use %s, which is open-source software. Development, maintenance and support are hard work. Please consider contributing (there are many ways) or a donation. It would be highly appreciated!</string>
    <string name="intro_open_source_details">How to contribute/donate</string>
+2 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ package at.bitfire.davdroid.ui.intro
import javax.inject.Inject

class OseIntroPageFactory @Inject constructor(
    backupsPage: BackupsPage,
    batteryOptimizationsPage: BatteryOptimizationsPage,
    openSourcePage: OpenSourcePage,
    permissionsIntroPage: PermissionsIntroPage,
@@ -18,6 +19,7 @@ class OseIntroPageFactory @Inject constructor(
        tasksIntroPage,
        permissionsIntroPage,
        batteryOptimizationsPage,
        backupsPage,
        openSourcePage
    )