diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ea33625d614d10af42bf1473442a0adc9ccad7c4..5dc226b3e7236fd9bd43558339e33b336737e8c9 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -68,6 +68,7 @@ dependencies { implementation(libs.androidx.compose.compiler) implementation(libs.androidx.compose.foundation) implementation(libs.androidx.compose.material) + implementation(libs.androidx.compose.material.icons) implementation(libs.androidx.compose.runtime) implementation(libs.androidx.compose.ui) diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/MainActivity.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/MainActivity.kt index ecb4779579a1f7856609e437a03cf10c51af51b2..e3fc553166fe593bfe4323cd6cbee008e452dffa 100644 --- a/app/src/main/kotlin/de/justjanne/quasseldroid/MainActivity.kt +++ b/app/src/main/kotlin/de/justjanne/quasseldroid/MainActivity.kt @@ -7,7 +7,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.Surface import androidx.compose.ui.Modifier import de.justjanne.quasseldroid.service.QuasselBackend -import de.justjanne.quasseldroid.ui.theme.QuasseldroidTheme +import de.justjanne.quasseldroid.ui.theme.QuasselTheme class MainActivity : ComponentActivity() { private val backend = QuasselBackend() @@ -16,7 +16,7 @@ class MainActivity : ComponentActivity() { super.onCreate(savedInstanceState) backend.onCreate(this) setContent { - QuasseldroidTheme { + QuasselTheme { Surface(modifier = Modifier.fillMaxSize()) { QuasseldroidRouter(backend = backend) } diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/model/SecurityLevel.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/model/SecurityLevel.kt new file mode 100644 index 0000000000000000000000000000000000000000..a1c1cb51eb9224cc8e4bf899071c02723b7a86e9 --- /dev/null +++ b/app/src/main/kotlin/de/justjanne/quasseldroid/model/SecurityLevel.kt @@ -0,0 +1,7 @@ +package de.justjanne.quasseldroid.model + +enum class SecurityLevel { + SECURE, + UNVERIFIED, + INSECURE +} diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/sample/SampleSecurityLevelProvider.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/sample/SampleSecurityLevelProvider.kt new file mode 100644 index 0000000000000000000000000000000000000000..ef595a103c909fa2f7fcb0d15dea9d598479ac3c --- /dev/null +++ b/app/src/main/kotlin/de/justjanne/quasseldroid/sample/SampleSecurityLevelProvider.kt @@ -0,0 +1,12 @@ +package de.justjanne.quasseldroid.sample + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import de.justjanne.quasseldroid.model.SecurityLevel + +class SampleSecurityLevelProvider: PreviewParameterProvider<SecurityLevel> { + override val values = sequenceOf( + SecurityLevel.SECURE, + SecurityLevel.UNVERIFIED, + SecurityLevel.INSECURE, + ) +} diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/ConnectedClientCard.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/ConnectedClientCard.kt index 66af09a56424ec699d8bf4be34758115702b306a..4092eeee0cd3351075d8bee72d5d9aeb6b7ef369 100644 --- a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/ConnectedClientCard.kt +++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/ConnectedClientCard.kt @@ -6,20 +6,20 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.material.Card -import androidx.compose.material.Icon +import androidx.compose.material.ContentAlpha +import androidx.compose.material.LocalContentAlpha import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import de.charlex.compose.HtmlText import de.justjanne.libquassel.protocol.models.ConnectedClient -import de.justjanne.quasseldroid.R +import de.justjanne.quasseldroid.model.SecurityLevel import de.justjanne.quasseldroid.sample.SampleConnectedClientProvider -import de.justjanne.quasseldroid.ui.theme.Insecure -import de.justjanne.quasseldroid.ui.theme.Secure import de.justjanne.quasseldroid.ui.theme.Typography import org.threeten.bp.ZoneId import org.threeten.bp.format.DateTimeFormatter @@ -34,33 +34,30 @@ fun ConnectedClientCard( client: ConnectedClient, modifier: Modifier = Modifier ) { - val secureResource = painterResource( - if (client.secure) R.drawable.ic_lock - else R.drawable.ic_no_encryption - ) - - val tint = if (client.secure) Secure else Insecure - Card(modifier = modifier) { Row(modifier = Modifier.padding(16.dp)) { Column(modifier = Modifier.weight(1.0f)) { HtmlText( text = client.version, - style = Typography.body1 - ) - Text( - client.remoteAddress, - style = Typography.body2 - ) - Text( - client.connectedSince - .atZone(ZoneId.systemDefault()) - .format(formatter), - style = Typography.body2 + style = Typography.body1, + overflow = TextOverflow.Ellipsis ) + CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { + Text( + client.remoteAddress, + style = Typography.body2, + overflow = TextOverflow.Ellipsis + ) + Text( + client.connectedSince + .atZone(ZoneId.systemDefault()) + .format(formatter), + style = Typography.body2 + ) + } } Spacer(modifier = Modifier.width(16.dp)) - Icon(secureResource, tint = tint, contentDescription = "") + SecurityIcon(level = if (client.secure) SecurityLevel.SECURE else SecurityLevel.INSECURE) } } } diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/MessageView.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/MessageView.kt new file mode 100644 index 0000000000000000000000000000000000000000..c1424a357c50c082cc6f43a15ace87dba7d4f2f6 --- /dev/null +++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/MessageView.kt @@ -0,0 +1,109 @@ +package de.justjanne.quasseldroid.ui.components + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material.ContentAlpha +import androidx.compose.material.LocalContentAlpha +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import de.justjanne.libquassel.protocol.models.Message +import de.justjanne.libquassel.protocol.util.irc.HostmaskHelper +import de.justjanne.quasseldroid.ui.icons.AvatarIcon +import de.justjanne.quasseldroid.ui.routes.SampleMessageProvider +import de.justjanne.quasseldroid.ui.theme.QuasselTheme +import de.justjanne.quasseldroid.ui.theme.Typography +import irc.SenderColorUtil +import org.threeten.bp.ZoneId +import org.threeten.bp.format.DateTimeFormatter +import org.threeten.bp.format.FormatStyle + +private val formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT) + +@Preview(name = "Message", showBackground = true) +@Composable +fun MessageView( + @PreviewParameter(SampleMessageProvider::class) + message: Message +) { + MessageBaseView(message, false, 32.dp) { + Text( + message.content, + style = Typography.body2, + ) + } +} + +@Composable +fun MessageBaseView( + message: Message, + followUp: Boolean, + avatarSize: Dp, + content: @Composable () -> Unit +) { + val nick = HostmaskHelper.nick(message.sender) + val senderColor = QuasselTheme.sender.colors[SenderColorUtil.senderColor(nick)] + + Row { + if (!followUp) { + AvatarIcon(nick, null, modifier = Modifier.padding(2.dp)) + Spacer(Modifier.width(4.dp)) + } else { + Spacer(Modifier.width(avatarSize + 8.dp)) + } + Column { + if (!followUp) { + Row { + Text( + message.senderPrefixes, + style = Typography.body2, + fontWeight = FontWeight.Bold, + ) + Text( + nick, + style = Typography.body2, + fontWeight = FontWeight.Bold, + color = senderColor, + ) + Spacer(Modifier.width(4.dp)) + CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { + Text( + message.realName, + modifier = Modifier.weight(1.0f), + style = Typography.body2, + overflow = TextOverflow.Ellipsis, + ) + } + } + } + Row { + Box(modifier = Modifier.weight(1.0f)) { + content() + } + CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { + Text( + message.time + .atZone(ZoneId.systemDefault()) + .format(formatter), + style = Typography.body2, + fontSize = 12.sp, + modifier = Modifier.align(Alignment.Bottom) + ) + } + } + } + } +} diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/PasswordTextField.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/PasswordTextField.kt index 978fa20da8d9c03a52d60d96360d3247d42fda05..fba9e2fdbf112b744f1a1c527983d4366745c8ca 100644 --- a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/PasswordTextField.kt +++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/PasswordTextField.kt @@ -10,6 +10,9 @@ import androidx.compose.material.MaterialTheme import androidx.compose.material.OutlinedTextField import androidx.compose.material.TextFieldColors import androidx.compose.material.TextFieldDefaults +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Visibility +import androidx.compose.material.icons.filled.VisibilityOff import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -21,8 +24,16 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.tooling.preview.Preview import de.justjanne.quasseldroid.R +@Preview(name = "PasswordTextField", showBackground = true) +@Composable +private fun PasswordTextFieldPreview() { + val (password, setPassword) = remember { mutableStateOf(TextFieldValue("password")) } + PasswordTextField(password, setPassword) +} + @Composable fun PasswordTextField( value: TextFieldValue, @@ -47,10 +58,9 @@ fun PasswordTextField( colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors() ) { val (showPassword, setShowPassword) = remember { mutableStateOf(false) } - val painter = painterResource( - if (showPassword) R.drawable.ic_eye_off - else R.drawable.ic_eye - ) + val icon = + if (showPassword) Icons.Filled.VisibilityOff + else Icons.Filled.Visibility OutlinedTextField( value, @@ -64,7 +74,7 @@ fun PasswordTextField( leadingIcon, { IconButton(onClick = { setShowPassword(!showPassword) }) { - Icon(painter = painter, contentDescription = "") + Icon(imageVector = icon, contentDescription = "") } trailingIcon?.invoke() }, @@ -105,10 +115,9 @@ fun PasswordTextField( colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors() ) { val (showPassword, setShowPassword) = remember { mutableStateOf(false) } - val painter = painterResource( - if (showPassword) R.drawable.ic_eye_off - else R.drawable.ic_eye - ) + val icon = + if (showPassword) Icons.Filled.VisibilityOff + else Icons.Filled.Visibility OutlinedTextField( value, @@ -122,7 +131,7 @@ fun PasswordTextField( leadingIcon, { IconButton(onClick = { setShowPassword(!showPassword) }) { - Icon(painter = painter, contentDescription = "") + Icon(imageVector = icon, contentDescription = "") } trailingIcon?.invoke() }, diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/icons/AvatarIcon.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/icons/AvatarIcon.kt new file mode 100644 index 0000000000000000000000000000000000000000..e6ac4bd99df36f9a93c6c768bae8afb2c704893f --- /dev/null +++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/icons/AvatarIcon.kt @@ -0,0 +1,57 @@ +package de.justjanne.quasseldroid.ui.icons + +import android.graphics.Bitmap +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.ContentAlpha +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalDensity +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.dp +import de.justjanne.quasseldroid.ui.theme.QuasselTheme +import irc.SenderColorUtil +import java.util.* + +@Preview +@Composable +private fun AvatarIconPreview() { + AvatarIcon("justJanne", null) +} + +@Composable +fun AvatarIcon( + nick: String, + avatar: Bitmap?, + modifier: Modifier = Modifier, + size: Dp = 32.dp +) { + val senderColor = QuasselTheme.sender.colors[SenderColorUtil.senderColor(nick)] + val initial = nick.asSequence().map { it.uppercase(Locale.ENGLISH) }.first() + val fontSize = with(LocalDensity.current) { (size.toPx() * 0.67f).toSp() } + + Surface( + shape = RoundedCornerShape(2.dp), + color = senderColor, + modifier = modifier.size(size) + ) { + Box { + Text( + text = initial, + color = MaterialTheme.colors.background.copy(alpha = ContentAlpha.medium), + textAlign = TextAlign.Center, + fontWeight = FontWeight.Bold, + modifier = Modifier.align(Alignment.Center), + fontSize = fontSize, + ) + } + } +} diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/icons/SecurityIcon.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/icons/SecurityIcon.kt new file mode 100644 index 0000000000000000000000000000000000000000..09d580ae80434574d188d24819a04062e1d9546b --- /dev/null +++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/icons/SecurityIcon.kt @@ -0,0 +1,34 @@ +package de.justjanne.quasseldroid.ui.components + +import androidx.compose.material.Icon +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Lock +import androidx.compose.material.icons.filled.LockOpen +import androidx.compose.material.icons.filled.NoEncryption +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import de.justjanne.quasseldroid.model.SecurityLevel +import de.justjanne.quasseldroid.sample.SampleSecurityLevelProvider +import de.justjanne.quasseldroid.ui.theme.QuasselTheme + +@Preview(name = "Security Icon", showBackground = true) +@Composable +fun SecurityIcon( + @PreviewParameter(SampleSecurityLevelProvider::class) + level: SecurityLevel +) { + val vector = when (level) { + SecurityLevel.SECURE -> Icons.Filled.Lock + SecurityLevel.UNVERIFIED -> Icons.Filled.LockOpen + SecurityLevel.INSECURE -> Icons.Filled.NoEncryption + } + + val color = when (level) { + SecurityLevel.SECURE -> QuasselTheme.security.secure + SecurityLevel.UNVERIFIED -> QuasselTheme.security.unverified + SecurityLevel.INSECURE -> QuasselTheme.security.insecure + } + + Icon(imageVector = vector, contentDescription = level.name, tint = color) +} diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/routes/HomeRoute.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/routes/HomeRoute.kt index 436ed14ff573a75b60626c49f7d754a17680d149..09ee1010faed3e51ef9e5b80037ba306c2764672 100644 --- a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/routes/HomeRoute.kt +++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/routes/HomeRoute.kt @@ -1,9 +1,6 @@ package de.justjanne.quasseldroid.ui.routes import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.Button @@ -11,39 +8,17 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable -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.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import androidx.navigation.NavController -import de.justjanne.bitflags.of -import de.justjanne.libquassel.protocol.models.BufferInfo import de.justjanne.libquassel.protocol.models.Message -import de.justjanne.libquassel.protocol.models.flags.BufferType -import de.justjanne.libquassel.protocol.models.flags.MessageFlag -import de.justjanne.libquassel.protocol.models.flags.MessageType import de.justjanne.libquassel.protocol.models.ids.BufferId -import de.justjanne.libquassel.protocol.models.ids.MsgId -import de.justjanne.libquassel.protocol.models.ids.NetworkId import de.justjanne.libquassel.protocol.util.flatMap -import de.justjanne.libquassel.protocol.util.irc.HostmaskHelper import de.justjanne.quasseldroid.service.QuasselBackend -import de.justjanne.quasseldroid.ui.theme.SenderColors -import de.justjanne.quasseldroid.ui.theme.Typography -import irc.SenderColorUtil +import de.justjanne.quasseldroid.ui.components.MessageView import de.justjanne.quasseldroid.util.mapNullable import de.justjanne.quasseldroid.util.rememberFlow import de.justjanne.quasseldroid.util.saver.BufferIdSaver import kotlinx.coroutines.flow.map -import org.threeten.bp.Instant -import org.threeten.bp.ZoneId -import org.threeten.bp.format.DateTimeFormatter -import org.threeten.bp.format.FormatStyle @Composable fun HomeRoute(backend: QuasselBackend, navController: NavController) { @@ -96,125 +71,3 @@ fun HomeRoute(backend: QuasselBackend, navController: NavController) { } -private val formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT) - -@Preview(name = "Message", showBackground = true) -@Composable -fun MessageView( - @PreviewParameter(SampleMessageProvider::class) - message: Message -) { - val nick = HostmaskHelper.nick(message.sender) - val senderColor = SenderColors[SenderColorUtil.senderColor(nick)] - - Column { - Row { - Text( - message.senderPrefixes, - style = Typography.body2, - fontWeight = FontWeight.Bold - ) - Text( - nick, - style = Typography.body2, - fontWeight = FontWeight.Bold, - color = senderColor - ) - Spacer(Modifier.width(4.dp)) - Text( - message.realName, - modifier = Modifier.weight(1.0f), - style = Typography.body2, - color = Color(0x8A000000) - ) - } - Row { - Text( - message.content, - modifier = Modifier.weight(1.0f), - style = Typography.body2 - ) - Text( - message.time - .atZone(ZoneId.systemDefault()) - .format(formatter), - style = Typography.body2, - color = Color(0x8A000000), - fontSize = 12.sp - ) - } - } -} - -class SampleMessageProvider : PreviewParameterProvider<Message> { - override val values = sequenceOf( - Message( - messageId = MsgId(108062924), - bufferInfo = BufferInfo( - bufferId = BufferId(3746), - bufferName = "#quasseldroid", - networkId = NetworkId(4), - type = BufferType.of(BufferType.Channel) - ), - time = Instant.parse("2022-02-20T18:24:48.891Z"), - type = MessageType.of(MessageType.Quit), - sender = "CrazyBonz!~CrazyBonz@user/CrazyBonz", - senderPrefixes = "", - avatarUrl = "", - realName = "CrazyBonz", - content = "#quasseldroid", - flag = MessageFlag.of() - ), - Message( - messageId = MsgId(108063975), - bufferInfo = BufferInfo( - bufferId = BufferId(3746), - bufferName = "#quasseldroid", - networkId = NetworkId(4), - type = BufferType.of(BufferType.Channel) - ), - time = Instant.parse("2022-02-20T19:56:01.588Z"), - type = MessageType.of(MessageType.Plain), - sender = "winch!~AdminUser@185.14.29.13", - senderPrefixes = "", - avatarUrl = "", - realName = "Wincher,,,", - content = "Can i script some actions like in mIRC?", - flag = MessageFlag.of() - ), - Message( - messageId = MsgId(108064014), - bufferInfo = BufferInfo( - bufferId = BufferId(3746), - bufferName = "#quasseldroid", - networkId = NetworkId(4), - type = BufferType.of(BufferType.Channel) - ), - time = Instant.parse("2022-02-20T20:06:39.159Z"), - type = MessageType.of(MessageType.Quit), - sender = "mavhq!~quassel@mapp-14-b2-v4wan-161519-cust401.vm15.cable.virginm.net", - senderPrefixes = "", - avatarUrl = "", - realName = "mavhc", - content = "Quit: http://quassel-irc.org - Chat comfortably. Anywhere.", - flag = MessageFlag.of() - ), - Message( - messageId = MsgId(108064022), - bufferInfo = BufferInfo( - bufferId = BufferId(3746), - bufferName = "#quasseldroid", - networkId = NetworkId(4), - type = BufferType.of(BufferType.Channel) - ), - time = Instant.parse("2022-02-20T20:07:13.45Z"), - type = MessageType.of(MessageType.Join), - sender = "mavhq!~quassel@mapp-14-b2-v4wan-161519-cust401.vm15.cable.virginm.net", - senderPrefixes = "", - avatarUrl = "", - realName = "mavhc", - content = "#quasseldroid", - flag = MessageFlag.of() - ) - ) -} diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/routes/SampleMessageProvider.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/routes/SampleMessageProvider.kt new file mode 100644 index 0000000000000000000000000000000000000000..ff30e9f8fc0ef879e35d45aceaf9d66672af8354 --- /dev/null +++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/routes/SampleMessageProvider.kt @@ -0,0 +1,86 @@ +package de.justjanne.quasseldroid.ui.routes + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import de.justjanne.bitflags.of +import de.justjanne.libquassel.protocol.models.BufferInfo +import de.justjanne.libquassel.protocol.models.Message +import de.justjanne.libquassel.protocol.models.flags.BufferType +import de.justjanne.libquassel.protocol.models.flags.MessageFlag +import de.justjanne.libquassel.protocol.models.flags.MessageType +import de.justjanne.libquassel.protocol.models.ids.BufferId +import de.justjanne.libquassel.protocol.models.ids.MsgId +import de.justjanne.libquassel.protocol.models.ids.NetworkId +import org.threeten.bp.Instant + +class SampleMessageProvider : PreviewParameterProvider<Message> { + override val values = sequenceOf( + Message( + messageId = MsgId(108062924), + bufferInfo = BufferInfo( + bufferId = BufferId(3746), + bufferName = "#quasseldroid", + networkId = NetworkId(4), + type = BufferType.of(BufferType.Channel) + ), + time = Instant.parse("2022-02-20T18:24:48.891Z"), + type = MessageType.of(MessageType.Quit), + sender = "CrazyBonz!~CrazyBonz@user/CrazyBonz", + senderPrefixes = "", + avatarUrl = "", + realName = "CrazyBonz", + content = "#quasseldroid", + flag = MessageFlag.of() + ), + Message( + messageId = MsgId(108063975), + bufferInfo = BufferInfo( + bufferId = BufferId(3746), + bufferName = "#quasseldroid", + networkId = NetworkId(4), + type = BufferType.of(BufferType.Channel) + ), + time = Instant.parse("2022-02-20T19:56:01.588Z"), + type = MessageType.of(MessageType.Plain), + sender = "winch!~AdminUser@185.14.29.13", + senderPrefixes = "", + avatarUrl = "", + realName = "Wincher,,,", + content = "Can i script some actions like in mIRC?", + flag = MessageFlag.of() + ), + Message( + messageId = MsgId(108064014), + bufferInfo = BufferInfo( + bufferId = BufferId(3746), + bufferName = "#quasseldroid", + networkId = NetworkId(4), + type = BufferType.of(BufferType.Channel) + ), + time = Instant.parse("2022-02-20T20:06:39.159Z"), + type = MessageType.of(MessageType.Quit), + sender = "mavhq!~quassel@mapp-14-b2-v4wan-161519-cust401.vm15.cable.virginm.net", + senderPrefixes = "", + avatarUrl = "", + realName = "mavhc", + content = "Quit: http://quassel-irc.org - Chat comfortably. Anywhere.", + flag = MessageFlag.of() + ), + Message( + messageId = MsgId(108064022), + bufferInfo = BufferInfo( + bufferId = BufferId(3746), + bufferName = "#quasseldroid", + networkId = NetworkId(4), + type = BufferType.of(BufferType.Channel) + ), + time = Instant.parse("2022-02-20T20:07:13.45Z"), + type = MessageType.of(MessageType.Join), + sender = "mavhq!~quassel@mapp-14-b2-v4wan-161519-cust401.vm15.cable.virginm.net", + senderPrefixes = "", + avatarUrl = "", + realName = "mavhc", + content = "#quasseldroid", + flag = MessageFlag.of() + ) + ) +} diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/Color.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/Color.kt deleted file mode 100644 index 1460baf88003fe3b174eefe3c464b5439a7e732b..0000000000000000000000000000000000000000 --- a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/Color.kt +++ /dev/null @@ -1,30 +0,0 @@ -package de.justjanne.quasseldroid.ui.theme - -import androidx.compose.ui.graphics.Color - -val Primary = Color(0xFF0a70c0) -val PrimaryDark = Color(0xFF105a94) -val Accent = Color(0xFFffaf3b) - -val Secure = Color(0xFF4CAF50) -val PartiallySecure = Color(0xFFFFC107) -val Insecure = Color(0xFFD32F2F) - -val SenderColors = listOf( - Color(0xFF_b80a73), - Color(0xFF_814dd5), - Color(0xFF_9f0b0b), - Color(0xFF_139f2f), - Color(0xFF_4e9c9f), - Color(0xFF_8b4a9f), - Color(0xFF_9f8669), - Color(0xFF_2b6b9f), - Color(0xFF_e00d8f), - Color(0xFF_995bfd), - Color(0xFF_c70e0e), - Color(0xFF_18c73e), - Color(0xFF_61c6c7), - Color(0xFF_af5ec7), - Color(0xFF_c7a782), - Color(0xFF_3684c7), -) diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/Theme.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/Theme.kt index b12c3825b2f5be26fbdcf09f28d208e0913e424b..4b29b45f73f84b4982975d37304b37fe0f2c7cf7 100644 --- a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/Theme.kt +++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/Theme.kt @@ -1,44 +1,74 @@ package de.justjanne.quasseldroid.ui.theme import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material.Colors import androidx.compose.material.MaterialTheme import androidx.compose.material.darkColors import androidx.compose.material.lightColors import androidx.compose.runtime.Composable - -private val DarkColorPalette = darkColors( - primary = Primary, - primaryVariant = PrimaryDark, - secondary = Accent -) - -private val LightColorPalette = lightColors( - primary = Primary, - primaryVariant = PrimaryDark, - secondary = Accent - - /* Other default colors to override - background = Color.White, - surface = Color.White, - onPrimary = Color.White, - onSecondary = Color.Black, - onBackground = Color.Black, - onSurface = Color.Black, - */ -) +import androidx.compose.runtime.CompositionLocalProvider +import de.justjanne.quasseldroid.ui.theme.quassel.ActivityColors +import de.justjanne.quasseldroid.ui.theme.quassel.ChatColors +import de.justjanne.quasseldroid.ui.theme.quassel.LocalActivityColors +import de.justjanne.quasseldroid.ui.theme.quassel.LocalChatColors +import de.justjanne.quasseldroid.ui.theme.quassel.LocalMircColors +import de.justjanne.quasseldroid.ui.theme.quassel.LocalSecurityColors +import de.justjanne.quasseldroid.ui.theme.quassel.LocalSenderColors +import de.justjanne.quasseldroid.ui.theme.quassel.MircColors +import de.justjanne.quasseldroid.ui.theme.quassel.SecurityColors +import de.justjanne.quasseldroid.ui.theme.quassel.SenderColors +import de.justjanne.quasseldroid.ui.theme.quassel.UiColors @Composable -fun QuasseldroidTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { - val colors = if (darkTheme) { - DarkColorPalette - } else { - LightColorPalette - } - +fun QuasselTheme( + dark: Boolean = isSystemInDarkTheme(), + ui: UiColors = UiColors.Default, + chat: ChatColors = ChatColors.Default, + activity: ActivityColors = ActivityColors.Default, + security: SecurityColors = SecurityColors.Default, + sender: SenderColors = SenderColors.Default, + mirc: MircColors = MircColors.Default, + content: @Composable () -> Unit +) { MaterialTheme( - colors = colors, + colors = buildColors(dark, ui), typography = Typography, - shapes = Shapes, - content = content + shapes = Shapes + ) { + CompositionLocalProvider( + LocalChatColors provides chat, + LocalActivityColors provides activity, + LocalSecurityColors provides security, + LocalSenderColors provides sender, + LocalMircColors provides mirc, + content = content + ) + } +} + +private fun buildColors(dark: Boolean, ui: UiColors): Colors = if (dark) { + darkColors( + primary = ui.primary, + primaryVariant = ui.primaryVariant, + secondary = ui.secondary, + ) +} else { + lightColors( + primary = ui.primary, + primaryVariant = ui.primaryVariant, + secondary = ui.secondary, ) } + +object QuasselTheme { + val chat: ChatColors + @Composable get() = LocalChatColors.current + val activity: ActivityColors + @Composable get() = LocalActivityColors.current + val security: SecurityColors + @Composable get() = LocalSecurityColors.current + val sender: SenderColors + @Composable get() = LocalSenderColors.current + val mirc: MircColors + @Composable get() = LocalMircColors.current +} diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/ActivityColors.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/ActivityColors.kt new file mode 100644 index 0000000000000000000000000000000000000000..ae7de444eaee87203e262451ae16434b0a60685c --- /dev/null +++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/ActivityColors.kt @@ -0,0 +1,24 @@ +package de.justjanne.quasseldroid.ui.theme.quassel + +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.compositionLocalOf +import androidx.compose.ui.graphics.Color + +val LocalActivityColors = compositionLocalOf { ActivityColors.Default } + +@Immutable +data class ActivityColors( + val activity: Color, + val message: Color, + val highlight: Color, + val notification: Color, +) { + companion object { + val Default = ActivityColors( + activity = Color(0xffafb42b), + message = Color(0xff1976d2), + highlight = Color(0xffffab00), + notification = Color(0xffd32f2f), + ) + } +} diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/ChatColors.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/ChatColors.kt new file mode 100644 index 0000000000000000000000000000000000000000..9f17ac7e85b5144379f12fa938b80928ef63fc2c --- /dev/null +++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/ChatColors.kt @@ -0,0 +1,32 @@ +package de.justjanne.quasseldroid.ui.theme.quassel + +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.compositionLocalOf +import androidx.compose.ui.graphics.Color + +val LocalChatColors = compositionLocalOf { ChatColors.Default } + +@Immutable +data class ChatColors( + val action: Color, + val onAction: Color, + val notice: Color, + val onNotice: Color, + val highlight: Color, + val onHighlight: Color, + val error: Color, + val onError: Color, +) { + companion object { + val Default = ChatColors( + action = Color(0x00000000), + onAction = Color(0xff01579b), + notice = Color(0x00000000), + onNotice = Color(0xffb56a00), + highlight = Color(0x40ffaf3b), + onHighlight = Color(0xde000000), + error = Color(0x00000000), + onError = Color(0xffb71c1c), + ) + } +} diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/MircColors.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/MircColors.kt new file mode 100644 index 0000000000000000000000000000000000000000..0ac2933c066f6335041509de9f4cd7aacf65818b --- /dev/null +++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/MircColors.kt @@ -0,0 +1,131 @@ +package de.justjanne.quasseldroid.ui.theme.quassel + +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.compositionLocalOf +import androidx.compose.ui.graphics.Color + +val LocalMircColors = compositionLocalOf { MircColors.Default } + +@Immutable +data class MircColors( + val colors: List<Color>, +) { + companion object { + val Default = MircColors( + colors = listOf( + Color(0xffffffff), + Color(0xff000000), + Color(0xff000080), + Color(0xff008000), + Color(0xffff0000), + Color(0xff800000), + Color(0xff800080), + Color(0xffffa500), + Color(0xffffff00), + Color(0xff00ff00), + Color(0xff008080), + Color(0xff00ffff), + Color(0xff4169e1), + Color(0xffff00ff), + Color(0xff808080), + Color(0xffc0c0c0), + + Color(0xff470000), + Color(0xff740000), + Color(0xffb50000), + Color(0xffff0000), + Color(0xffff5959), + Color(0xffff9c9c), + + Color(0xff472100), + Color(0xff743a00), + Color(0xffb56300), + Color(0xffff8c00), + Color(0xffffb459), + Color(0xffffd39c), + + Color(0xff474700), + Color(0xff747400), + Color(0xffb5b500), + Color(0xffffff00), + Color(0xffffff71), + Color(0xffffff9c), + + Color(0xff324700), + Color(0xff517400), + Color(0xff7db500), + Color(0xffb2ff00), + Color(0xffcfff60), + Color(0xffe2ff9c), + + Color(0xff004700), + Color(0xff007400), + Color(0xff00b500), + Color(0xff00ff00), + Color(0xff6fff6f), + Color(0xff9cff9c), + + Color(0xff00472c), + Color(0xff007449), + Color(0xff00b571), + Color(0xff00ffa0), + Color(0xff65ffc9), + Color(0xff9cffdb), + + Color(0xff004747), + Color(0xff007474), + Color(0xff00b5b5), + Color(0xff00ffff), + Color(0xff6dffff), + Color(0xff9cffff), + + Color(0xff002747), + Color(0xff004074), + Color(0xff0063b5), + Color(0xff008cff), + Color(0xff59b4ff), + Color(0xff9cd3ff), + + Color(0xff000047), + Color(0xff000074), + Color(0xff0000b5), + Color(0xff0000ff), + Color(0xff5959ff), + Color(0xff9c9cff), + + Color(0xff2e0047), + Color(0xff4b0074), + Color(0xff7500b5), + Color(0xffa500ff), + Color(0xffc459ff), + Color(0xffdc9cff), + + Color(0xff470047), + Color(0xff740074), + Color(0xffb500b5), + Color(0xffff00ff), + Color(0xffff66ff), + Color(0xffff9cff), + + Color(0xff47002a), + Color(0xff740045), + Color(0xffb5006b), + Color(0xffff0098), + Color(0xffff59bc), + Color(0xffff94d3), + + Color(0xff000000), + Color(0xff131313), + Color(0xff282828), + Color(0xff363636), + Color(0xff4d4d4d), + Color(0xff656565), + Color(0xff818181), + Color(0xff9f9f9f), + Color(0xffbcbcbc), + Color(0xffe2e2e2), + Color(0xffffffff), + ) + ) + } +} diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/SecurityColors.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/SecurityColors.kt new file mode 100644 index 0000000000000000000000000000000000000000..dfe6370ffa5f0e1240eb7d1618c36ebbfe12e0f9 --- /dev/null +++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/SecurityColors.kt @@ -0,0 +1,22 @@ +package de.justjanne.quasseldroid.ui.theme.quassel + +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.compositionLocalOf +import androidx.compose.ui.graphics.Color + +val LocalSecurityColors = compositionLocalOf { SecurityColors.Default } + +@Immutable +data class SecurityColors( + val secure: Color, + val unverified: Color, + val insecure: Color, +) { + companion object { + val Default = SecurityColors( + secure = Color(0xff4caf50), + unverified = Color(0xffffc107), + insecure = Color(0xffd32f2f), + ) + } +} diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/SenderColors.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/SenderColors.kt new file mode 100644 index 0000000000000000000000000000000000000000..6e957b12b5c6d47af33e89aeee71e21201344453 --- /dev/null +++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/SenderColors.kt @@ -0,0 +1,35 @@ +package de.justjanne.quasseldroid.ui.theme.quassel + +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.compositionLocalOf +import androidx.compose.ui.graphics.Color + +val LocalSenderColors = compositionLocalOf { SenderColors.Default } + +@Immutable +data class SenderColors( + val colors: List<Color>, +) { + companion object { + val Default = SenderColors( + colors = listOf( + Color(0xfff44336), + Color(0xff2196f3), + Color(0xff7cb342), + Color(0xff7b1fa2), + Color(0xffda8e00), + Color(0xff4caf50), + Color(0xff3f51b5), + Color(0xffe91e63), + Color(0xffb94600), + Color(0xff9e9d24), + Color(0xff558b2f), + Color(0xff009688), + Color(0xff0277bd), + Color(0xff00838f), + Color(0xff9c27b0), + Color(0xffc51162), + ) + ) + } +} diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/UiColors.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/UiColors.kt new file mode 100644 index 0000000000000000000000000000000000000000..beebef92f0fd7036bd572ef15879deffc61407db --- /dev/null +++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/theme/quassel/UiColors.kt @@ -0,0 +1,19 @@ +package de.justjanne.quasseldroid.ui.theme.quassel + +import androidx.compose.runtime.Immutable +import androidx.compose.ui.graphics.Color + +@Immutable +data class UiColors( + val primary: Color, + val primaryVariant: Color, + val secondary: Color, +) { + companion object { + val Default = UiColors( + primary = Color(0xff0a70c0), + primaryVariant = Color(0xff105a94), + secondary = Color(0xffffaf3b), + ) + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bdf9f59c73e752c7596b8641ed88c76b6210893a..4258909f6bfd691639f75c909be300225557c341 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -19,6 +19,7 @@ androidx-compose-animation = { module = "androidx.compose.animation:animation", androidx-compose-compiler = { module = "androidx.compose.compiler:compiler", version.ref = "androidx-compose" } androidx-compose-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "androidx-compose" } androidx-compose-material = { module = "androidx.compose.material:material", version.ref = "androidx-compose" } +androidx-compose-material-icons = { module = "androidx.compose.material:material-icons-extended", version.ref = "androidx-compose" } androidx-compose-runtime = { module = "androidx.compose.runtime:runtime", version.ref = "androidx-compose" } androidx-compose-ui = { module = "androidx.compose.ui:ui", version.ref = "androidx-compose" } androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "androidx-compose" }