From 023ac6e5cb57b019d45f5d8c027106db0c31ebee Mon Sep 17 00:00:00 2001 From: Infi Date: Mon, 30 Jan 2023 00:55:18 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20theme=20picker=20and=20settings=20sync?= =?UTF-8?q?=20=F0=9F=94=A5=F0=9F=94=A5=F0=9F=94=A5=F0=9F=94=A5=F0=9F=94=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 1 + app/src/main/java/chat/revolt/MainActivity.kt | 107 +++++++------ .../revolt/api/realtime/RealtimeSocket.kt | 9 +- .../frames/receivable/ReceivableFrames.kt | 2 +- .../frames/sendable/SendableFrames.kt | 2 +- .../revolt/api/routes/sync/SettingsSync.kt | 68 ++++++++ .../java/chat/revolt/api/schemas/Settings.kt | 17 ++ .../chat/revolt/api/settings/GlobalState.kt | 14 ++ .../revolt/api/settings/SyncedSettings.kt | 46 ++++++ .../revolt/components/generic/PageHeader.kt | 51 ++++-- .../chat/revolt/components/generic/Weblink.kt | 5 +- .../screens/settings/SettingsCategory.kt | 67 ++++++++ .../screens/settings/appearance/ThemeChip.kt | 49 ++++++ .../java/chat/revolt/screens/SplashScreen.kt | 19 ++- .../chat/revolt/screens/about/AboutScreen.kt | 13 +- .../revolt/screens/about/AttributionScreen.kt | 9 +- .../revolt/screens/about/PlaceholderScreen.kt | 7 +- .../screens/chat/views/ChannelScreen.kt | 14 +- .../revolt/screens/login/GreeterScreen.kt | 5 +- .../chat/revolt/screens/login/MfaScreen.kt | 13 +- .../settings/AppearanceSettingsScreen.kt | 145 ++++++++++++++++++ .../revolt/screens/settings/SettingsScreen.kt | 65 ++++++-- .../main/java/chat/revolt/ui/theme/Color.kt | 11 -- .../main/java/chat/revolt/ui/theme/Theme.kt | 106 ++++++++----- .../java/chat/revolt/ui/theme/Typography.kt | 16 +- app/src/main/res/drawable/ic_palette_24dp.xml | 9 ++ .../main/res/drawable/revolt_logo_wide.xml | 4 +- app/src/main/res/values/strings.xml | 10 ++ 28 files changed, 710 insertions(+), 174 deletions(-) create mode 100644 app/src/main/java/chat/revolt/api/routes/sync/SettingsSync.kt create mode 100644 app/src/main/java/chat/revolt/api/schemas/Settings.kt create mode 100644 app/src/main/java/chat/revolt/api/settings/GlobalState.kt create mode 100644 app/src/main/java/chat/revolt/api/settings/SyncedSettings.kt create mode 100644 app/src/main/java/chat/revolt/components/screens/settings/SettingsCategory.kt create mode 100644 app/src/main/java/chat/revolt/components/screens/settings/appearance/ThemeChip.kt create mode 100644 app/src/main/java/chat/revolt/screens/settings/AppearanceSettingsScreen.kt delete mode 100644 app/src/main/java/chat/revolt/ui/theme/Color.kt create mode 100644 app/src/main/res/drawable/ic_palette_24dp.xml diff --git a/app/build.gradle b/app/build.gradle index f7ab3acf..ef9bac75 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -75,6 +75,7 @@ dependencies { implementation "com.google.accompanist:accompanist-systemuicontroller:$accompanist_version" implementation "com.google.accompanist:accompanist-permissions:$accompanist_version" implementation "com.google.accompanist:accompanist-navigation-animation:$accompanist_version" + implementation "com.google.accompanist:accompanist-flowlayout:$accompanist_version" // KTOR - HTTP+WebSocket Library implementation "io.ktor:ktor-client-core:$ktor_version" diff --git a/app/src/main/java/chat/revolt/MainActivity.kt b/app/src/main/java/chat/revolt/MainActivity.kt index 3d969cc1..96bff5ad 100644 --- a/app/src/main/java/chat/revolt/MainActivity.kt +++ b/app/src/main/java/chat/revolt/MainActivity.kt @@ -13,6 +13,7 @@ import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntSize +import chat.revolt.api.settings.GlobalState import chat.revolt.screens.SplashScreen import chat.revolt.screens.about.AboutScreen import chat.revolt.screens.about.AttributionScreen @@ -21,6 +22,7 @@ import chat.revolt.screens.chat.ChatRouterScreen import chat.revolt.screens.login.GreeterScreen import chat.revolt.screens.login.LoginScreen import chat.revolt.screens.login.MfaScreen +import chat.revolt.screens.settings.AppearanceSettingsScreen import chat.revolt.screens.settings.SettingsScreen import chat.revolt.ui.theme.RevoltTheme import com.google.accompanist.navigation.animation.AnimatedNavHost @@ -33,14 +35,7 @@ class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { - RevoltTheme { - Surface( - modifier = Modifier.fillMaxSize(), - color = MaterialTheme.colorScheme.background - ) { - AppEntrypoint() - } - } + AppEntrypoint() } } } @@ -54,52 +49,62 @@ val RevoltTweenFloat: FiniteAnimationSpec = tween(400, easing = EaseInOut fun AppEntrypoint() { val navController = rememberAnimatedNavController() - AnimatedNavHost( - navController = navController, - startDestination = "splash", - enterTransition = { - slideIntoContainer( - AnimatedContentScope.SlideDirection.Left, - animationSpec = RevoltTweenInt - ) + fadeIn(animationSpec = RevoltTweenFloat) - }, - exitTransition = { - slideOutOfContainer( - AnimatedContentScope.SlideDirection.Left, - animationSpec = RevoltTweenInt - ) + fadeOut(animationSpec = RevoltTweenFloat) - }, - popEnterTransition = { - slideIntoContainer( - AnimatedContentScope.SlideDirection.Right, - animationSpec = RevoltTweenInt - ) + fadeIn(animationSpec = RevoltTweenFloat) - }, - popExitTransition = { - slideOutOfContainer( - AnimatedContentScope.SlideDirection.Right, - animationSpec = RevoltTweenInt - ) + fadeOut(animationSpec = RevoltTweenFloat) - } + RevoltTheme( + requestedTheme = GlobalState.theme, ) { - composable("splash") { SplashScreen(navController) } + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background + ) { + AnimatedNavHost( + navController = navController, + startDestination = "splash", + enterTransition = { + slideIntoContainer( + AnimatedContentScope.SlideDirection.Left, + animationSpec = RevoltTweenInt + ) + fadeIn(animationSpec = RevoltTweenFloat) + }, + exitTransition = { + slideOutOfContainer( + AnimatedContentScope.SlideDirection.Left, + animationSpec = RevoltTweenInt + ) + fadeOut(animationSpec = RevoltTweenFloat) + }, + popEnterTransition = { + slideIntoContainer( + AnimatedContentScope.SlideDirection.Right, + animationSpec = RevoltTweenInt + ) + fadeIn(animationSpec = RevoltTweenFloat) + }, + popExitTransition = { + slideOutOfContainer( + AnimatedContentScope.SlideDirection.Right, + animationSpec = RevoltTweenInt + ) + fadeOut(animationSpec = RevoltTweenFloat) + } + ) { + composable("splash") { SplashScreen(navController) } - composable("login/greeting") { GreeterScreen(navController) } - composable("login/login") { LoginScreen(navController) } - composable("login/mfa/{mfaTicket}/{allowedAuthTypes}") { backStackEntry -> - val mfaTicket = backStackEntry.arguments?.getString("mfaTicket") ?: "" - val allowedAuthTypes = - backStackEntry.arguments?.getString("allowedAuthTypes") ?: "" + composable("login/greeting") { GreeterScreen(navController) } + composable("login/login") { LoginScreen(navController) } + composable("login/mfa/{mfaTicket}/{allowedAuthTypes}") { backStackEntry -> + val mfaTicket = backStackEntry.arguments?.getString("mfaTicket") ?: "" + val allowedAuthTypes = + backStackEntry.arguments?.getString("allowedAuthTypes") ?: "" - MfaScreen(navController, allowedAuthTypes, mfaTicket) + MfaScreen(navController, allowedAuthTypes, mfaTicket) + } + + composable("chat") { ChatRouterScreen(navController) } + + composable("settings") { SettingsScreen(navController) } + composable("settings/appearance") { AppearanceSettingsScreen(navController) } + + composable("about") { AboutScreen(navController) } + composable("about/oss") { AttributionScreen(navController) } + composable("about/placeholder") { PlaceholderScreen(navController) } + } } - - composable("chat") { ChatRouterScreen(navController) } - - composable("settings") { SettingsScreen(navController) } - - composable("about") { AboutScreen(navController) } - composable("about/oss") { AttributionScreen(navController) } - composable("about/placeholder") { PlaceholderScreen(navController) } } } diff --git a/app/src/main/java/chat/revolt/api/realtime/RealtimeSocket.kt b/app/src/main/java/chat/revolt/api/realtime/RealtimeSocket.kt index 2901882d..d6fa59b5 100644 --- a/app/src/main/java/chat/revolt/api/realtime/RealtimeSocket.kt +++ b/app/src/main/java/chat/revolt/api/realtime/RealtimeSocket.kt @@ -14,7 +14,6 @@ import chat.revolt.api.realtime.frames.sendable.PingFrame import io.ktor.client.plugins.websocket.* import io.ktor.websocket.* import kotlinx.coroutines.channels.consumeEach -import java.util.* enum class DisconnectionState { Disconnected, @@ -64,7 +63,7 @@ object RealtimeSocket { suspend fun sendPing() { if (disconnectionState != DisconnectionState.Connected) return - val pingPacket = PingFrame("Ping", Calendar.getInstance().timeInMillis.toInt()) + val pingPacket = PingFrame("Ping", System.currentTimeMillis()) socket?.send(RevoltJson.encodeToString(PingFrame.serializer(), pingPacket)) Log.d("RealtimeSocket", "Sent ping frame with ${pingPacket.data}") } @@ -167,7 +166,7 @@ object RealtimeSocket { } } } - + private fun invalidateAllChannelStates() { channelCallbacks.forEach { (_, callback) -> callback.onStateInvalidate() @@ -189,8 +188,8 @@ object RealtimeSocket { Log.d("RealtimeSocket", "Registered channel callback for $channelId.") } - fun unregisterChannelCallback(channelId: String, callback: ChannelCallback) { - channelCallbacks.remove(channelId, callback) + fun unregisterChannelCallback(channelId: String) { + channelCallbacks.remove(channelId) Log.d("RealtimeSocket", "Unregistered channel callback for $channelId") } diff --git a/app/src/main/java/chat/revolt/api/realtime/frames/receivable/ReceivableFrames.kt b/app/src/main/java/chat/revolt/api/realtime/frames/receivable/ReceivableFrames.kt index a3e928c8..40f0f85c 100644 --- a/app/src/main/java/chat/revolt/api/realtime/frames/receivable/ReceivableFrames.kt +++ b/app/src/main/java/chat/revolt/api/realtime/frames/receivable/ReceivableFrames.kt @@ -25,7 +25,7 @@ data class BulkFrame( @Serializable data class PongFrame( val type: String = "Pong", - val data: Int + val data: Long ) @Serializable diff --git a/app/src/main/java/chat/revolt/api/realtime/frames/sendable/SendableFrames.kt b/app/src/main/java/chat/revolt/api/realtime/frames/sendable/SendableFrames.kt index 5b5309b9..527f01ca 100644 --- a/app/src/main/java/chat/revolt/api/realtime/frames/sendable/SendableFrames.kt +++ b/app/src/main/java/chat/revolt/api/realtime/frames/sendable/SendableFrames.kt @@ -11,7 +11,7 @@ data class AuthorizationFrame( @Serializable data class PingFrame( val type: String, - val data: Int + val data: Long ) @Serializable diff --git a/app/src/main/java/chat/revolt/api/routes/sync/SettingsSync.kt b/app/src/main/java/chat/revolt/api/routes/sync/SettingsSync.kt new file mode 100644 index 00000000..10740c66 --- /dev/null +++ b/app/src/main/java/chat/revolt/api/routes/sync/SettingsSync.kt @@ -0,0 +1,68 @@ +package chat.revolt.api.routes.sync + +import chat.revolt.api.RevoltAPI +import chat.revolt.api.RevoltHttp +import chat.revolt.api.RevoltJson +import io.ktor.client.request.* +import io.ktor.client.statement.* +import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.ListSerializer +import kotlinx.serialization.builtins.MapSerializer +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.json.JsonArray + +@Serializable +data class SyncedSetting(val timestamp: Long, val value: String) + +suspend fun getKeys(vararg keys: String): Map { + val response = RevoltHttp.post("/sync/settings/fetch") { + headers.append(RevoltAPI.TOKEN_HEADER_NAME, RevoltAPI.sessionToken) + + // format: {"keys": ["key1", "key2"]} + setBody( + RevoltJson.encodeToString( + MapSerializer( + String.serializer(), + ListSerializer(String.serializer()) + ), + mapOf("keys" to keys.toList()) + ) + ) + }.bodyAsText() + + return RevoltJson.decodeFromString( + MapSerializer( + String.serializer(), + JsonArray.serializer() + ), + response + ).mapValues { (_, value) -> + SyncedSetting( + timestamp = value[0].toString().toLong(), + value = value[1] + .toString() + .removeSurrounding("\"") + .replace("\\\"", "\"") + .replace("\\\\", "\\") // the revolt API is so scuffed i can't even make this up + ) + } +} + +suspend fun setKey(key: String, value: String) { + RevoltHttp.post("/sync/settings/set") { + headers.append(RevoltAPI.TOKEN_HEADER_NAME, RevoltAPI.sessionToken) + + parameter("timestamp", System.currentTimeMillis()) + + // format: {"key": "value"} + setBody( + RevoltJson.encodeToString( + MapSerializer( + String.serializer(), + String.serializer() + ), + mapOf(key to value) + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/api/schemas/Settings.kt b/app/src/main/java/chat/revolt/api/schemas/Settings.kt new file mode 100644 index 00000000..c4af60ca --- /dev/null +++ b/app/src/main/java/chat/revolt/api/schemas/Settings.kt @@ -0,0 +1,17 @@ +package chat.revolt.api.schemas + +import kotlinx.serialization.Serializable + +@Serializable +data class OrderingSettings( + val servers: List = emptyList(), +) + +@Serializable +data class AndroidSpecificSettings( + /** + * The theme to use for the app. + * Can be one of { None, Revolt, Light, M3Dynamic, Amoled } + */ + var theme: String? = null, +) \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/api/settings/GlobalState.kt b/app/src/main/java/chat/revolt/api/settings/GlobalState.kt new file mode 100644 index 00000000..0f7f0168 --- /dev/null +++ b/app/src/main/java/chat/revolt/api/settings/GlobalState.kt @@ -0,0 +1,14 @@ +package chat.revolt.api.settings + +import androidx.compose.runtime.mutableStateOf +import chat.revolt.ui.theme.Theme + +object GlobalState { + private var _theme = mutableStateOf(Theme.Revolt) + val theme + get() = _theme.value + + fun setTheme(theme: Theme) { + _theme.value = theme + } +} \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/api/settings/SyncedSettings.kt b/app/src/main/java/chat/revolt/api/settings/SyncedSettings.kt new file mode 100644 index 00000000..ceaa1712 --- /dev/null +++ b/app/src/main/java/chat/revolt/api/settings/SyncedSettings.kt @@ -0,0 +1,46 @@ +package chat.revolt.api.settings + +import androidx.compose.runtime.mutableStateOf +import chat.revolt.api.RevoltJson +import chat.revolt.api.routes.sync.getKeys +import chat.revolt.api.routes.sync.setKey +import chat.revolt.api.schemas.AndroidSpecificSettings +import chat.revolt.api.schemas.OrderingSettings + +object SyncedSettings { + private val _ordering = mutableStateOf(OrderingSettings()) + private val _android = mutableStateOf(AndroidSpecificSettings("None")) + + val ordering: OrderingSettings + get() = _ordering.value + val android: AndroidSpecificSettings + get() = _android.value + + suspend fun fetch() { + val settings = getKeys("ordering", "android") + + settings["ordering"]?.let { + _ordering.value = RevoltJson.decodeFromString( + OrderingSettings.serializer(), + it.value + ) + } + + settings["android"]?.let { + _android.value = RevoltJson.decodeFromString( + AndroidSpecificSettings.serializer(), + it.value + ) + } + } + + suspend fun updateOrdering(value: OrderingSettings) { + _ordering.value = value + setKey("ordering", RevoltJson.encodeToString(OrderingSettings.serializer(), value)) + } + + suspend fun updateAndroid(value: AndroidSpecificSettings) { + _android.value = value + setKey("android", RevoltJson.encodeToString(AndroidSpecificSettings.serializer(), value)) + } +} \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/components/generic/PageHeader.kt b/app/src/main/java/chat/revolt/components/generic/PageHeader.kt index 6e7963ab..37bca79a 100644 --- a/app/src/main/java/chat/revolt/components/generic/PageHeader.kt +++ b/app/src/main/java/chat/revolt/components/generic/PageHeader.kt @@ -1,37 +1,66 @@ package chat.revolt.components.generic +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +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.compose.ui.unit.sp +import chat.revolt.R @Composable fun PageHeader( text: String, modifier: Modifier = Modifier, + showBackButton: Boolean = false, + onBackButtonClicked: () -> Unit = {}, ) { - Text( - text = text, - style = MaterialTheme.typography.displaySmall.copy( - fontWeight = FontWeight.Bold, - textAlign = TextAlign.Left, - fontSize = 24.sp - ), - modifier = modifier - .padding(horizontal = 15.dp, vertical = 15.dp) - .fillMaxWidth(), - ) + Row( + verticalAlignment = Alignment.CenterVertically + ) { + if (showBackButton) { + IconButton(onClick = onBackButtonClicked) { + Icon( + modifier = modifier, + imageVector = Icons.Default.ArrowBack, + contentDescription = stringResource(id = R.string.back) + ) + } + } + Text( + text = text, + style = MaterialTheme.typography.displaySmall.copy( + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Left, + fontSize = 24.sp + ), + modifier = modifier + .padding(horizontal = 15.dp, vertical = 15.dp) + .fillMaxWidth(), + ) + } } @Preview @Composable fun PageHeaderPreview() { PageHeader(text = "Page Header") +} + +@Preview +@Composable +fun PageHeaderPreviewWithBackButton() { + PageHeader(text = "Page Header", showBackButton = true) } \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/components/generic/Weblink.kt b/app/src/main/java/chat/revolt/components/generic/Weblink.kt index 1de9c11c..3a1df168 100644 --- a/app/src/main/java/chat/revolt/components/generic/Weblink.kt +++ b/app/src/main/java/chat/revolt/components/generic/Weblink.kt @@ -8,7 +8,6 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign @@ -32,7 +31,9 @@ fun Weblink(text: String, url: String, modifier: Modifier = Modifier) { fun AnyLink(text: String, action: () -> Unit, modifier: Modifier = Modifier) { Text( text = text, - color = Color(0xaaffffff), + color = MaterialTheme.colorScheme.onBackground.copy( + alpha = 0.5f + ), style = MaterialTheme.typography.titleMedium.copy( textAlign = TextAlign.Center, fontWeight = FontWeight.Normal, diff --git a/app/src/main/java/chat/revolt/components/screens/settings/SettingsCategory.kt b/app/src/main/java/chat/revolt/components/screens/settings/SettingsCategory.kt new file mode 100644 index 00000000..24691ccb --- /dev/null +++ b/app/src/main/java/chat/revolt/components/screens/settings/SettingsCategory.kt @@ -0,0 +1,67 @@ +package chat.revolt.components.screens.settings + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Person +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp + +@Composable +fun SettingsCategory( + icon: @Composable (Modifier) -> Unit, + label: @Composable (TextStyle) -> Unit, + onClick: () -> Unit, +) { + Box(modifier = Modifier.padding(bottom = 8.dp)) { + Row( + modifier = Modifier + .clip(MaterialTheme.shapes.medium) + .background(MaterialTheme.colorScheme.surface) + .clickable(onClick = onClick) + .padding(all = 4.dp) + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + icon(Modifier.padding(end = 16.dp)) + label( + MaterialTheme.typography.bodyMedium.copy( + color = MaterialTheme.colorScheme.onSurface, + fontWeight = FontWeight.SemiBold, + ) + ) + } + } +} + +@Preview +@Composable +fun SettingsCategoryPreview() { + SettingsCategory( + icon = { modifier -> + Icon( + modifier = modifier, + imageVector = Icons.Default.Person, + contentDescription = "Account" + ) + }, + label = { textStyle -> + Text(text = "Account", style = textStyle) + }, + onClick = {} + ) +} \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/components/screens/settings/appearance/ThemeChip.kt b/app/src/main/java/chat/revolt/components/screens/settings/appearance/ThemeChip.kt new file mode 100644 index 00000000..d2323e47 --- /dev/null +++ b/app/src/main/java/chat/revolt/components/screens/settings/appearance/ThemeChip.kt @@ -0,0 +1,49 @@ +package chat.revolt.components.screens.settings.appearance + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp + +@Composable +fun ThemeChip( + modifier: Modifier = Modifier, + color: Color, + text: String, + selected: Boolean = false, + onClick: () -> Unit +) { + Column( + Modifier + .clip(MaterialTheme.shapes.medium) + .clickable(onClick = onClick) + .then(modifier) + .then( + if (selected) + Modifier + .background(MaterialTheme.colorScheme.onSurface.copy(alpha = 0.1f)) + else Modifier + ) + .padding(4.dp) + .padding(8.dp) + ) { + Box( + modifier = Modifier + .clip(MaterialTheme.shapes.medium) + .background(color) + .height(60.dp) + .fillMaxWidth(1f) + ) + Text( + text = text, + modifier = Modifier.padding(top = 8.dp), + style = MaterialTheme.typography.labelLarge + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/screens/SplashScreen.kt b/app/src/main/java/chat/revolt/screens/SplashScreen.kt index a5ff3b89..7c92ffa8 100644 --- a/app/src/main/java/chat/revolt/screens/SplashScreen.kt +++ b/app/src/main/java/chat/revolt/screens/SplashScreen.kt @@ -5,6 +5,7 @@ import android.content.Context import android.net.ConnectivityManager import android.net.NetworkCapabilities import androidx.compose.foundation.Image +import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -21,8 +22,12 @@ import androidx.lifecycle.viewModelScope import androidx.navigation.NavController import chat.revolt.R import chat.revolt.api.RevoltAPI +import chat.revolt.api.settings.GlobalState +import chat.revolt.api.settings.SyncedSettings import chat.revolt.components.screens.splash.DisconnectedScreen import chat.revolt.persistence.KVStorage +import chat.revolt.ui.theme.RevoltColorScheme +import chat.revolt.ui.theme.Theme import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.launch @@ -82,18 +87,29 @@ class SplashScreenViewModel @Inject constructor( setNavigateTo("login") } else { RevoltAPI.loginAs(token) + loadSettings() setNavigateTo("home") } } } + private fun loadSettings() { + viewModelScope.launch { + SyncedSettings.fetch() + SyncedSettings.android.theme?.let { GlobalState.setTheme(Theme.valueOf(it)) } + } + } + init { checkLoggedInState() } } @Composable -fun SplashScreen(navController: NavController, viewModel: SplashScreenViewModel = hiltViewModel()) { +fun SplashScreen( + navController: NavController, + viewModel: SplashScreenViewModel = hiltViewModel() +) { if (!viewModel.isConnected) { DisconnectedScreen( onRetry = { @@ -105,6 +121,7 @@ fun SplashScreen(navController: NavController, viewModel: SplashScreenViewModel Column( modifier = Modifier + .background(color = RevoltColorScheme.background) .fillMaxWidth() .fillMaxHeight(), verticalArrangement = Arrangement.Center, diff --git a/app/src/main/java/chat/revolt/screens/about/AboutScreen.kt b/app/src/main/java/chat/revolt/screens/about/AboutScreen.kt index f41e5721..a8f76d22 100644 --- a/app/src/main/java/chat/revolt/screens/about/AboutScreen.kt +++ b/app/src/main/java/chat/revolt/screens/about/AboutScreen.kt @@ -11,7 +11,6 @@ import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign @@ -50,7 +49,9 @@ fun VersionItem( Row(modifier) { Text( text = key, - color = Color(0xccffffff), + color = MaterialTheme.colorScheme.onBackground.copy( + alpha = 0.5f + ), style = MaterialTheme.typography.titleMedium.copy( textAlign = TextAlign.Center, fontWeight = FontWeight.Bold @@ -60,7 +61,9 @@ fun VersionItem( ) Text( text = value, - color = Color(0xccffffff), + color = MaterialTheme.colorScheme.onBackground.copy( + alpha = 0.5f + ), style = MaterialTheme.typography.titleMedium.copy( textAlign = TextAlign.Center, fontWeight = FontWeight.Normal @@ -117,7 +120,9 @@ fun AboutScreen( if (viewModel.root.value == null) { Text( text = stringResource(R.string.loading), - color = Color(0xaaffffff), + color = MaterialTheme.colorScheme.onBackground.copy( + alpha = 0.5f + ), style = MaterialTheme.typography.titleMedium.copy( textAlign = TextAlign.Center, fontWeight = FontWeight.Normal diff --git a/app/src/main/java/chat/revolt/screens/about/AttributionScreen.kt b/app/src/main/java/chat/revolt/screens/about/AttributionScreen.kt index 6180b025..6ab01fbd 100644 --- a/app/src/main/java/chat/revolt/screens/about/AttributionScreen.kt +++ b/app/src/main/java/chat/revolt/screens/about/AttributionScreen.kt @@ -16,7 +16,6 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController import chat.revolt.R -import chat.revolt.ui.theme.DarkColorScheme import com.mikepenz.aboutlibraries.ui.compose.LibrariesContainer import com.mikepenz.aboutlibraries.ui.compose.LibraryDefaults @@ -39,10 +38,10 @@ fun AttributionScreen(navController: NavController) { .fillMaxSize() .weight(1f), colors = LibraryDefaults.libraryColors( - backgroundColor = DarkColorScheme.background, - contentColor = DarkColorScheme.onBackground, - badgeBackgroundColor = DarkColorScheme.primary, - badgeContentColor = DarkColorScheme.onPrimary + backgroundColor = MaterialTheme.colorScheme.background, + contentColor = MaterialTheme.colorScheme.onBackground, + badgeBackgroundColor = MaterialTheme.colorScheme.primary, + badgeContentColor = MaterialTheme.colorScheme.onPrimary ) ) Button( diff --git a/app/src/main/java/chat/revolt/screens/about/PlaceholderScreen.kt b/app/src/main/java/chat/revolt/screens/about/PlaceholderScreen.kt index 13268997..460b2a54 100644 --- a/app/src/main/java/chat/revolt/screens/about/PlaceholderScreen.kt +++ b/app/src/main/java/chat/revolt/screens/about/PlaceholderScreen.kt @@ -7,7 +7,6 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign @@ -49,10 +48,8 @@ fun PlaceholderScreen( Text( text = stringResource(R.string.comingsoon_body), - color = Color(0xaaffffff), - style = MaterialTheme.typography.titleMedium.copy( - textAlign = TextAlign.Center, - fontWeight = FontWeight.Normal, + color = MaterialTheme.colorScheme.onBackground.copy( + alpha = 0.5f ), modifier = Modifier .padding(horizontal = 20.dp, vertical = 10.dp) diff --git a/app/src/main/java/chat/revolt/screens/chat/views/ChannelScreen.kt b/app/src/main/java/chat/revolt/screens/chat/views/ChannelScreen.kt index def801ad..8929c977 100644 --- a/app/src/main/java/chat/revolt/screens/chat/views/ChannelScreen.kt +++ b/app/src/main/java/chat/revolt/screens/chat/views/ChannelScreen.kt @@ -64,9 +64,9 @@ class ChannelScreenViewModel : ViewModel() { val channel: Channel? get() = _channel - private var _callbacks = mutableStateOf(null) - val callbacks: RealtimeSocket.ChannelCallback? - get() = _callbacks.value + private var _callback = mutableStateOf(null) + val callback: RealtimeSocket.ChannelCallback? + get() = _callback.value private var _renderableMessages = mutableStateListOf() val renderableMessages: List @@ -164,8 +164,8 @@ class ChannelScreenViewModel : ViewModel() { } private fun registerCallback() { - _callbacks.value = ChannelScreenCallback() - RealtimeSocket.registerChannelCallback(channel!!.id!!, callbacks!!) + _callback.value = ChannelScreenCallback() + RealtimeSocket.registerChannelCallback(channel!!.id!!, callback!!) } fun fetchMessages() { @@ -399,9 +399,7 @@ fun ChannelScreen( DisposableEffect(channelId) { onDispose { - viewModel.callbacks?.let { - RealtimeSocket.unregisterChannelCallback(channelId, it) - } + RealtimeSocket.unregisterChannelCallback(channelId) } } diff --git a/app/src/main/java/chat/revolt/screens/login/GreeterScreen.kt b/app/src/main/java/chat/revolt/screens/login/GreeterScreen.kt index 2b2df01a..9cf1e169 100644 --- a/app/src/main/java/chat/revolt/screens/login/GreeterScreen.kt +++ b/app/src/main/java/chat/revolt/screens/login/GreeterScreen.kt @@ -12,7 +12,6 @@ import androidx.compose.material3.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource @@ -84,7 +83,9 @@ fun GreeterScreen(navController: NavController) { Text( text = stringResource(R.string.login_onboarding_body), - color = Color(0xaaffffff), + color = MaterialTheme.colorScheme.onBackground.copy( + alpha = 0.5f + ), style = MaterialTheme.typography.titleMedium.copy( textAlign = TextAlign.Center, fontWeight = FontWeight.Normal, diff --git a/app/src/main/java/chat/revolt/screens/login/MfaScreen.kt b/app/src/main/java/chat/revolt/screens/login/MfaScreen.kt index 017587c2..9bf992c8 100644 --- a/app/src/main/java/chat/revolt/screens/login/MfaScreen.kt +++ b/app/src/main/java/chat/revolt/screens/login/MfaScreen.kt @@ -14,7 +14,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardType @@ -169,7 +168,9 @@ fun MfaScreen( Text( text = stringResource(R.string.mfa_interstitial_lead), - color = Color(0xaaffffff), + color = MaterialTheme.colorScheme.onBackground.copy( + alpha = 0.5f + ), style = MaterialTheme.typography.titleMedium.copy( textAlign = TextAlign.Center, fontWeight = FontWeight.Normal, @@ -206,7 +207,9 @@ fun MfaScreen( ) { Text( text = stringResource(R.string.mfa_totp_lead), - color = Color(0xaaffffff), + color = MaterialTheme.colorScheme.onBackground.copy( + alpha = 0.5f + ), style = MaterialTheme.typography.titleMedium.copy( textAlign = TextAlign.Center, fontWeight = FontWeight.Normal, @@ -244,7 +247,9 @@ fun MfaScreen( ) { Text( text = stringResource(R.string.mfa_recovery_lead), - color = Color(0xaaffffff), + color = MaterialTheme.colorScheme.onBackground.copy( + alpha = 0.5f + ), style = MaterialTheme.typography.titleMedium.copy( textAlign = TextAlign.Center, fontWeight = FontWeight.Normal, diff --git a/app/src/main/java/chat/revolt/screens/settings/AppearanceSettingsScreen.kt b/app/src/main/java/chat/revolt/screens/settings/AppearanceSettingsScreen.kt new file mode 100644 index 00000000..4b4c6405 --- /dev/null +++ b/app/src/main/java/chat/revolt/screens/settings/AppearanceSettingsScreen.kt @@ -0,0 +1,145 @@ +package chat.revolt.screens.settings + +import android.widget.Toast +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavController +import chat.revolt.R +import chat.revolt.api.settings.GlobalState +import chat.revolt.api.settings.SyncedSettings +import chat.revolt.components.generic.PageHeader +import chat.revolt.components.screens.settings.appearance.ThemeChip +import chat.revolt.ui.theme.Theme +import chat.revolt.ui.theme.systemSupportsDynamicColors +import com.google.accompanist.flowlayout.FlowRow +import kotlinx.coroutines.launch + +class AppearanceSettingsScreenViewModel : ViewModel() { + fun saveNewTheme(theme: Theme) { + viewModelScope.launch { + val android = SyncedSettings.android + android.theme = theme.toString() + SyncedSettings.updateAndroid(android) + } + } +} + +@Composable +fun AppearanceSettingsScreen( + navController: NavController, + viewModel: AppearanceSettingsScreenViewModel = viewModel() +) { + val context = LocalContext.current + + fun setNewTheme(theme: Theme) { + GlobalState.setTheme(theme) + viewModel.saveNewTheme(theme) + } + + Column( + modifier = Modifier + .fillMaxSize() + ) { + PageHeader( + text = stringResource(id = R.string.settings_appearance), + showBackButton = true, + onBackButtonClicked = { + navController.popBackStack() + }) + + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(20.dp) + ) { + Text( + text = stringResource(id = R.string.settings_appearance_theme), + style = MaterialTheme.typography.headlineSmall, + modifier = Modifier.padding(bottom = 10.dp) + ) + + + FlowRow( + mainAxisSpacing = 10.dp, + crossAxisSpacing = 10.dp, + ) { + ThemeChip( + color = Color(0xff172333), + text = stringResource(id = R.string.settings_appearance_theme_revolt), + selected = GlobalState.theme == Theme.Revolt, + modifier = Modifier.weight(1f), + ) { + setNewTheme(Theme.Revolt) + } + + ThemeChip( + color = Color(0xfff7f7f7), + text = stringResource(id = R.string.settings_appearance_theme_light), + selected = GlobalState.theme == Theme.Light, + modifier = Modifier.weight(1f), + ) { + setNewTheme(Theme.Light) + } + + ThemeChip( + color = Color(0xff000000), + text = stringResource(id = R.string.settings_appearance_theme_amoled), + selected = GlobalState.theme == Theme.Amoled, + modifier = Modifier.weight(1f), + ) { + setNewTheme(Theme.Amoled) + } + + ThemeChip( + color = if (isSystemInDarkTheme()) Color(0xff172333) else Color(0xfff7f7f7), + text = stringResource(id = R.string.settings_appearance_theme_none), + selected = GlobalState.theme == Theme.None, + modifier = Modifier.weight(1f), + ) { + setNewTheme(Theme.None) + } + + if (systemSupportsDynamicColors()) { + ThemeChip( + color = dynamicDarkColorScheme(LocalContext.current).primary, + text = stringResource(id = R.string.settings_appearance_theme_m3dynamic), + selected = GlobalState.theme == Theme.M3Dynamic, + modifier = Modifier.weight(1f), + ) { + setNewTheme(Theme.M3Dynamic) + } + } else { + ThemeChip( + color = Color(0xffa0a0a0), + text = stringResource(id = R.string.settings_appearance_theme_m3dynamic_unsupported), + selected = false, + modifier = Modifier.weight(1f), + ) { + Toast.makeText( + context, + context.getString(R.string.settings_appearance_theme_m3dynamic_unsupported_toast), + Toast.LENGTH_SHORT + ).show() + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/screens/settings/SettingsScreen.kt b/app/src/main/java/chat/revolt/screens/settings/SettingsScreen.kt index 4455c08b..115ce6b6 100644 --- a/app/src/main/java/chat/revolt/screens/settings/SettingsScreen.kt +++ b/app/src/main/java/chat/revolt/screens/settings/SettingsScreen.kt @@ -2,16 +2,20 @@ package chat.revolt.screens.settings import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.material3.Button -import androidx.compose.material3.ElevatedButton +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Info +import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp import androidx.navigation.NavController import chat.revolt.R import chat.revolt.components.generic.PageHeader +import chat.revolt.components.screens.settings.SettingsCategory @Composable fun SettingsScreen( @@ -21,20 +25,51 @@ fun SettingsScreen( modifier = Modifier .fillMaxSize() ) { - PageHeader(stringResource(id = R.string.settings)) - ElevatedButton( - modifier = Modifier.fillMaxWidth(), - onClick = { + PageHeader( + text = stringResource(id = R.string.settings), + showBackButton = true, + onBackButtonClicked = { navController.popBackStack() - }) { - Text(text = stringResource(id = R.string.back)) - } - Button( - modifier = Modifier.fillMaxWidth(), - onClick = { + }) + + Column( + modifier = Modifier + .fillMaxSize() + .padding(10.dp) + ) { + SettingsCategory( + icon = { modifier -> + Icon( + painter = painterResource(id = R.drawable.ic_palette_24dp), + contentDescription = + stringResource(id = R.string.settings_appearance), + modifier = modifier + ) + }, + label = { textStyle -> + Text( + text = stringResource(id = R.string.settings_appearance), + style = textStyle + ) + }) + { + navController.navigate("settings/appearance") + } + + SettingsCategory( + icon = { modifier -> + Icon( + imageVector = Icons.Default.Info, + contentDescription = stringResource(id = R.string.about), + modifier = modifier + ) + }, + label = { textStyle -> + Text(text = stringResource(id = R.string.about), style = textStyle) + }) + { navController.navigate("about") - }) { - Text(text = stringResource(id = R.string.about)) + } } } } \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/ui/theme/Color.kt b/app/src/main/java/chat/revolt/ui/theme/Color.kt deleted file mode 100644 index 04ae03bb..00000000 --- a/app/src/main/java/chat/revolt/ui/theme/Color.kt +++ /dev/null @@ -1,11 +0,0 @@ -package chat.revolt.ui.theme - -import androidx.compose.ui.graphics.Color - -val Purple80 = Color(0xFFD0BCFF) -val PurpleGrey80 = Color(0xFFCCC2DC) -val Pink80 = Color(0xFFEFB8C8) - -val Purple40 = Color(0xFF6650a4) -val PurpleGrey40 = Color(0xFF625b71) -val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/ui/theme/Theme.kt b/app/src/main/java/chat/revolt/ui/theme/Theme.kt index 502537bb..f2aa3a0e 100644 --- a/app/src/main/java/chat/revolt/ui/theme/Theme.kt +++ b/app/src/main/java/chat/revolt/ui/theme/Theme.kt @@ -3,10 +3,7 @@ package chat.revolt.ui.theme import android.app.Activity import android.os.Build import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.darkColorScheme -import androidx.compose.material3.dynamicDarkColorScheme -import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.runtime.SideEffect import androidx.compose.ui.graphics.Color @@ -15,43 +12,78 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalView import androidx.core.view.ViewCompat -const val FORCE_ANDROID_DEFAULTS = false - -const val FOREGROUND = 0xffffffff - -val DarkColorScheme = darkColorScheme( +val RevoltColorScheme = darkColorScheme( primary = Color(0xfffe4654), - onPrimary = Color(FOREGROUND), + onPrimary = Color(0xffffffff), secondary = Color(0xfffd6671), - onSecondary = Color(FOREGROUND), + onSecondary = Color(0xffffffff), tertiary = Color(0xffff6667), - onTertiary = Color(FOREGROUND), + onTertiary = Color(0xffffffff), background = Color(0xff101823), - onBackground = Color(FOREGROUND), + onBackground = Color(0xffffffff), surfaceVariant = Color(0xff172333), - onSurfaceVariant = Color(FOREGROUND), + onSurfaceVariant = Color(0xffffffff), surface = Color(0xff111a26), - onSurface = Color(FOREGROUND), + onSurface = Color(0xffffffff), ) +val AmoledColorScheme = RevoltColorScheme.copy( + background = Color(0xff000000), + onBackground = Color(0xffffffff), + surfaceVariant = Color(0xff131313), + onSurfaceVariant = Color(0xffffffff), + surface = Color(0xff212121), + onSurface = Color(0xffffffff), +) + +val LightColorScheme = lightColorScheme( + primary = Color(0xfffe4654), + onPrimary = Color(0xffffffff), + secondary = Color(0xfffd6671), + onSecondary = Color(0xffffffff), + tertiary = Color(0xffff6667), + onTertiary = Color(0xffffffff), + background = Color(0xffffffff), + onBackground = Color(0xff000000), + surfaceVariant = Color(0xffe6e6e6), + onSurfaceVariant = Color(0xff000000), + surface = Color(0xffdddddd), + onSurface = Color(0xff000000), +) + +enum class Theme { + None, + Revolt, + Light, + M3Dynamic, + Amoled, +} + @Composable fun RevoltTheme( - darkTheme: Boolean = isSystemInDarkTheme(), - // Dynamic color is available on Android 12+ - dynamicColor: Boolean = false, + requestedTheme: Theme, content: @Composable () -> Unit ) { + val context = LocalContext.current + + val systemInDarkTheme = isSystemInDarkTheme() + val m3Supported = systemSupportsDynamicColors() + val colorScheme = when { - dynamicColor && darkTheme && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { - val context = LocalContext.current - dynamicDarkColorScheme(context) - } - dynamicColor && !darkTheme && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { - val context = LocalContext.current - dynamicLightColorScheme(context) - } - else -> DarkColorScheme + m3Supported && requestedTheme == Theme.M3Dynamic && systemInDarkTheme -> dynamicDarkColorScheme( + context + ) + m3Supported && requestedTheme == Theme.M3Dynamic && !systemInDarkTheme -> dynamicLightColorScheme( + context + ) + requestedTheme == Theme.Revolt -> RevoltColorScheme + requestedTheme == Theme.Light -> LightColorScheme + requestedTheme == Theme.Amoled -> AmoledColorScheme + requestedTheme == Theme.None && systemInDarkTheme -> RevoltColorScheme + requestedTheme == Theme.None && !systemInDarkTheme -> LightColorScheme + else -> RevoltColorScheme } + val view = LocalView.current if (!view.isInEditMode) { SideEffect { @@ -62,15 +94,13 @@ fun RevoltTheme( } } - if (FORCE_ANDROID_DEFAULTS) { - MaterialTheme( - content = content - ) - } else { - MaterialTheme( - colorScheme = colorScheme, - typography = RevoltTypography, - content = content - ) - } + MaterialTheme( + colorScheme = colorScheme, + typography = RevoltTypography, + content = content + ) +} + +fun systemSupportsDynamicColors(): Boolean { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S } \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/ui/theme/Typography.kt b/app/src/main/java/chat/revolt/ui/theme/Typography.kt index 262ceb13..fe30ede7 100644 --- a/app/src/main/java/chat/revolt/ui/theme/Typography.kt +++ b/app/src/main/java/chat/revolt/ui/theme/Typography.kt @@ -33,7 +33,7 @@ val RevoltTypography = Typography( ), displaySmall = TextStyle( fontFamily = Inter, - fontWeight = FontWeight.Normal, + fontWeight = FontWeight.Bold, fontSize = 36.sp ), @@ -49,39 +49,39 @@ val RevoltTypography = Typography( ), headlineSmall = TextStyle( fontFamily = Inter, - fontWeight = FontWeight.Medium, + fontWeight = FontWeight.Bold, fontSize = 24.sp ), titleLarge = TextStyle( fontFamily = Inter, - fontWeight = FontWeight.Normal, + fontWeight = FontWeight.SemiBold, fontSize = 22.sp ), titleMedium = TextStyle( fontFamily = Inter, - fontWeight = FontWeight.Medium, + fontWeight = FontWeight.SemiBold, fontSize = 16.sp ), titleSmall = TextStyle( fontFamily = Inter, - fontWeight = FontWeight.Medium, + fontWeight = FontWeight.Bold, fontSize = 14.sp ), labelLarge = TextStyle( fontFamily = Inter, - fontWeight = FontWeight.Medium, + fontWeight = FontWeight.SemiBold, fontSize = 14.sp ), labelMedium = TextStyle( fontFamily = Inter, - fontWeight = FontWeight.Medium, + fontWeight = FontWeight.Bold, fontSize = 12.sp ), labelSmall = TextStyle( fontFamily = Inter, - fontWeight = FontWeight.Medium, + fontWeight = FontWeight.Bold, fontSize = 11.sp ), diff --git a/app/src/main/res/drawable/ic_palette_24dp.xml b/app/src/main/res/drawable/ic_palette_24dp.xml new file mode 100644 index 00000000..64bfe4e5 --- /dev/null +++ b/app/src/main/res/drawable/ic_palette_24dp.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/revolt_logo_wide.xml b/app/src/main/res/drawable/revolt_logo_wide.xml index d8f3de1b..ea3611bc 100644 --- a/app/src/main/res/drawable/revolt_logo_wide.xml +++ b/app/src/main/res/drawable/revolt_logo_wide.xml @@ -6,6 +6,6 @@ + android:fillColor="#ffffff" + android:strokeColor="#ffffff"/> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fb98b6cf..84749554 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -107,4 +107,14 @@ Scroll to bottom Settings + + Appearance + Theme + System + Revolt + Light + Pure Black + Material You + Material You (unsupported) + Material You is not supported on this device. \ No newline at end of file