Add contact information to the About screen

This commit is contained in:
Pierre-Yves Nicolas
2025-09-13 08:26:52 +02:00
parent 60fb714356
commit fa41c57388
4 changed files with 106 additions and 45 deletions

View File

@@ -14,8 +14,10 @@
*/ */
package org.fairscan.app.view package org.fairscan.app.view
import android.content.Intent
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@@ -25,10 +27,13 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Email
import androidx.compose.material.icons.filled.Language
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
@@ -48,10 +53,15 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalResources import androidx.compose.ui.platform.LocalResources
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import org.fairscan.app.BuildConfig import org.fairscan.app.BuildConfig
import org.fairscan.app.R import org.fairscan.app.R
import org.fairscan.app.ui.theme.MyScanTheme import org.fairscan.app.ui.theme.MyScanTheme
@@ -83,71 +93,116 @@ fun AboutContent(
showLicenseDialog: MutableState<Boolean>, showLicenseDialog: MutableState<Boolean>,
onViewLibraries: () -> Unit, onViewLibraries: () -> Unit,
) { ) {
val context = LocalContext.current
Column( Column(
modifier = modifier modifier = modifier
.fillMaxSize() .fillMaxSize()
.padding(16.dp) .padding(16.dp)
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(20.dp),
horizontalAlignment = Alignment.CenterHorizontally
) { ) {
Text( Text(stringResource(R.string.app_name), style = MaterialTheme.typography.headlineMedium)
stringResource(R.string.app_name),
style = MaterialTheme.typography.headlineSmall
)
Spacer(Modifier.height(8.dp))
Text( Text(
stringResource(R.string.app_tagline), stringResource(R.string.app_tagline),
style = MaterialTheme.typography.bodyMedium textAlign = TextAlign.Center
) )
Spacer(Modifier.height(16.dp))
HorizontalDivider() HorizontalDivider()
Spacer(Modifier.height(16.dp))
Text( Section(title = stringResource(R.string.version)) {
stringResource(R.string.version), Text(BuildConfig.VERSION_NAME)
style = MaterialTheme.typography.titleMedium }
)
Text(BuildConfig.VERSION_NAME)
Spacer(Modifier.height(16.dp)) Section(title = stringResource(R.string.developer)) {
Text("Pierre-Yves Nicolas")
}
Text( Section(title = stringResource(R.string.contact)) {
stringResource(R.string.license), val emailAddress = "contact@fairscan.org"
style = MaterialTheme.typography.titleMedium ContactLink(
) icon = Icons.Default.Email,
Text( text = emailAddress,
stringResource(R.string.licensed_under), onClick = {
style = MaterialTheme.typography.bodyMedium val intent = Intent(Intent.ACTION_SENDTO).apply {
) data = "mailto:$emailAddress".toUri()
Text( }
text = stringResource(R.string.view_the_full_license), context.startActivity(intent)
style = MaterialTheme.typography.bodyMedium, }
modifier = Modifier.clickable { showLicenseDialog.value = true }, )
color = MaterialTheme.colorScheme.primary val websiteUrl = "https://fairscan.org"
) ContactLink(
icon = Icons.Default.Language,
text = websiteUrl,
onClick = {
val intent = Intent(Intent.ACTION_VIEW, websiteUrl.toUri())
context.startActivity(intent)
}
)
}
Spacer(Modifier.height(16.dp)) Section(title = stringResource(R.string.license)) {
Text(
stringResource(R.string.licensed_under),
Text( )
stringResource(R.string.libraries), Text(
style = MaterialTheme.typography.titleMedium text = stringResource(R.string.view_the_full_license),
) modifier = Modifier.clickable { showLicenseDialog.value = true },
Text( color = MaterialTheme.colorScheme.primary
stringResource(R.string.libraries_intro) + )
"\n• CameraX\n• Jetpack Compose\n• LiteRT\n• OpenCV\n• PDFBox", }
style = MaterialTheme.typography.bodyMedium)
Text(
text = stringResource(R.string.view_full_list),
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.clickable(onClick = onViewLibraries),
color = MaterialTheme.colorScheme.primary
)
Section(title = stringResource(R.string.libraries)) {
Text(
stringResource(R.string.libraries_intro) +
"\n• CameraX\n• Jetpack Compose\n• LiteRT\n• OpenCV\n• PDFBox",
)
Text(
text = stringResource(R.string.view_full_list),
modifier = Modifier.clickable(onClick = onViewLibraries),
color = MaterialTheme.colorScheme.primary
)
}
Spacer(Modifier.height(32.dp)) Spacer(Modifier.height(32.dp))
} }
} }
@Composable
private fun Section(title: String, content: @Composable () -> Unit) {
Column(
verticalArrangement = Arrangement.spacedBy(4.dp),
modifier = Modifier.fillMaxWidth()
) {
Text(title, style = MaterialTheme.typography.titleMedium)
content()
}
}
@Composable
private fun ContactLink(icon: ImageVector, text: String, onClick: () -> Unit) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.clickable { onClick() }
.padding(vertical = 4.dp)
) {
Icon(icon, contentDescription = null, tint = MaterialTheme.colorScheme.primary)
Spacer(Modifier.width(8.dp))
Text(
text,
style = MaterialTheme.typography.bodyLarge.copy(
color = MaterialTheme.colorScheme.primary,
textDecoration = TextDecoration.Underline
)
)
}
}
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun LicenseBottomSheet( fun LicenseBottomSheet(

View File

@@ -7,9 +7,11 @@
<string name="camera_permission_rationale">Die App benötigt Zugriff auf die Kamera, um Dokumente zu scannen. Aufgenommene Bilder werden nur auf diesem Gerät gespeichert und beim Schließen des aktuellen Dokuments gelöscht.</string> <string name="camera_permission_rationale">Die App benötigt Zugriff auf die Kamera, um Dokumente zu scannen. Aufgenommene Bilder werden nur auf diesem Gerät gespeichert und beim Schließen des aktuellen Dokuments gelöscht.</string>
<string name="cancel">Abbrechen</string> <string name="cancel">Abbrechen</string>
<string name="clear_text">Text löschen</string> <string name="clear_text">Text löschen</string>
<string name="contact">Kontakt</string>
<string name="creating_pdf">PDF wird erstellt…</string> <string name="creating_pdf">PDF wird erstellt…</string>
<string name="delete_page">Seite löschen</string> <string name="delete_page">Seite löschen</string>
<string name="delete_page_warning">Möchten Sie diese Seite löschen?</string> <string name="delete_page_warning">Möchten Sie diese Seite löschen?</string>
<string name="developer">Entwickler</string>
<string name="discard_scan">Löschen</string> <string name="discard_scan">Löschen</string>
<string name="end_scan">Scan beenden</string> <string name="end_scan">Scan beenden</string>
<string name="error">Fehler: %1$s</string> <string name="error">Fehler: %1$s</string>

View File

@@ -6,10 +6,12 @@
<string name="camera_permission_denied">L\'autorisation d\'accès à la caméra a été refusée</string> <string name="camera_permission_denied">L\'autorisation d\'accès à la caméra a été refusée</string>
<string name="camera_permission_rationale">Lapplication a besoin daccéder à lappareil photo pour scanner des documents. Les images capturées sont enregistrées uniquement sur cet appareil et seront supprimées lorsque vous fermerez le document en cours.</string> <string name="camera_permission_rationale">Lapplication a besoin daccéder à lappareil photo pour scanner des documents. Les images capturées sont enregistrées uniquement sur cet appareil et seront supprimées lorsque vous fermerez le document en cours.</string>
<string name="cancel">Annuler</string> <string name="cancel">Annuler</string>
<string name="contact">Contact</string>
<string name="clear_text">Effacer le text</string> <string name="clear_text">Effacer le text</string>
<string name="creating_pdf">Création du PDF…</string> <string name="creating_pdf">Création du PDF…</string>
<string name="delete_page">Supprimer la page</string> <string name="delete_page">Supprimer la page</string>
<string name="delete_page_warning">Voulez-vous supprimer cette page ?</string> <string name="delete_page_warning">Voulez-vous supprimer cette page ?</string>
<string name="developer">Développeur</string>
<string name="discard_scan">Supprimer le scan</string> <string name="discard_scan">Supprimer le scan</string>
<string name="end_scan">Terminer le scan</string> <string name="end_scan">Terminer le scan</string>
<string name="error">Erreur : %1$s</string> <string name="error">Erreur : %1$s</string>

View File

@@ -8,9 +8,11 @@
<string name="camera_permission_rationale">The app requires camera access to scan documents. Captured images are stored only on this device and will be deleted when you close the current document.</string> <string name="camera_permission_rationale">The app requires camera access to scan documents. Captured images are stored only on this device and will be deleted when you close the current document.</string>
<string name="cancel">Cancel</string> <string name="cancel">Cancel</string>
<string name="clear_text">Clear text</string> <string name="clear_text">Clear text</string>
<string name="contact">Contact</string>
<string name="creating_pdf">Creating PDF…</string> <string name="creating_pdf">Creating PDF…</string>
<string name="delete_page">Delete page</string> <string name="delete_page">Delete page</string>
<string name="delete_page_warning">Do you want to delete this page?</string> <string name="delete_page_warning">Do you want to delete this page?</string>
<string name="developer">Developer</string>
<string name="discard_scan">Discard scan</string> <string name="discard_scan">Discard scan</string>
<string name="end_scan">End scan</string> <string name="end_scan">End scan</string>
<string name="error">Error: %1$s</string> <string name="error">Error: %1$s</string>