Skip to content

Commit

Permalink
Provide three options for donation reminder time (#1256)
Browse files Browse the repository at this point in the history
* Provide three options for donation reminder time

* Move radio buttons to top card; Change strings

* Use padding instead of height for radio elements
  • Loading branch information
sunkup authored and rfc2822 committed Jan 27, 2025
1 parent 4f2d4e3 commit eef85f1
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 40 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
*/

package at.bitfire.davdroid.ui.composable

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.selection.selectable
import androidx.compose.foundation.selection.selectableGroup
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import at.bitfire.davdroid.ui.AppTheme

@Composable
fun RadioButtons(
radioOptions: List<String> = listOf<String>(),
onOptionSelected: (String) -> Unit = {},
textPaddingPerOption: PaddingValues = PaddingValues(10.dp),
modifier: Modifier = Modifier
) {
val (selectedOption, setSelectedOption) = remember { mutableStateOf(radioOptions[0]) }
Column(
// Modifier.selectableGroup() is essential to ensure correct accessibility behavior
modifier = modifier.selectableGroup()
) {
radioOptions.forEach { text ->
Row(
Modifier
.fillMaxWidth()
.selectable(
selected = (text == selectedOption),
onClick = {
setSelectedOption(text)
onOptionSelected(text)
},
role = Role.Companion.RadioButton
)
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(
selected = (text == selectedOption),
onClick = null, // null recommended for accessibility with screen readers
)
Text(
text = text,
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.padding(textPaddingPerOption)
)
}
}
}
}

@Preview
@Composable
private fun RadioButtonsPreview() {
AppTheme {
RadioButtons(
radioOptions = listOf(
"Option 1",
"Option 2 is the longest of all the options, so we can see whether line breaks are not a problem.",
"Option 3")
)
}
}
97 changes: 58 additions & 39 deletions app/src/main/kotlin/at/bitfire/davdroid/ui/intro/OpenSourcePage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,34 @@

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.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Checkbox
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
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.layout.ContentScale
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
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.AppTheme
import at.bitfire.davdroid.ui.composable.CardWithImage
import at.bitfire.davdroid.ui.composable.RadioButtons
import dagger.hilt.android.lifecycle.HiltViewModel
import java.util.logging.Logger
import javax.inject.Inject

class OpenSourcePage @Inject constructor(
Expand All @@ -55,43 +52,46 @@ class OpenSourcePage @Inject constructor(

@Composable
private fun Page(model: Model = viewModel()) {
val dontShow by model.dontShow.collectAsStateWithLifecycle(false)
OpenSourcePage(
dontShow = dontShow,
onChangeDontShow = {
model.setDontShow(it)
}
donationPopupIntervalOptions = model.donationPopupIntervalOptions,
onChangeDontShowFor = model::setDontShowFor
)
}

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

companion object {
const val SETTING_NEXT_DONATION_POPUP = "time_nextDonationPopup"
}

val dontShow = settings.containsKeyFlow(SETTING_NEXT_DONATION_POPUP)
/**
* Possible number of months (30 days) to hide the donation popup for.
*/
val donationPopupIntervalOptions = listOf(1, 3, 9)

fun setDontShow(dontShowAgain: Boolean) {
if (dontShowAgain) {
val nextReminder = System.currentTimeMillis() + 90*86400000L // 90 days (~ 3 months)
settings.putLong(SETTING_NEXT_DONATION_POPUP, nextReminder)
} else
settings.remove(SETTING_NEXT_DONATION_POPUP)
/**
* Set the next time the donation popup should be shown.
* @param dontShowFor Number of months (30 days) to hide the donation popup for.
*/
fun setDontShowFor(dontShowFor: Int) {
logger.info("Setting next donation popup to $dontShowFor months")
val month = 30*86400000L // 30 days (~ 1 month)
val nextReminder = month * dontShowFor + System.currentTimeMillis()
settings.putLong(SETTING_NEXT_DONATION_POPUP, nextReminder)
}

}

}

@Preview
@Composable
fun OpenSourcePage(
dontShow: Boolean = false,
onChangeDontShow: (Boolean) -> Unit = {}
donationPopupIntervalOptions: List<Int>,
onChangeDontShowFor: (Int) -> Unit = {}
) {
val uriHandler = LocalUriHandler.current

Expand All @@ -108,9 +108,11 @@ fun OpenSourcePage(
message = stringResource(
R.string.intro_open_source_text,
stringResource(R.string.app_name)
)
),
modifier = Modifier.padding(vertical = 8.dp)
) {
OutlinedButton(
modifier = Modifier.padding(top = 8.dp, bottom = 16.dp),
onClick = {
uriHandler.openUri(
Constants.HOMEPAGE_URL.buildUpon()
Expand All @@ -121,24 +123,41 @@ fun OpenSourcePage(
)
}
) {
Text(stringResource(R.string.intro_open_source_details))
}
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
Checkbox(
checked = dontShow,
onCheckedChange = onChangeDontShow
)
Text(
text = stringResource(R.string.intro_open_source_dont_show),
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier
.clickable { onChangeDontShow(!dontShow) }
.weight(1f)
stringResource(R.string.intro_open_source_details)
)
}

Text(
text = stringResource(R.string.intro_open_source_dont_show),
style = MaterialTheme.typography.bodyLarge
)
val radioOptions = donationPopupIntervalOptions.associate { numberOfMonths ->
pluralStringResource(
R.plurals.intro_open_source_dont_show_months,
numberOfMonths,
numberOfMonths
) to numberOfMonths
}
RadioButtons(
radioOptions = radioOptions.keys.toList(),
onOptionSelected = { option ->
val months = radioOptions[option] ?: radioOptions.values.first()
onChangeDontShowFor(months)
},
modifier = Modifier.padding(bottom = 12.dp)
)

}
}
}

@Preview
@Composable
fun OpenSourcePagePreview() {
AppTheme {
OpenSourcePage(
donationPopupIntervalOptions = listOf(1, 3, 9)
)
}
}
6 changes: 5 additions & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@
<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>
<string name="intro_open_source_dont_show">Don\'t show in the near future</string>
<string name="intro_open_source_dont_show">Don\'t remind me for</string>
<plurals name="intro_open_source_dont_show_months">
<item quantity="one">%d month</item>
<item quantity="other">%d months</item>
</plurals>
<string name="intro_next">Next</string>

<!-- PermissionsActivity -->
Expand Down

0 comments on commit eef85f1

Please sign in to comment.