fix(fdroid): hide Play-only ToS gate and Privacy/Child-Safety links

F-Droid distributes Amethyst as MIT-licensed free software with no
acceptable-use terms layered on top — only the Play Store build needs
a ToS-acceptance checkbox at login/signup and links to the published
Child Safety Standards.

Gates added behind `BuildConfig.FLAVOR == "play"`:

- Settings → "About & Legal" section (Privacy Policy + Child Safety
  Standards) is now Play-only; F-Droid settings no longer link to
  PRIVACY.md.
- AcceptTerms checkbox in LoginScreen and SignUpScreen is now Play-only.
- LoginViewModel.load() / clear() and SignUpViewModel pre-accept
  `acceptedTerms` on F-Droid so the login/signup button isn't disabled.

The Play build is unchanged: first-time login still requires checking
the ToS box, and the About & Legal section still surfaces the published
Child Safety Standards link required by Google Play.
This commit is contained in:
Claude
2026-05-24 16:24:57 +00:00
parent c3e03308f8
commit f058662349
5 changed files with 47 additions and 38 deletions
@@ -46,6 +46,7 @@ import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.BuildConfig
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
@@ -263,30 +264,32 @@ fun AllSettingsScreen(
)
}
SettingsSection(R.string.about_legal) {
SettingsItem(
title = R.string.privacy_policy,
icon = MaterialSymbols.Lock,
onClick = {
runCatching {
uriHandler.openUri(
"https://github.com/vitorpamplona/amethyst/blob/main/PRIVACY.md",
)
}
},
)
SettingsDivider()
SettingsItem(
title = R.string.child_safety_standards,
icon = MaterialSymbols.Shield,
onClick = {
runCatching {
uriHandler.openUri(
"https://github.com/vitorpamplona/amethyst/blob/main/PRIVACY.md#child-safety-standards",
)
}
},
)
if (BuildConfig.FLAVOR == "play") {
SettingsSection(R.string.about_legal) {
SettingsItem(
title = R.string.privacy_policy,
icon = MaterialSymbols.Lock,
onClick = {
runCatching {
uriHandler.openUri(
"https://github.com/vitorpamplona/amethyst/blob/main/PRIVACY.md",
)
}
},
)
SettingsDivider()
SettingsItem(
title = R.string.child_safety_standards,
icon = MaterialSymbols.Shield,
onClick = {
runCatching {
uriHandler.openUri(
"https://github.com/vitorpamplona/amethyst/blob/main/PRIVACY.md#child-safety-standards",
)
}
},
)
}
}
SettingsSection(R.string.danger_zone, isDanger = true) {
@@ -66,6 +66,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.BuildConfig
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.hashtags.Amethyst
import com.vitorpamplona.amethyst.commons.hashtags.CustomHashTagIcons
@@ -196,7 +197,7 @@ fun LoginPage(
)
}
if (loginViewModel.isFirstLogin) {
if (loginViewModel.isFirstLogin && BuildConfig.FLAVOR == "play") {
AcceptTerms(
checked = loginViewModel.acceptedTerms,
onCheckedChange = loginViewModel::updateAcceptedTerms,
@@ -27,6 +27,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.text.input.TextFieldValue
import androidx.lifecycle.ViewModel
import com.vitorpamplona.amethyst.BuildConfig
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.screen.AccountSessionManager
import com.vitorpamplona.amethyst.ui.tor.TorSettingsFlow
@@ -68,7 +69,7 @@ class LoginViewModel : ViewModel() {
) {
clear()
this.isFirstLogin = isFirstLogin
acceptedTerms = !isFirstLogin
acceptedTerms = !isFirstLogin || BuildConfig.FLAVOR != "play"
if (newAccountKey != null) {
key = TextFieldValue(newAccountKey)
}
@@ -79,7 +80,7 @@ class LoginViewModel : ViewModel() {
password = TextFieldValue("")
errorManager.clearErrors()
acceptedTerms = false
acceptedTerms = BuildConfig.FLAVOR != "play"
processingLogin = false
isTemporary = false
offerTemporaryLogin = false
@@ -50,6 +50,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.BuildConfig
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.hashtags.Amethyst
import com.vitorpamplona.amethyst.commons.hashtags.CustomHashTagIcons
@@ -187,17 +188,19 @@ fun SignUpPage(
},
)
AcceptTerms(
checked = signUpViewModel.acceptedTerms,
onCheckedChange = signUpViewModel::updateAcceptedTerms,
)
if (signUpViewModel.termsAcceptanceIsRequiredError) {
Text(
text = stringRes(R.string.acceptance_of_terms_is_required),
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.bodySmall,
if (BuildConfig.FLAVOR == "play") {
AcceptTerms(
checked = signUpViewModel.acceptedTerms,
onCheckedChange = signUpViewModel::updateAcceptedTerms,
)
if (signUpViewModel.termsAcceptanceIsRequiredError) {
Text(
text = stringRes(R.string.acceptance_of_terms_is_required),
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.bodySmall,
)
}
}
Spacer(modifier = Modifier.height(Size10dp))
@@ -26,6 +26,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.text.input.TextFieldValue
import androidx.lifecycle.ViewModel
import com.vitorpamplona.amethyst.BuildConfig
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.screen.AccountSessionManager
import com.vitorpamplona.amethyst.ui.screen.loggedOff.login.LoginErrorManager
@@ -40,7 +41,7 @@ class SignUpViewModel : ViewModel() {
var displayName by mutableStateOf(TextFieldValue(""))
var acceptedTerms by mutableStateOf(false)
var acceptedTerms by mutableStateOf(BuildConfig.FLAVOR != "play")
var termsAcceptanceIsRequiredError by mutableStateOf(false)
fun init(accountSessionManager: AccountSessionManager) {