Add M3 theme and apply to AboutActivity (#731)

* Rename AppTheme to M2Theme, add M3 theme

* Rewrite AboutActivity to M3

* Apply M3 theme; minor optimizations

* Use M3 version of AboutLibraries

* Use material3 instead of material3-android dependency

* Use reversed theme
This commit is contained in:
Ricki Hirner 2024-04-19 11:05:39 +02:00 committed by GitHub
parent a1d85c4c9b
commit 34b88c3ad8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 222 additions and 71 deletions

View file

@ -165,6 +165,7 @@ dependencies {
implementation(libs.compose.accompanist.permissions) implementation(libs.compose.accompanist.permissions)
implementation(platform(libs.compose.bom)) implementation(platform(libs.compose.bom))
implementation(libs.compose.material) implementation(libs.compose.material)
implementation(libs.compose.material3)
implementation(libs.compose.materialIconsExtended) implementation(libs.compose.materialIconsExtended)
implementation(libs.compose.runtime.livedata) implementation(libs.compose.runtime.livedata)
debugImplementation(libs.compose.ui.tooling) debugImplementation(libs.compose.ui.tooling)

View file

@ -23,17 +23,18 @@ import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Tab
import androidx.compose.material.TabRow
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Home import androidx.compose.material.icons.filled.Home
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
@ -55,7 +56,7 @@ import at.bitfire.davdroid.R
import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.ui.composable.PixelBoxes import at.bitfire.davdroid.ui.composable.PixelBoxes
import com.mikepenz.aboutlibraries.Libs import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.ui.compose.LibrariesContainer import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer
import com.mikepenz.aboutlibraries.util.withJson import com.mikepenz.aboutlibraries.util.withJson
import dagger.BindsOptionalOf import dagger.BindsOptionalOf
import dagger.Module import dagger.Module
@ -87,7 +88,7 @@ class AboutActivity: AppCompatActivity() {
lateinit var licenseInfoProvider: Optional<AppLicenseInfoProvider> lateinit var licenseInfoProvider: Optional<AppLicenseInfoProvider>
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -266,7 +267,7 @@ fun AboutApp(licenseInfoProvider: AboutActivity.AppLicenseInfoProvider? = null)
) )
Text( Text(
stringResource(R.string.app_name), stringResource(R.string.app_name),
style = MaterialTheme.typography.h5, style = MaterialTheme.typography.headlineMedium,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -275,7 +276,7 @@ fun AboutApp(licenseInfoProvider: AboutActivity.AppLicenseInfoProvider? = null)
Text( Text(
stringResource(R.string.about_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE), stringResource(R.string.about_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE),
style = MaterialTheme.typography.body1, style = MaterialTheme.typography.bodyLarge,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
@ -283,14 +284,14 @@ fun AboutApp(licenseInfoProvider: AboutActivity.AppLicenseInfoProvider? = null)
val dateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT) val dateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
Text( Text(
stringResource(R.string.about_build_date, dateFormatter.format(buildTime)), stringResource(R.string.about_build_date, dateFormatter.format(buildTime)),
style = MaterialTheme.typography.body1, style = MaterialTheme.typography.bodyLarge,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
Text( Text(
stringResource(R.string.about_copyright), stringResource(R.string.about_copyright),
style = MaterialTheme.typography.body1, style = MaterialTheme.typography.bodyLarge,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -299,17 +300,17 @@ fun AboutApp(licenseInfoProvider: AboutActivity.AppLicenseInfoProvider? = null)
Text( Text(
stringResource(R.string.about_license_info_no_warranty), stringResource(R.string.about_license_info_no_warranty),
style = MaterialTheme.typography.body1, style = MaterialTheme.typography.bodyLarge,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(vertical = 8.dp) .padding(top = 8.dp)
) )
PixelBoxes( PixelBoxes(
arrayOf(Color(0xFFFCF434), Color.White, Color(0xFF9C59D1), Color.Black), arrayOf(Color(0xFFFCF434), Color.White, Color(0xFF9C59D1), Color.Black),
modifier = Modifier modifier = Modifier
.align(Alignment.CenterHorizontally) .align(Alignment.CenterHorizontally)
.padding(8.dp) .padding(16.dp)
) )
licenseInfoProvider?.LicenseInfo() licenseInfoProvider?.LicenseInfo()
@ -337,14 +338,14 @@ fun TranslatorsGallery(
items(translations) { translation -> items(translations) { translation ->
Text( Text(
translation.language, translation.language,
style = MaterialTheme.typography.h6, style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.padding(vertical = 4.dp) modifier = Modifier.padding(vertical = 4.dp)
) )
Text( Text(
translation.translators translation.translators
.sortedWith { a, b -> collator.compare(a, b) } .sortedWith { a, b -> collator.compare(a, b) }
.joinToString(" · "), .joinToString(" · "),
style = MaterialTheme.typography.body1, style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.padding(bottom = 16.dp) modifier = Modifier.padding(bottom = 16.dp)
) )
} }

View file

@ -141,7 +141,7 @@ class AccountsActivity: AppCompatActivity() {
val accounts by model.accountInfos.observeAsState() val accounts by model.accountInfos.observeAsState()
AppTheme { M2Theme {
Scaffold( Scaffold(
scaffoldState = scaffoldState, scaffoldState = scaffoldState,
drawerContent = { drawerContent = {

View file

@ -96,7 +96,7 @@ class AppSettingsActivity: AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContent { setContent {
AppTheme { M2Theme {
AppSettings() AppSettings()
} }
} }

View file

@ -5,7 +5,7 @@
package at.bitfire.davdroid.ui package at.bitfire.davdroid.ui
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@ -13,15 +13,31 @@ import androidx.compose.ui.platform.LocalUriHandler
import at.bitfire.davdroid.ui.composable.SafeAndroidUriHandler import at.bitfire.davdroid.ui.composable.SafeAndroidUriHandler
@Composable @Composable
fun AppTheme(content: @Composable () -> Unit) { fun M2Theme(content: @Composable () -> Unit) {
val colors = if (isSystemInDarkTheme()) val colors = if (isSystemInDarkTheme())
ThemeColors.dark M2Colors.dark
else else
ThemeColors.light M2Colors.light
CompositionLocalProvider(LocalUriHandler provides SafeAndroidUriHandler(LocalContext.current)) { CompositionLocalProvider(LocalUriHandler provides SafeAndroidUriHandler(LocalContext.current)) {
MaterialTheme(colors = colors) { androidx.compose.material.MaterialTheme(colors = colors) {
content() content()
} }
} }
}
@Composable
fun AppTheme(
useDarkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val colors = if (!useDarkTheme)
M3ColorScheme.LightColors
else
M3ColorScheme.DarkColors
MaterialTheme(
colorScheme = colors,
content = content
)
} }

View file

@ -179,7 +179,7 @@ class DebugInfoActivity : AppCompatActivity() {
} }
setContent { setContent {
AppTheme { M2Theme {
val debugInfo by model.debugInfo.observeAsState() val debugInfo by model.debugInfo.observeAsState()
val zipProgress by model.zipProgress.observeAsState(false) val zipProgress by model.zipProgress.observeAsState(false)

View file

@ -57,7 +57,7 @@ class PermissionsActivity: AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContent { setContent {
AppTheme { M2Theme {
Scaffold( Scaffold(
topBar = { topBar = {
BasicTopAppBar( BasicTopAppBar(
@ -143,7 +143,7 @@ fun PermissionsContent(
@Preview(showBackground = true, showSystemUi = true) @Preview(showBackground = true, showSystemUi = true)
@Composable @Composable
fun PermissionsCard_Preview() { fun PermissionsCard_Preview() {
AppTheme { M2Theme {
PermissionsCardContent( PermissionsCardContent(
keepPermissions = true, keepPermissions = true,
onKeepPermissionsRequested = {}, onKeepPermissionsRequested = {},

View file

@ -70,7 +70,7 @@ class TasksActivity: AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContent { setContent {
AppTheme { M2Theme {
Scaffold( Scaffold(
topBar = { topBar = {
BasicTopAppBar( BasicTopAppBar(
@ -202,7 +202,7 @@ fun TasksCard(
@Preview(showBackground = true, showSystemUi = true) @Preview(showBackground = true, showSystemUi = true)
@Composable @Composable
fun TasksCardContent_Preview() { fun TasksCardContent_Preview() {
AppTheme { M2Theme {
TasksCardContent( TasksCardContent(
jtxSelected = true, jtxSelected = true,
jtxInstalled = true, jtxInstalled = true,

View file

@ -80,7 +80,7 @@ import at.bitfire.davdroid.db.Collection
import at.bitfire.davdroid.servicedetection.RefreshCollectionsWorker import at.bitfire.davdroid.servicedetection.RefreshCollectionsWorker
import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.davdroid.settings.AccountSettings
import at.bitfire.davdroid.syncadapter.OneTimeSyncWorker import at.bitfire.davdroid.syncadapter.OneTimeSyncWorker
import at.bitfire.davdroid.ui.AppTheme import at.bitfire.davdroid.ui.M2Theme
import at.bitfire.davdroid.ui.PermissionsActivity import at.bitfire.davdroid.ui.PermissionsActivity
import at.bitfire.davdroid.ui.composable.ActionCard import at.bitfire.davdroid.ui.composable.ActionCard
import at.bitfire.davdroid.util.TaskUtils import at.bitfire.davdroid.util.TaskUtils
@ -133,7 +133,7 @@ class AccountActivity : AppCompatActivity() {
} }
setContent { setContent {
AppTheme { M2Theme {
val cardDavSvc by model.cardDavSvc.observeAsState() val cardDavSvc by model.cardDavSvc.observeAsState()
val canCreateAddressBook by model.canCreateAddressBook.observeAsState(false) val canCreateAddressBook by model.canCreateAddressBook.observeAsState(false)
val cardDavRefreshing by model.cardDavRefreshing.observeAsState(false) val cardDavRefreshing by model.cardDavRefreshing.observeAsState(false)

View file

@ -67,7 +67,7 @@ import at.bitfire.davdroid.settings.AccountSettings
import at.bitfire.davdroid.settings.SettingsManager import at.bitfire.davdroid.settings.SettingsManager
import at.bitfire.davdroid.syncadapter.OneTimeSyncWorker import at.bitfire.davdroid.syncadapter.OneTimeSyncWorker
import at.bitfire.davdroid.syncadapter.Syncer import at.bitfire.davdroid.syncadapter.Syncer
import at.bitfire.davdroid.ui.AppTheme import at.bitfire.davdroid.ui.M2Theme
import at.bitfire.davdroid.ui.composable.ActionCard import at.bitfire.davdroid.ui.composable.ActionCard
import at.bitfire.davdroid.ui.composable.EditTextInputDialog import at.bitfire.davdroid.ui.composable.EditTextInputDialog
import at.bitfire.davdroid.ui.composable.MultipleChoiceInputDialog import at.bitfire.davdroid.ui.composable.MultipleChoiceInputDialog
@ -117,7 +117,7 @@ class AccountSettingsActivity: AppCompatActivity() {
title = account.name title = account.name
setContent { setContent {
AppTheme { M2Theme {
val uriHandler = LocalUriHandler.current val uriHandler = LocalUriHandler.current
val snackbarHostState = remember { SnackbarHostState() } val snackbarHostState = remember { SnackbarHostState() }

View file

@ -46,7 +46,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import at.bitfire.davdroid.R import at.bitfire.davdroid.R
import at.bitfire.davdroid.db.HomeSet import at.bitfire.davdroid.db.HomeSet
import at.bitfire.davdroid.ui.AppTheme import at.bitfire.davdroid.ui.M2Theme
import at.bitfire.davdroid.ui.widget.ExceptionInfoDialog import at.bitfire.davdroid.ui.widget.ExceptionInfoDialog
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
@ -78,7 +78,7 @@ class CreateAddressBookActivity: AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContent { setContent {
AppTheme { M2Theme {
var displayName by remember { mutableStateOf("") } var displayName by remember { mutableStateOf("") }
var description by remember { mutableStateOf("") } var description by remember { mutableStateOf("") }
var homeSet by remember { mutableStateOf<HomeSet?>(null) } var homeSet by remember { mutableStateOf<HomeSet?>(null) }

View file

@ -62,7 +62,7 @@ import androidx.lifecycle.viewModelScope
import at.bitfire.davdroid.Constants import at.bitfire.davdroid.Constants
import at.bitfire.davdroid.R import at.bitfire.davdroid.R
import at.bitfire.davdroid.db.HomeSet import at.bitfire.davdroid.db.HomeSet
import at.bitfire.davdroid.ui.AppTheme import at.bitfire.davdroid.ui.M2Theme
import at.bitfire.davdroid.ui.composable.MultipleChoiceInputDialog import at.bitfire.davdroid.ui.composable.MultipleChoiceInputDialog
import at.bitfire.davdroid.ui.widget.CalendarColorPickerDialog import at.bitfire.davdroid.ui.widget.CalendarColorPickerDialog
import at.bitfire.davdroid.ui.widget.ExceptionInfoDialog import at.bitfire.davdroid.ui.widget.ExceptionInfoDialog
@ -104,7 +104,7 @@ class CreateCalendarActivity: AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContent { setContent {
AppTheme { M2Theme {
var displayName by remember { mutableStateOf("") } var displayName by remember { mutableStateOf("") }
var color by remember { mutableIntStateOf(Constants.DAVDROID_GREEN_RGBA) } var color by remember { mutableIntStateOf(Constants.DAVDROID_GREEN_RGBA) }
var description by remember { mutableStateOf("") } var description by remember { mutableStateOf("") }

View file

@ -52,7 +52,7 @@ import androidx.lifecycle.viewModelScope
import at.bitfire.davdroid.Constants import at.bitfire.davdroid.Constants
import at.bitfire.davdroid.Constants.withStatParams import at.bitfire.davdroid.Constants.withStatParams
import at.bitfire.davdroid.R import at.bitfire.davdroid.R
import at.bitfire.davdroid.ui.AppTheme import at.bitfire.davdroid.ui.M2Theme
import at.bitfire.davdroid.ui.composable.PermissionSwitchRow import at.bitfire.davdroid.ui.composable.PermissionSwitchRow
import at.bitfire.davdroid.util.PermissionUtils import at.bitfire.davdroid.util.PermissionUtils
import at.bitfire.davdroid.util.broadcastReceiverFlow import at.bitfire.davdroid.util.broadcastReceiverFlow
@ -78,7 +78,7 @@ class WifiPermissionsActivity: AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContent { setContent {
AppTheme { M2Theme {
Scaffold( Scaffold(
topBar = { topBar = {
TopAppBar( TopAppBar(

View file

@ -52,7 +52,7 @@ import at.bitfire.davdroid.Constants
import at.bitfire.davdroid.Constants.withStatParams import at.bitfire.davdroid.Constants.withStatParams
import at.bitfire.davdroid.R import at.bitfire.davdroid.R
import at.bitfire.davdroid.settings.SettingsManager import at.bitfire.davdroid.settings.SettingsManager
import at.bitfire.davdroid.ui.AppTheme import at.bitfire.davdroid.ui.M2Theme
import at.bitfire.davdroid.ui.intro.BatteryOptimizationsPage.Model.Companion.HINT_AUTOSTART_PERMISSION import at.bitfire.davdroid.ui.intro.BatteryOptimizationsPage.Model.Companion.HINT_AUTOSTART_PERMISSION
import at.bitfire.davdroid.ui.intro.BatteryOptimizationsPage.Model.Companion.HINT_BATTERY_OPTIMIZATIONS import at.bitfire.davdroid.ui.intro.BatteryOptimizationsPage.Model.Companion.HINT_BATTERY_OPTIMIZATIONS
import at.bitfire.davdroid.util.PermissionUtils import at.bitfire.davdroid.util.PermissionUtils
@ -224,7 +224,7 @@ class BatteryOptimizationsPage: IntroPage {
@Preview(showBackground = true, showSystemUi = true) @Preview(showBackground = true, showSystemUi = true)
@Composable @Composable
private fun BatteryOptimizationsContent_Preview() { private fun BatteryOptimizationsContent_Preview() {
AppTheme { M2Theme {
BatteryOptimizationsContent( BatteryOptimizationsContent(
dontShowBattery = true, dontShowBattery = true,
onChangeDontShowBattery = {}, onChangeDontShowBattery = {},

View file

@ -26,8 +26,8 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.ui.AppTheme import at.bitfire.davdroid.ui.M2Theme
import at.bitfire.davdroid.ui.ThemeColors import at.bitfire.davdroid.ui.M2Colors
import com.github.appintro.AppIntro2 import com.github.appintro.AppIntro2
import dagger.hilt.EntryPoint import dagger.hilt.EntryPoint
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
@ -73,7 +73,7 @@ class IntroActivity : AppIntro2() {
}) })
} }
setBarColor(ThemeColors.primaryDark.toArgb()) setBarColor(M2Colors.primaryDark.toArgb())
isSkipButtonEnabled = false isSkipButtonEnabled = false
onBackPressedDispatcher.addCallback(this) { onBackPressedDispatcher.addCallback(this) {
@ -112,7 +112,7 @@ class IntroActivity : AppIntro2() {
ComposeView(requireActivity()).apply { ComposeView(requireActivity()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent { setContent {
AppTheme { M2Theme {
Box(Modifier.padding(bottom = dimensionResource(com.github.appintro.R.dimen.appintro2_bottombar_height))) { Box(Modifier.padding(bottom = dimensionResource(com.github.appintro.R.dimen.appintro2_bottombar_height))) {
page.ComposePage() page.ComposePage()
} }

View file

@ -31,7 +31,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import at.bitfire.davdroid.R import at.bitfire.davdroid.R
import at.bitfire.davdroid.ui.ThemeColors.primaryDark import at.bitfire.davdroid.ui.M2Colors.primaryDark
class WelcomePage: IntroPage { class WelcomePage: IntroPage {

View file

@ -10,7 +10,7 @@ import android.os.Bundle
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import at.bitfire.davdroid.db.Credentials import at.bitfire.davdroid.db.Credentials
import at.bitfire.davdroid.ui.AppTheme import at.bitfire.davdroid.ui.M2Theme
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import java.net.URI import java.net.URI
import javax.inject.Inject import javax.inject.Inject
@ -118,7 +118,7 @@ class LoginActivity @Inject constructor(): AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContent { setContent {
AppTheme { M2Theme {
LoginScreen( LoginScreen(
loginTypesProvider = loginTypesProvider, loginTypesProvider = loginTypesProvider,
initialLoginInfo = loginInfoFromIntent(intent), initialLoginInfo = loginInfoFromIntent(intent),

View file

@ -59,7 +59,7 @@ import at.bitfire.davdroid.db.Credentials
import at.bitfire.davdroid.db.WebDavMount import at.bitfire.davdroid.db.WebDavMount
import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.network.HttpClient import at.bitfire.davdroid.network.HttpClient
import at.bitfire.davdroid.ui.AppTheme import at.bitfire.davdroid.ui.M2Theme
import at.bitfire.davdroid.ui.composable.PasswordTextField import at.bitfire.davdroid.ui.composable.PasswordTextField
import at.bitfire.davdroid.webdav.CredentialsStore import at.bitfire.davdroid.webdav.CredentialsStore
import at.bitfire.davdroid.webdav.DavDocumentsProvider import at.bitfire.davdroid.webdav.DavDocumentsProvider
@ -93,7 +93,7 @@ class AddWebdavMountActivity : AppCompatActivity() {
val username by model.userName.observeAsState(initial = "") val username by model.userName.observeAsState(initial = "")
val password by model.password.observeAsState(initial = "") val password by model.password.observeAsState(initial = "")
AppTheme { M2Theme {
Layout( Layout(
isLoading = isLoading, isLoading = isLoading,
error = error, error = error,
@ -303,7 +303,7 @@ class AddWebdavMountActivity : AppCompatActivity() {
@Preview @Preview
@Composable @Composable
fun Layout_Preview() { fun Layout_Preview() {
AppTheme { M2Theme {
Layout() Layout()
} }
} }

View file

@ -65,7 +65,7 @@ import at.bitfire.davdroid.db.AppDatabase
import at.bitfire.davdroid.db.WebDavDocument import at.bitfire.davdroid.db.WebDavDocument
import at.bitfire.davdroid.db.WebDavMount import at.bitfire.davdroid.db.WebDavMount
import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.ui.AppTheme import at.bitfire.davdroid.ui.M2Theme
import at.bitfire.davdroid.ui.UiUtils.toAnnotatedString import at.bitfire.davdroid.ui.UiUtils.toAnnotatedString
import at.bitfire.davdroid.ui.widget.ClickableTextWithLink import at.bitfire.davdroid.ui.widget.ClickableTextWithLink
import at.bitfire.davdroid.util.DavUtils import at.bitfire.davdroid.util.DavUtils
@ -104,7 +104,7 @@ class WebdavMountsActivity: AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContent { setContent {
AppTheme { M2Theme {
val mountInfos by model.mountInfos.observeAsState(emptyList()) val mountInfos by model.mountInfos.observeAsState(emptyList())
WebdavMountsContent(mountInfos) WebdavMountsContent(mountInfos)
} }
@ -328,7 +328,7 @@ class WebdavMountsActivity: AppCompatActivity() {
@Preview(showBackground = true, showSystemUi = true) @Preview(showBackground = true, showSystemUi = true)
@Composable @Composable
fun WebdavMountsContent_Preview() { fun WebdavMountsContent_Preview() {
AppTheme { M2Theme {
WebdavMountsContent(emptyList()) WebdavMountsContent(emptyList())
} }
} }
@ -336,7 +336,7 @@ class WebdavMountsActivity: AppCompatActivity() {
@Preview(showBackground = true) @Preview(showBackground = true)
@Composable @Composable
fun WebdavMountsItem_Preview() { fun WebdavMountsItem_Preview() {
AppTheme { M2Theme {
WebdavMountsItem( WebdavMountsItem(
info = MountInfo( info = MountInfo(
mount = WebDavMount( mount = WebDavMount(

View file

@ -31,8 +31,8 @@ import androidx.glance.text.TextDefaults
import androidx.glance.unit.ColorProvider import androidx.glance.unit.ColorProvider
import at.bitfire.davdroid.R import at.bitfire.davdroid.R
import at.bitfire.davdroid.syncadapter.OneTimeSyncWorker import at.bitfire.davdroid.syncadapter.OneTimeSyncWorker
import at.bitfire.davdroid.ui.ThemeColors.onPrimary import at.bitfire.davdroid.ui.M2Colors.onPrimary
import at.bitfire.davdroid.ui.ThemeColors.primary import at.bitfire.davdroid.ui.M2Colors.primary
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

View file

@ -6,17 +6,18 @@ package at.bitfire.davdroid.ui
import android.app.Application import android.app.Application
import android.text.Spanned import android.text.Spanned
import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import at.bitfire.davdroid.ui.UiUtils.toAnnotatedString import at.bitfire.davdroid.ui.UiUtils.toAnnotatedString
import at.bitfire.davdroid.ui.widget.ClickableTextWithLink
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -34,23 +35,20 @@ class OpenSourceLicenseInfoProvider @Inject constructor(): AboutActivity.AppLice
fun LicenseInfoGpl( fun LicenseInfoGpl(
model: Model = viewModel() model: Model = viewModel()
) { ) {
model.gpl.observeAsState().value?.let { gpl -> model.gpl?.let { OpenSourceLicenseInfo(it.toAnnotatedString()) }
OpenSourceLicenseInfo(gpl.toAnnotatedString())
}
} }
@HiltViewModel @HiltViewModel
class Model @Inject constructor(app: Application): AndroidViewModel(app) { class Model @Inject constructor(app: Application): AndroidViewModel(app) {
val gpl = MutableLiveData<Spanned>() var gpl by mutableStateOf<Spanned?>(null)
init { init {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
app.resources.assets.open("gplv3.html").use { inputStream -> app.resources.assets.open("gplv3.html").use { inputStream ->
val raw = IOUtils.toString(inputStream, Charsets.UTF_8) val raw = IOUtils.toString(inputStream, Charsets.UTF_8)
val html = HtmlCompat.fromHtml(raw, HtmlCompat.FROM_HTML_MODE_LEGACY) gpl = HtmlCompat.fromHtml(raw, HtmlCompat.FROM_HTML_MODE_LEGACY)
gpl.postValue(html)
} }
} }
} }
@ -62,7 +60,7 @@ class OpenSourceLicenseInfoProvider @Inject constructor(): AboutActivity.AppLice
@Composable @Composable
fun OpenSourceLicenseInfo(license: AnnotatedString) { fun OpenSourceLicenseInfo(license: AnnotatedString) {
Text(license) ClickableTextWithLink(license)
} }
@Composable @Composable

View file

@ -5,9 +5,11 @@
package at.bitfire.davdroid.ui package at.bitfire.davdroid.ui
import androidx.compose.material.Colors import androidx.compose.material.Colors
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
object ThemeColors { object M2Colors {
private val grey100 = Color(0xfff5f5f5) private val grey100 = Color(0xfff5f5f5)
private val grey200 = Color(0xffeeeeee) private val grey200 = Color(0xffeeeeee)
@ -54,4 +56,136 @@ object ThemeColors {
isLight = false isLight = false
) )
}
object M3ColorScheme {
val md_theme_light_primary = Color(0xFF3C6A00)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFFB8F47A)
val md_theme_light_onPrimaryContainer = Color(0xFF0E2000)
val md_theme_light_secondary = Color(0xFF3C6A00)
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
val md_theme_light_secondaryContainer = Color(0xFFB8F47A)
val md_theme_light_onSecondaryContainer = Color(0xFF0E2000)
val md_theme_light_tertiary = Color(0xFF964900)
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
val md_theme_light_tertiaryContainer = Color(0xFFFFDCC7)
val md_theme_light_onTertiaryContainer = Color(0xFF311300)
val md_theme_light_error = Color(0xFFBA1A1A)
val md_theme_light_errorContainer = Color(0xFFFFDAD6)
val md_theme_light_onError = Color(0xFFFFFFFF)
val md_theme_light_onErrorContainer = Color(0xFF410002)
val md_theme_light_background = Color(0xFFF8FDFF)
val md_theme_light_onBackground = Color(0xFF001F25)
val md_theme_light_surface = Color(0xFFF8FDFF)
val md_theme_light_onSurface = Color(0xFF001F25)
val md_theme_light_surfaceVariant = Color(0xFFE1E4D5)
val md_theme_light_onSurfaceVariant = Color(0xFF44483D)
val md_theme_light_outline = Color(0xFF75796C)
val md_theme_light_inverseOnSurface = Color(0xFFD6F6FF)
val md_theme_light_inverseSurface = Color(0xFF00363F)
val md_theme_light_inversePrimary = Color(0xFF9DD761)
val md_theme_light_shadow = Color(0xFF000000)
val md_theme_light_surfaceTint = Color(0xFF3C6A00)
val md_theme_light_outlineVariant = Color(0xFFC4C8BA)
val md_theme_light_scrim = Color(0xFF000000)
val md_theme_dark_primary = Color(0xFF9DD761)
val md_theme_dark_onPrimary = Color(0xFF1D3700)
val md_theme_dark_primaryContainer = Color(0xFF2C5000)
val md_theme_dark_onPrimaryContainer = Color(0xFFB8F47A)
val md_theme_dark_secondary = Color(0xFF9DD761)
val md_theme_dark_onSecondary = Color(0xFF1D3700)
val md_theme_dark_secondaryContainer = Color(0xFF2C5000)
val md_theme_dark_onSecondaryContainer = Color(0xFFB8F47A)
val md_theme_dark_tertiary = Color(0xFFFFB787)
val md_theme_dark_onTertiary = Color(0xFF502400)
val md_theme_dark_tertiaryContainer = Color(0xFF723600)
val md_theme_dark_onTertiaryContainer = Color(0xFFFFDCC7)
val md_theme_dark_error = Color(0xFFFFB4AB)
val md_theme_dark_errorContainer = Color(0xFF93000A)
val md_theme_dark_onError = Color(0xFF690005)
val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6)
val md_theme_dark_background = Color(0xFF001F25)
val md_theme_dark_onBackground = Color(0xFFA6EEFF)
val md_theme_dark_surface = Color(0xFF001F25)
val md_theme_dark_onSurface = Color(0xFFA6EEFF)
val md_theme_dark_surfaceVariant = Color(0xFF44483D)
val md_theme_dark_onSurfaceVariant = Color(0xFFC4C8BA)
val md_theme_dark_outline = Color(0xFF8E9285)
val md_theme_dark_inverseOnSurface = Color(0xFF001F25)
val md_theme_dark_inverseSurface = Color(0xFFA6EEFF)
val md_theme_dark_inversePrimary = Color(0xFF3C6A00)
val md_theme_dark_shadow = Color(0xFF000000)
val md_theme_dark_surfaceTint = Color(0xFF9DD761)
val md_theme_dark_outlineVariant = Color(0xFF44483D)
val md_theme_dark_scrim = Color(0xFF000000)
val seed = Color(0xFF7CB342)
val LightColors = lightColorScheme(
primary = md_theme_light_primary,
onPrimary = md_theme_light_onPrimary,
primaryContainer = md_theme_light_primaryContainer,
onPrimaryContainer = md_theme_light_onPrimaryContainer,
secondary = md_theme_light_secondary,
onSecondary = md_theme_light_onSecondary,
secondaryContainer = md_theme_light_secondaryContainer,
onSecondaryContainer = md_theme_light_onSecondaryContainer,
tertiary = md_theme_light_tertiary,
onTertiary = md_theme_light_onTertiary,
tertiaryContainer = md_theme_light_tertiaryContainer,
onTertiaryContainer = md_theme_light_onTertiaryContainer,
error = md_theme_light_error,
errorContainer = md_theme_light_errorContainer,
onError = md_theme_light_onError,
onErrorContainer = md_theme_light_onErrorContainer,
background = md_theme_light_background,
onBackground = md_theme_light_onBackground,
surface = md_theme_light_surface,
onSurface = md_theme_light_onSurface,
surfaceVariant = md_theme_light_surfaceVariant,
onSurfaceVariant = md_theme_light_onSurfaceVariant,
outline = md_theme_light_outline,
inverseOnSurface = md_theme_light_inverseOnSurface,
inverseSurface = md_theme_light_inverseSurface,
inversePrimary = md_theme_light_inversePrimary,
surfaceTint = md_theme_light_surfaceTint,
outlineVariant = md_theme_light_outlineVariant,
scrim = md_theme_light_scrim,
)
val DarkColors = darkColorScheme(
primary = md_theme_dark_primary,
onPrimary = md_theme_dark_onPrimary,
primaryContainer = md_theme_dark_primaryContainer,
onPrimaryContainer = md_theme_dark_onPrimaryContainer,
secondary = md_theme_dark_secondary,
onSecondary = md_theme_dark_onSecondary,
secondaryContainer = md_theme_dark_secondaryContainer,
onSecondaryContainer = md_theme_dark_onSecondaryContainer,
tertiary = md_theme_dark_tertiary,
onTertiary = md_theme_dark_onTertiary,
tertiaryContainer = md_theme_dark_tertiaryContainer,
onTertiaryContainer = md_theme_dark_onTertiaryContainer,
error = md_theme_dark_error,
errorContainer = md_theme_dark_errorContainer,
onError = md_theme_dark_onError,
onErrorContainer = md_theme_dark_onErrorContainer,
background = md_theme_dark_background,
onBackground = md_theme_dark_onBackground,
surface = md_theme_dark_surface,
onSurface = md_theme_dark_onSurface,
surfaceVariant = md_theme_dark_surfaceVariant,
onSurfaceVariant = md_theme_dark_onSurfaceVariant,
outline = md_theme_dark_outline,
inverseOnSurface = md_theme_dark_inverseOnSurface,
inverseSurface = md_theme_dark_inverseSurface,
inversePrimary = md_theme_dark_inversePrimary,
surfaceTint = md_theme_dark_surfaceTint,
outlineVariant = md_theme_dark_outlineVariant,
scrim = md_theme_dark_scrim,
)
} }

View file

@ -98,6 +98,7 @@ commons-text = { module = "org.apache.commons:commons-text", version.ref = "comm
compose-accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "compose-accompanist" } compose-accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "compose-accompanist" }
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" } compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
compose-material = { module = "androidx.compose.material:material" } compose-material = { module = "androidx.compose.material:material" }
compose-material3 = { group = "androidx.compose.material3", name = "material3" }
compose-materialIconsExtended = { module = "androidx.compose.material:material-icons-extended" } compose-materialIconsExtended = { module = "androidx.compose.material:material-icons-extended" }
compose-runtime-livedata = { module = "androidx.compose.runtime:runtime-livedata" } compose-runtime-livedata = { module = "androidx.compose.runtime:runtime-livedata" }
compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" } compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" }
@ -111,7 +112,7 @@ hilt-android-testing = { module = "com.google.dagger:hilt-android-testing", vers
junit = { module = "junit:junit", version = "4.13.2" } junit = { module = "junit:junit", version = "4.13.2" }
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" } kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" } kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
mikepenz-aboutLibraries = { module = "com.mikepenz:aboutlibraries-compose", version.ref = "mikepenz-aboutLibraries" } mikepenz-aboutLibraries = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "mikepenz-aboutLibraries" }
mockk-android = { module = "io.mockk:mockk-android", version.ref = "mockk" } mockk-android = { module = "io.mockk:mockk-android", version.ref = "mockk" }
nsk90-kstatemachine = { module = "io.github.nsk90:kstatemachine-jvm", version.ref = "nsk90-kstatemachine" } nsk90-kstatemachine = { module = "io.github.nsk90:kstatemachine-jvm", version.ref = "nsk90-kstatemachine" }
okhttp-base = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } okhttp-base = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }