From 6a96386fc1d085e2e3e658ba0c8ec1c43f5389ee Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Mon, 25 Aug 2025 20:25:08 +0200 Subject: [PATCH 1/5] Migrate info activity to Nav3 --- app/src/main/AndroidManifest.xml | 4 - .../at/bitfire/icsdroid/ui/InfoActivity.kt | 261 ------------------ .../at/bitfire/icsdroid/ui/nav/Destination.kt | 3 + .../at/bitfire/icsdroid/ui/nav/MainApp.kt | 9 + .../bitfire/icsdroid/ui/screen/InfoScreen.kt | 217 +++++++++++++++ .../icsdroid/ui/screen/SubscriptionsScreen.kt | 6 +- 6 files changed, 231 insertions(+), 269 deletions(-) delete mode 100644 app/src/main/java/at/bitfire/icsdroid/ui/InfoActivity.kt create mode 100644 app/src/main/java/at/bitfire/icsdroid/ui/screen/InfoScreen.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ddcfb415..5bba530a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -119,10 +119,6 @@ android:label="@string/activity_edit_calendar" android:parentActivityName=".MainActivity" android:windowSoftInputMode="stateAlwaysHidden" /> - diff --git a/app/src/main/java/at/bitfire/icsdroid/ui/InfoActivity.kt b/app/src/main/java/at/bitfire/icsdroid/ui/InfoActivity.kt deleted file mode 100644 index 48708682..00000000 --- a/app/src/main/java/at/bitfire/icsdroid/ui/InfoActivity.kt +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - */ - -package at.bitfire.icsdroid.ui - -import android.content.ActivityNotFoundException -import android.content.Intent -import android.net.Uri -import android.os.Bundle -import android.util.Log -import android.widget.Toast -import androidx.annotation.StringRes -import androidx.appcompat.app.AppCompatActivity -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.ArrowBack -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedButton -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.asImageBitmap -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.core.graphics.drawable.toBitmap -import androidx.core.net.toUri -import androidx.core.text.HtmlCompat -import androidx.datastore.preferences.core.edit -import at.bitfire.icsdroid.BuildConfig -import at.bitfire.icsdroid.Constants -import at.bitfire.icsdroid.R -import at.bitfire.icsdroid.Settings.Companion.nextReminder -import at.bitfire.icsdroid.dataStore -import at.bitfire.icsdroid.service.ComposableStartupService -import at.bitfire.icsdroid.service.ComposableStartupService.Companion.FLAG_DONATION_DIALOG -import at.bitfire.icsdroid.ui.partials.ExtendedTopAppBar -import at.bitfire.icsdroid.ui.partials.GenericAlertDialog -import at.bitfire.icsdroid.ui.theme.setContentThemed -import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer -import com.mikepenz.aboutlibraries.ui.compose.rememberLibraries -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.runBlocking -import java.util.ServiceLoader - -@AndroidEntryPoint -class InfoActivity: AppCompatActivity() { - - /** - * Will get updated when the activity is created. - * Stores whether the donate dialog should be shown or not. - */ - private var hasDonateDialogService: Boolean = false - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - // Init and collect all ComposableStartupServices - val compStartupServices = ServiceLoader.load(ComposableStartupService::class.java) - .also { srv -> - hasDonateDialogService = srv.any { it.hasFlag(FLAG_DONATION_DIALOG) } - } - - setContentThemed { - compStartupServices.forEach { service -> - val show: Boolean by service.shouldShow() - if (show) service.Content() - } - - MainLayout() - } - } - - private fun showWebSite() { - launchUri("https://icsx5.bitfire.at/?pk_campaign=icsx5-app&pk_kwd=info-activity".toUri()) - } - - private fun showMastodon() { - launchUri("https://fosstodon.org/@davx5app".toUri()) - } - - private fun launchUri(uri: Uri) { - val intent = Intent(Intent.ACTION_VIEW, uri) - try { - startActivity(intent) - } catch (_: ActivityNotFoundException) { - Toast.makeText(this, getString(R.string.no_browser), Toast.LENGTH_LONG).show() - Log.w(Constants.TAG, "No browser to view $uri") - } - } - - - @OptIn(ExperimentalMaterial3Api::class) - @Composable - @Preview - fun MainLayout() { - Scaffold( - topBar = { - ExtendedTopAppBar( - navigationIcon = { - IconButton({ onNavigateUp() }) { - Icon( - imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = null - ) - } - }, - title = { - Text( - stringResource(R.string.app_name) - ) - }, - actions = { - IconButton({ showWebSite() }) { - Icon( - painter = painterResource(R.drawable.ic_public), - contentDescription = stringResource(R.string.app_info_web_site) - ) - } - IconButton({ showMastodon() }) { - Icon( - painter = painterResource(R.drawable.mastodon_white), - contentDescription = stringResource(R.string.app_info_mastodon) - ) - } - } - ) - } - ) { contentPadding -> - val libraries by rememberLibraries { - resources.openRawResource(R.raw.aboutlibraries).bufferedReader().use { input -> - input.readText() - } - } - - Column(Modifier.padding(contentPadding)) { - Header() - License() - LibrariesContainer( - libraries = libraries - ) - } - } - } - - @Composable - fun Header() { - Column( - modifier = Modifier.fillMaxWidth(), - horizontalAlignment = Alignment.CenterHorizontally - ) { - val context = LocalContext.current - - Image( - bitmap = context.applicationInfo - .loadIcon(context.packageManager) - .toBitmap() - .asImageBitmap(), - contentDescription = null, - modifier = Modifier - .padding(vertical = 12.dp) - .size(72.dp) - ) - Text( - text = stringResource(R.string.app_name), - style = MaterialTheme.typography.headlineSmall, - color = MaterialTheme.colorScheme.onBackground - ) - Text( - text = stringResource( - R.string.app_info_version, - BuildConfig.VERSION_NAME, - BuildConfig.FLAVOR - ), - style = MaterialTheme.typography.titleMedium, - color = MaterialTheme.colorScheme.onBackground, - fontWeight = FontWeight.Normal - ) - Text( - text = stringResource(R.string.app_info_copyright), - style = MaterialTheme.typography.titleMedium, - color = MaterialTheme.colorScheme.onBackground, - fontWeight = FontWeight.Normal, - textAlign = TextAlign.Center, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 8.dp) - ) - } - } - - @Composable - fun License() { - val showLicenseDialog = rememberSaveable { mutableStateOf(false) } - if (showLicenseDialog.value) - TextDialog(R.string.app_info_gplv3_note, showLicenseDialog) - - val showDonateDialog = rememberSaveable { mutableStateOf(false) } - if (showDonateDialog.value) - TextDialog(R.string.donate_message, showDonateDialog) - - Row { - OutlinedButton( - onClick = { showLicenseDialog.value = true }, - modifier = Modifier - .weight(1f) - .padding(horizontal = 4.dp) - ) { - Text(stringResource(R.string.app_info_gplv3)) - } - OutlinedButton( - onClick = { - if (hasDonateDialogService) runBlocking { - // If there's a donate dialog service, show the dialog - dataStore.edit { it[nextReminder] = 0 } - } else { - // If there's no service, show the donate dialog directly - showDonateDialog.value = true - } - }, - modifier = Modifier - .weight(1f) - .padding(horizontal = 4.dp) - ) { - Text(stringResource(R.string.app_info_donate)) - } - } - } - - @Composable - fun TextDialog(@StringRes text: Int, state: MutableState) { - GenericAlertDialog( - content = { Text(HtmlCompat.fromHtml( - getString(text).replace("\n", "
"), - HtmlCompat.FROM_HTML_MODE_COMPACT).toString()) }, - confirmButton = stringResource(R.string.edit_calendar_dismiss) to { - state.value = false - }, - ) { state.value = false } - } - -} \ No newline at end of file diff --git a/app/src/main/java/at/bitfire/icsdroid/ui/nav/Destination.kt b/app/src/main/java/at/bitfire/icsdroid/ui/nav/Destination.kt index 04c330d3..8be1d7d4 100644 --- a/app/src/main/java/at/bitfire/icsdroid/ui/nav/Destination.kt +++ b/app/src/main/java/at/bitfire/icsdroid/ui/nav/Destination.kt @@ -13,6 +13,9 @@ sealed interface Destination : NavKey { @Serializable object SubscriptionList : Destination + @Serializable + object Info : Destination + @Serializable data class AddSubscription( val title: String? = null, diff --git a/app/src/main/java/at/bitfire/icsdroid/ui/nav/MainApp.kt b/app/src/main/java/at/bitfire/icsdroid/ui/nav/MainApp.kt index 16440f79..e6fd88e3 100644 --- a/app/src/main/java/at/bitfire/icsdroid/ui/nav/MainApp.kt +++ b/app/src/main/java/at/bitfire/icsdroid/ui/nav/MainApp.kt @@ -26,8 +26,10 @@ import at.bitfire.icsdroid.MainActivity.Companion.EXTRA_ERROR_MESSAGE import at.bitfire.icsdroid.MainActivity.Companion.EXTRA_REQUEST_CALENDAR_PERMISSION import at.bitfire.icsdroid.MainActivity.Companion.EXTRA_THROWABLE import at.bitfire.icsdroid.service.ComposableStartupService +import at.bitfire.icsdroid.service.ComposableStartupService.Companion.FLAG_DONATION_DIALOG import at.bitfire.icsdroid.ui.partials.AlertDialog import at.bitfire.icsdroid.ui.screen.AddSubscriptionScreen +import at.bitfire.icsdroid.ui.screen.InfoScreen import at.bitfire.icsdroid.ui.screen.SubscriptionsScreen import java.util.ServiceLoader @@ -94,9 +96,16 @@ fun MainApp( entry(Destination.SubscriptionList) { SubscriptionsScreen( requestPermissions, + onAboutRequested = { backStack.add(Destination.Info) }, onAddRequested = { backStack.add(Destination.AddSubscription()) } ) } + entry(Destination.Info) { + InfoScreen( + compStartupServices, + onNavigateUp = ::goBack + ) + } entry { destination -> var url: String? = null LaunchedEffect(intentExtras) { diff --git a/app/src/main/java/at/bitfire/icsdroid/ui/screen/InfoScreen.kt b/app/src/main/java/at/bitfire/icsdroid/ui/screen/InfoScreen.kt new file mode 100644 index 00000000..b5ab1139 --- /dev/null +++ b/app/src/main/java/at/bitfire/icsdroid/ui/screen/InfoScreen.kt @@ -0,0 +1,217 @@ +package at.bitfire.icsdroid.ui.screen + +import androidx.annotation.StringRes +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalResources +import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.core.graphics.drawable.toBitmap +import androidx.core.text.HtmlCompat +import androidx.datastore.preferences.core.edit +import at.bitfire.icsdroid.BuildConfig +import at.bitfire.icsdroid.R +import at.bitfire.icsdroid.Settings.Companion.nextReminder +import at.bitfire.icsdroid.dataStore +import at.bitfire.icsdroid.service.ComposableStartupService +import at.bitfire.icsdroid.service.ComposableStartupService.Companion.FLAG_DONATION_DIALOG +import at.bitfire.icsdroid.ui.partials.ExtendedTopAppBar +import at.bitfire.icsdroid.ui.partials.GenericAlertDialog +import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer +import com.mikepenz.aboutlibraries.ui.compose.rememberLibraries +import kotlinx.coroutines.runBlocking +import java.util.ServiceLoader + +@Composable +@OptIn(ExperimentalMaterial3Api::class) +fun InfoScreen( + compStartupServices: ServiceLoader, + onNavigateUp: () -> Unit +) { + val resources = LocalResources.current + val uriHandler = LocalUriHandler.current + + val hasDonateDialogService = compStartupServices.any { it.hasFlag(FLAG_DONATION_DIALOG) } + + Scaffold( + topBar = { + ExtendedTopAppBar( + navigationIcon = { + IconButton(onNavigateUp) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = null + ) + } + }, + title = { + Text( + stringResource(R.string.app_name) + ) + }, + actions = { + IconButton( + onClick = { + uriHandler.openUri("https://icsx5.bitfire.at/?pk_campaign=icsx5-app&pk_kwd=info-activity") + } + ) { + Icon( + painter = painterResource(R.drawable.ic_public), + contentDescription = stringResource(R.string.app_info_web_site) + ) + } + IconButton( + onClick = { + uriHandler.openUri("https://fosstodon.org/@davx5app") + } + ) { + Icon( + painter = painterResource(R.drawable.mastodon_white), + contentDescription = stringResource(R.string.app_info_mastodon) + ) + } + } + ) + } + ) { contentPadding -> + val libraries by rememberLibraries { + resources.openRawResource(R.raw.aboutlibraries).bufferedReader().use { input -> + input.readText() + } + } + + Column(Modifier.padding(contentPadding)) { + Header() + License(hasDonateDialogService) + LibrariesContainer( + libraries = libraries + ) + } + } +} + +@Composable +private fun Header() { + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + val context = LocalContext.current + + Image( + bitmap = context.applicationInfo + .loadIcon(context.packageManager) + .toBitmap() + .asImageBitmap(), + contentDescription = null, + modifier = Modifier + .padding(vertical = 12.dp) + .size(72.dp) + ) + Text( + text = stringResource(R.string.app_name), + style = MaterialTheme.typography.headlineSmall, + color = MaterialTheme.colorScheme.onBackground + ) + Text( + text = stringResource( + R.string.app_info_version, + BuildConfig.VERSION_NAME, + BuildConfig.FLAVOR + ), + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.onBackground, + fontWeight = FontWeight.Normal + ) + Text( + text = stringResource(R.string.app_info_copyright), + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.onBackground, + fontWeight = FontWeight.Normal, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 8.dp) + ) + } +} + +@Composable +private fun License( + hasDonateDialogService: Boolean +) { + val context = LocalContext.current + + val showLicenseDialog = rememberSaveable { mutableStateOf(false) } + if (showLicenseDialog.value) + TextDialog(R.string.app_info_gplv3_note, showLicenseDialog) + + val showDonateDialog = rememberSaveable { mutableStateOf(false) } + if (showDonateDialog.value) + TextDialog(R.string.donate_message, showDonateDialog) + + Row { + OutlinedButton( + onClick = { showLicenseDialog.value = true }, + modifier = Modifier + .weight(1f) + .padding(horizontal = 4.dp) + ) { + Text(stringResource(R.string.app_info_gplv3)) + } + OutlinedButton( + onClick = { + if (hasDonateDialogService) runBlocking { + // If there's a donate dialog service, show the dialog + context.dataStore.edit { it[nextReminder] = 0 } + } else { + // If there's no service, show the donate dialog directly + showDonateDialog.value = true + } + }, + modifier = Modifier + .weight(1f) + .padding(horizontal = 4.dp) + ) { + Text(stringResource(R.string.app_info_donate)) + } + } +} + +@Composable +private fun TextDialog(@StringRes text: Int, state: MutableState) { + GenericAlertDialog( + content = { Text(HtmlCompat.fromHtml( + stringResource(text).replace("\n", "
"), + HtmlCompat.FROM_HTML_MODE_COMPACT).toString()) }, + confirmButton = stringResource(R.string.edit_calendar_dismiss) to { + state.value = false + }, + ) { state.value = false } +} diff --git a/app/src/main/java/at/bitfire/icsdroid/ui/screen/SubscriptionsScreen.kt b/app/src/main/java/at/bitfire/icsdroid/ui/screen/SubscriptionsScreen.kt index 02d035d6..cc9ad25b 100644 --- a/app/src/main/java/at/bitfire/icsdroid/ui/screen/SubscriptionsScreen.kt +++ b/app/src/main/java/at/bitfire/icsdroid/ui/screen/SubscriptionsScreen.kt @@ -57,7 +57,6 @@ import at.bitfire.icsdroid.SyncWorker import at.bitfire.icsdroid.UriUtils import at.bitfire.icsdroid.db.entity.Subscription import at.bitfire.icsdroid.model.SubscriptionsModel -import at.bitfire.icsdroid.ui.InfoActivity import at.bitfire.icsdroid.ui.partials.ActionCard import at.bitfire.icsdroid.ui.partials.CalendarListItem import at.bitfire.icsdroid.ui.partials.ExtendedTopAppBar @@ -68,6 +67,7 @@ import at.bitfire.icsdroid.ui.views.EditSubscriptionActivity @Composable fun SubscriptionsScreen( requestPermissions: Boolean, + onAboutRequested: () -> Unit, onAddRequested: () -> Unit, model: SubscriptionsModel = hiltViewModel() ) { @@ -96,9 +96,7 @@ fun SubscriptionsScreen( SubscriptionsScreen( model = model, - onAboutRequested = { - activity?.startActivity(Intent(context, InfoActivity::class.java)) - }, + onAboutRequested = onAboutRequested, onAddRequested = onAddRequested, onRequestCalendarPermissions = requestCalendarPermissions, onRequestNotificationPermission = requestNotificationPermission, From 613ebf55cf385b76b08529a97aaf30da304810d3 Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Tue, 26 Aug 2025 15:41:44 +0200 Subject: [PATCH 2/5] Optimize imports Signed-off-by: Arnau Mora --- app/src/main/java/at/bitfire/icsdroid/ui/nav/MainApp.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/at/bitfire/icsdroid/ui/nav/MainApp.kt b/app/src/main/java/at/bitfire/icsdroid/ui/nav/MainApp.kt index e6fd88e3..b0fad9a7 100644 --- a/app/src/main/java/at/bitfire/icsdroid/ui/nav/MainApp.kt +++ b/app/src/main/java/at/bitfire/icsdroid/ui/nav/MainApp.kt @@ -26,7 +26,6 @@ import at.bitfire.icsdroid.MainActivity.Companion.EXTRA_ERROR_MESSAGE import at.bitfire.icsdroid.MainActivity.Companion.EXTRA_REQUEST_CALENDAR_PERMISSION import at.bitfire.icsdroid.MainActivity.Companion.EXTRA_THROWABLE import at.bitfire.icsdroid.service.ComposableStartupService -import at.bitfire.icsdroid.service.ComposableStartupService.Companion.FLAG_DONATION_DIALOG import at.bitfire.icsdroid.ui.partials.AlertDialog import at.bitfire.icsdroid.ui.screen.AddSubscriptionScreen import at.bitfire.icsdroid.ui.screen.InfoScreen From d56033828cef25a772bdc8516704476d0925c7ca Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Wed, 27 Aug 2025 15:49:24 +0200 Subject: [PATCH 3/5] Improve HTML display Signed-off-by: Arnau Mora --- .../java/at/bitfire/icsdroid/ui/screen/InfoScreen.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/at/bitfire/icsdroid/ui/screen/InfoScreen.kt b/app/src/main/java/at/bitfire/icsdroid/ui/screen/InfoScreen.kt index b5ab1139..79cace30 100644 --- a/app/src/main/java/at/bitfire/icsdroid/ui/screen/InfoScreen.kt +++ b/app/src/main/java/at/bitfire/icsdroid/ui/screen/InfoScreen.kt @@ -29,11 +29,12 @@ import androidx.compose.ui.platform.LocalResources import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.fromHtml import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.core.graphics.drawable.toBitmap -import androidx.core.text.HtmlCompat import androidx.datastore.preferences.core.edit import at.bitfire.icsdroid.BuildConfig import at.bitfire.icsdroid.R @@ -207,9 +208,10 @@ private fun License( @Composable private fun TextDialog(@StringRes text: Int, state: MutableState) { GenericAlertDialog( - content = { Text(HtmlCompat.fromHtml( - stringResource(text).replace("\n", "
"), - HtmlCompat.FROM_HTML_MODE_COMPACT).toString()) }, + content = { + val htmlString = stringResource(text).replace("\n", "
") + Text(AnnotatedString.fromHtml(htmlString)) + }, confirmButton = stringResource(R.string.edit_calendar_dismiss) to { state.value = false }, From 188fc4bc89947e650aea3c0deb225874f289bd6d Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Wed, 27 Aug 2025 15:53:15 +0200 Subject: [PATCH 4/5] Fixed link styles Signed-off-by: Arnau Mora --- .../at/bitfire/icsdroid/ui/screen/InfoScreen.kt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/at/bitfire/icsdroid/ui/screen/InfoScreen.kt b/app/src/main/java/at/bitfire/icsdroid/ui/screen/InfoScreen.kt index 79cace30..31950a56 100644 --- a/app/src/main/java/at/bitfire/icsdroid/ui/screen/InfoScreen.kt +++ b/app/src/main/java/at/bitfire/icsdroid/ui/screen/InfoScreen.kt @@ -12,6 +12,7 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Scaffold @@ -30,9 +31,11 @@ import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.TextLinkStyles import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.fromHtml import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.dp import androidx.core.graphics.drawable.toBitmap import androidx.datastore.preferences.core.edit @@ -210,7 +213,16 @@ private fun TextDialog(@StringRes text: Int, state: MutableState) { GenericAlertDialog( content = { val htmlString = stringResource(text).replace("\n", "
") - Text(AnnotatedString.fromHtml(htmlString)) + val linkStyle = LocalTextStyle.current.copy( + color = MaterialTheme.colorScheme.primary, + textDecoration = TextDecoration.Underline + ).toSpanStyle() + Text( + text = AnnotatedString.fromHtml( + htmlString, + linkStyles = TextLinkStyles(linkStyle) + ) + ) }, confirmButton = stringResource(R.string.edit_calendar_dismiss) to { state.value = false From 1ba0b1a91ab4a5100c7949ce6b354941f707c6db Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Mon, 1 Sep 2025 10:35:09 +0200 Subject: [PATCH 5/5] Rename argument Signed-off-by: Arnau Mora --- app/src/main/java/at/bitfire/icsdroid/ui/nav/MainApp.kt | 2 +- app/src/main/java/at/bitfire/icsdroid/ui/screen/InfoScreen.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/at/bitfire/icsdroid/ui/nav/MainApp.kt b/app/src/main/java/at/bitfire/icsdroid/ui/nav/MainApp.kt index b0fad9a7..2ad02ade 100644 --- a/app/src/main/java/at/bitfire/icsdroid/ui/nav/MainApp.kt +++ b/app/src/main/java/at/bitfire/icsdroid/ui/nav/MainApp.kt @@ -102,7 +102,7 @@ fun MainApp( entry(Destination.Info) { InfoScreen( compStartupServices, - onNavigateUp = ::goBack + onBackRequested = ::goBack ) } entry { destination -> diff --git a/app/src/main/java/at/bitfire/icsdroid/ui/screen/InfoScreen.kt b/app/src/main/java/at/bitfire/icsdroid/ui/screen/InfoScreen.kt index 31950a56..ff05e9fe 100644 --- a/app/src/main/java/at/bitfire/icsdroid/ui/screen/InfoScreen.kt +++ b/app/src/main/java/at/bitfire/icsdroid/ui/screen/InfoScreen.kt @@ -56,7 +56,7 @@ import java.util.ServiceLoader @OptIn(ExperimentalMaterial3Api::class) fun InfoScreen( compStartupServices: ServiceLoader, - onNavigateUp: () -> Unit + onBackRequested: () -> Unit ) { val resources = LocalResources.current val uriHandler = LocalUriHandler.current @@ -67,7 +67,7 @@ fun InfoScreen( topBar = { ExtendedTopAppBar( navigationIcon = { - IconButton(onNavigateUp) { + IconButton(onBackRequested) { Icon( imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null