From f60ac5f22d6044177b284138fc44054b792e8926 Mon Sep 17 00:00:00 2001 From: Aenri Lovehart Date: Sat, 25 May 2024 06:21:48 -0400 Subject: [PATCH] Colors, QR Codes, Custom Themes, and More! --- .gitignore | 1 + genesis/app/build.gradle.kts | 8 +- .../kotlin/uninit/genesis/app/App.kt | 15 +- .../ui/screens/onboarding/OnboardingScreen.kt | 133 +++-- .../uninit/genesis/app/ui/theme/Theme.kt | 10 - .../ui/theme/catppuccin/mocha/CTPMochaPink.kt | 11 - .../kotlin/uninit/genesis/app/ui/theme/rgb.kt | 9 - .../client/gateway/GatewayQRLoginClient.kt | 64 ++- genesis/genesisApi/build.gradle.kts | 6 +- gradle/libs.versions.toml | 14 +- uninit/common-compose/build.gradle.kts | 5 + .../common/compose/HCaptchaClearanceHost.kt | 37 ++ .../common/compose/theme/ApplicationTheme.kt | 214 ++++++++ .../uninit/common/compose/theme/ColorUtils.kt | 36 ++ .../compose/theme/LocalApplicationTheme.kt | 5 + .../common/compose/theme/ThemeRegistry.kt | 133 +++++ .../compose/theme/catppuccin/catppuccin.kt | 67 +++ .../common/compose/theme/catppuccin/frappe.kt | 16 + .../common/compose/theme/catppuccin/latte.kt | 16 + .../compose/theme/catppuccin/macchiato.kt | 17 + .../common/compose/theme/catppuccin/mocha.kt | 16 + .../kotlin/uninit/common/QrMatrix.kt | 25 - .../commonMain/kotlin/uninit/common/Quad.kt | 10 + .../kotlin/uninit/common/collections/Three.kt | 32 ++ .../kotlin/uninit/common/collections/Two.kt | 29 ++ .../kotlin/uninit/common/color/RGBA.kt | 36 ++ .../common/color/pallete/CatppuccinPallete.kt | 172 +++++++ .../kotlin/uninit/common/platform/Text.kt | 9 + .../kotlin/uninit/common/qr/BitBuffer.kt | 49 -- .../uninit/common/qr/ErrorCorrectionLevel.kt | 30 -- .../kotlin/uninit/common/qr/LICENSE | 25 - .../kotlin/uninit/common/qr/MaskPattern.kt | 26 - .../kotlin/uninit/common/qr/Mode.kt | 18 - .../kotlin/uninit/common/qr/Polynomial.kt | 91 ---- .../kotlin/uninit/common/qr/QR8BitByte.kt | 30 -- .../kotlin/uninit/common/qr/QRAlphaNum.kt | 43 -- .../kotlin/uninit/common/qr/QRCode.kt | 485 ------------------ .../kotlin/uninit/common/qr/QRData.kt | 47 -- .../kotlin/uninit/common/qr/QRKanji.kt | 52 -- .../kotlin/uninit/common/qr/QRMath.kt | 49 -- .../kotlin/uninit/common/qr/QRNumber.kt | 49 -- .../kotlin/uninit/common/qr/QRUtil.kt | 384 -------------- .../kotlin/uninit/common/qr/RSBlock.kt | 251 --------- .../uninit/common/platform/Text.desktop.kt | 7 + .../kotlin/uninit/common/platform/Text.ios.kt | 5 + 45 files changed, 1038 insertions(+), 1749 deletions(-) delete mode 100644 genesis/app/src/commonMain/kotlin/uninit/genesis/app/ui/theme/Theme.kt delete mode 100644 genesis/app/src/commonMain/kotlin/uninit/genesis/app/ui/theme/catppuccin/mocha/CTPMochaPink.kt delete mode 100644 genesis/app/src/commonMain/kotlin/uninit/genesis/app/ui/theme/rgb.kt create mode 100644 uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/HCaptchaClearanceHost.kt create mode 100644 uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/ApplicationTheme.kt create mode 100644 uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/ColorUtils.kt create mode 100644 uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/LocalApplicationTheme.kt create mode 100644 uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/ThemeRegistry.kt create mode 100644 uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/catppuccin/catppuccin.kt create mode 100644 uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/catppuccin/frappe.kt create mode 100644 uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/catppuccin/latte.kt create mode 100644 uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/catppuccin/macchiato.kt create mode 100644 uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/catppuccin/mocha.kt delete mode 100644 uninit/common/src/commonMain/kotlin/uninit/common/QrMatrix.kt create mode 100644 uninit/common/src/commonMain/kotlin/uninit/common/Quad.kt create mode 100644 uninit/common/src/commonMain/kotlin/uninit/common/collections/Three.kt create mode 100644 uninit/common/src/commonMain/kotlin/uninit/common/collections/Two.kt create mode 100644 uninit/common/src/commonMain/kotlin/uninit/common/color/RGBA.kt create mode 100644 uninit/common/src/commonMain/kotlin/uninit/common/color/pallete/CatppuccinPallete.kt create mode 100644 uninit/common/src/commonMain/kotlin/uninit/common/platform/Text.kt delete mode 100644 uninit/common/src/commonMain/kotlin/uninit/common/qr/BitBuffer.kt delete mode 100644 uninit/common/src/commonMain/kotlin/uninit/common/qr/ErrorCorrectionLevel.kt delete mode 100644 uninit/common/src/commonMain/kotlin/uninit/common/qr/LICENSE delete mode 100644 uninit/common/src/commonMain/kotlin/uninit/common/qr/MaskPattern.kt delete mode 100644 uninit/common/src/commonMain/kotlin/uninit/common/qr/Mode.kt delete mode 100644 uninit/common/src/commonMain/kotlin/uninit/common/qr/Polynomial.kt delete mode 100644 uninit/common/src/commonMain/kotlin/uninit/common/qr/QR8BitByte.kt delete mode 100644 uninit/common/src/commonMain/kotlin/uninit/common/qr/QRAlphaNum.kt delete mode 100644 uninit/common/src/commonMain/kotlin/uninit/common/qr/QRCode.kt delete mode 100644 uninit/common/src/commonMain/kotlin/uninit/common/qr/QRData.kt delete mode 100644 uninit/common/src/commonMain/kotlin/uninit/common/qr/QRKanji.kt delete mode 100644 uninit/common/src/commonMain/kotlin/uninit/common/qr/QRMath.kt delete mode 100644 uninit/common/src/commonMain/kotlin/uninit/common/qr/QRNumber.kt delete mode 100644 uninit/common/src/commonMain/kotlin/uninit/common/qr/QRUtil.kt delete mode 100644 uninit/common/src/commonMain/kotlin/uninit/common/qr/RSBlock.kt create mode 100644 uninit/common/src/desktopMain/kotlin/uninit/common/platform/Text.desktop.kt create mode 100644 uninit/common/src/iosMain/kotlin/uninit/common/platform/Text.ios.kt diff --git a/.gitignore b/.gitignore index 69fa111..eb47b10 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ build xcuserdata /smali /appDesktop/.genesis.json +/.idea/artifacts/ diff --git a/genesis/app/build.gradle.kts b/genesis/app/build.gradle.kts index 09fa425..7a57aa1 100644 --- a/genesis/app/build.gradle.kts +++ b/genesis/app/build.gradle.kts @@ -47,8 +47,14 @@ kotlin { implementation(libs.napier) - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") + implementation(libs.qr) + + implementation(libs.coil) + implementation(libs.coil.svg) + implementation(libs.coil.compose) + implementation(libs.coil.network.ktor) + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") // implementation(libs.kamel) diff --git a/genesis/app/src/commonMain/kotlin/uninit/genesis/app/App.kt b/genesis/app/src/commonMain/kotlin/uninit/genesis/app/App.kt index c9a2a32..07c0ccc 100644 --- a/genesis/app/src/commonMain/kotlin/uninit/genesis/app/App.kt +++ b/genesis/app/src/commonMain/kotlin/uninit/genesis/app/App.kt @@ -1,19 +1,18 @@ package uninit.genesis.app -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.compositionLocalOf import cafe.adriel.voyager.navigator.Navigator import io.github.aakira.napier.Antilog import io.github.aakira.napier.Napier +import org.koin.compose.getKoin import org.koin.core.context.startKoin -import org.koin.dsl.koinApplication +import uninit.common.compose.preferences.PreferencesManager +import uninit.common.compose.theme.LocalApplicationTheme +import uninit.common.compose.theme.ThemeRegistry import uninit.genesis.app.ui.screens.LaunchScreen -import uninit.genesis.app.ui.theme.Theme -import uninit.genesis.app.ui.theme.catppuccin.mocha.CTPMochaPink -val LocalCompositionTheme = compositionLocalOf { CTPMochaPink } + @Composable fun App() { Napier.base(getAntiLog()) @@ -22,7 +21,9 @@ fun App() { startKoin { modules(nativeModule) } - CompositionLocalProvider( LocalCompositionTheme provides CTPMochaPink ) { + val themeId by getKoin().get().preference("currentThemeId", "Catppuccin/Mocha/Pink") + + CompositionLocalProvider( LocalApplicationTheme provides ThemeRegistry.getOrDefault(themeId) ) { Navigator(LaunchScreen()) } } diff --git a/genesis/app/src/commonMain/kotlin/uninit/genesis/app/ui/screens/onboarding/OnboardingScreen.kt b/genesis/app/src/commonMain/kotlin/uninit/genesis/app/ui/screens/onboarding/OnboardingScreen.kt index c1113ab..f9a2f63 100644 --- a/genesis/app/src/commonMain/kotlin/uninit/genesis/app/ui/screens/onboarding/OnboardingScreen.kt +++ b/genesis/app/src/commonMain/kotlin/uninit/genesis/app/ui/screens/onboarding/OnboardingScreen.kt @@ -1,27 +1,36 @@ package uninit.genesis.app.ui.screens.onboarding -import androidx.compose.foundation.Canvas +import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.Size +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp import cafe.adriel.voyager.core.screen.Screen +import coil3.compose.AsyncImage +import coil3.compose.AsyncImagePainter +import coil3.compose.rememberAsyncImagePainter import io.github.aakira.napier.Napier import io.ktor.client.engine.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.IO import kotlinx.coroutines.launch +import org.jetbrains.skia.Bitmap +import org.jetbrains.skia.Image import org.koin.compose.getKoin -import uninit.common.QrMatrix -import uninit.genesis.app.LocalCompositionTheme +import qrcode.QRCode +import qrcode.color.Colors +import uninit.common.compose.theme.LocalApplicationTheme import uninit.genesis.discord.client.GenesisClient import uninit.genesis.discord.client.gateway.GatewayQRLoginClient import uninit.genesis.discord.client.gateway.auth.GatewayQRAuthAwaitingApprovalEvent @@ -29,12 +38,14 @@ import uninit.genesis.discord.client.gateway.auth.GatewayQRAuthAwaitingApprovalE class OnboardingScreen : Screen { @Composable override fun Content() { - val theme = LocalCompositionTheme.current + val theme = LocalApplicationTheme.current // val windowSize = currentWindowAdaptiveInfo().windowSizeClass Box( modifier = Modifier .fillMaxSize() - .background(theme.base) + .background(theme.backgroundPane), + contentAlignment = Alignment.Center + ) { OnboardingQRLoginPage() } @@ -89,7 +100,7 @@ class OnboardingScreen : Screen { } QRLoginState.AwaitingApproval, QRLoginState.ExchangingTicket -> { - partialUser?.let { UserPartialInfo(it) } + partialUser?.let { UserPartialInfo(it, state == QRLoginState.AwaitingApproval) } if (state == QRLoginState.ExchangingTicket) LoadingScreen("Exchanging ticket...") } QRLoginState.TokenReceived -> { @@ -106,47 +117,88 @@ class OnboardingScreen : Screen { modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { - Text(text, color = LocalCompositionTheme.current.text) + Text(text, color = LocalApplicationTheme.current.body) } } @Composable fun QRCode(link: String) { Napier.v("QRCode: $link") - val qrMatrix by remember { mutableStateOf(QrMatrix(link)) } - val primary = LocalCompositionTheme.current.primary + var qr by remember> { mutableStateOf(null) } + var size by remember { mutableStateOf(IntSize.Zero) } Box( - modifier = Modifier.aspectRatio(1f).fillMaxWidth(0.5f).padding(16.dp), + modifier = Modifier + .aspectRatio(1f) + .fillMaxWidth(0.5f) + .padding(16.dp) + .onSizeChanged { + size = it + }, contentAlignment = Alignment.Center ) { - Canvas(modifier = Modifier.fillMaxSize()) { - val cellSize = size.width / qrMatrix.width - qrMatrix.matrix.forEachIndexed { x, row -> - row.forEachIndexed { y, value -> - if (value) { - drawRect( - color = primary, - topLeft = Offset(x * cellSize, y * cellSize), - size = Size(cellSize, cellSize) - ) - } - } + val color = LocalApplicationTheme.current.body + LaunchedEffect(link) { + color.let { + qr = QRCode + .ofRoundedSquares() + .withColor(Colors.rgba( + (it.red * 255F).toInt(), + (it.green * 255F).toInt(), + (it.blue * 255F).toInt(), + (it.alpha * 255F).toInt() + )) + .withBackgroundColor(Colors.TRANSPARENT) + .withRadius(10) + .withSize(10) + .build(link) + .renderToBytes() } } + qr?.let { + AsyncImage(it, "QR Code") + } } - } + @Composable - fun UserPartialInfo(partialUser: GatewayQRAuthAwaitingApprovalEvent) { + fun UserPartialInfo( + partialUser: GatewayQRAuthAwaitingApprovalEvent, + sayApprove: Boolean + ) { Napier.v("UserPartialInfo: $partialUser") + val painter = rememberAsyncImagePainter(partialUser.userAvatarUri) Column( modifier = Modifier.fillMaxWidth().padding(16.dp), - horizontalAlignment = Alignment.CenterHorizontally + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, ) { - Text("User: ${partialUser.userName}") - Text("ID: ${partialUser.userId}") - Text("AvatarUri: ${partialUser.userAvatarUri}") + ThemedText("Welcome Back, ${partialUser.userName}!") + when (painter.state) { + is AsyncImagePainter.State.Loading -> { + CircularProgressIndicator( + modifier = Modifier.size(100.dp), + color = LocalApplicationTheme.current.accent + ) + } + is AsyncImagePainter.State.Error -> { + ThemedText("Error loading avatar") + } + is AsyncImagePainter.State.Success -> { + Image( + painter = painter, + contentDescription = "User Avatar", + modifier = Modifier + .size(100.dp) + .clip( + RoundedCornerShape(50.dp) + ) + ) + } + + AsyncImagePainter.State.Empty -> TODO() + } + if (sayApprove) ThemedText("Please approve the login request on your device.") } } @@ -158,9 +210,9 @@ class OnboardingScreen : Screen { modifier = Modifier.fillMaxSize().padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally ) { - Text("Token Received, testing...") + ThemedText("Token Received, testing...") messages.forEach { - Text(it) + ThemedText(it) } } CoroutineScope(Dispatchers.IO).launch { @@ -174,11 +226,26 @@ class OnboardingScreen : Screen { } } + @Composable + fun ThemedText(text: String) { + Text(text, color = LocalApplicationTheme.current.body) + } + + +} + +private fun Bitmap.Companion.decodeByteArray(it: ByteArray): Bitmap { + val bitmap = Bitmap() + val image = Image.makeFromEncoded(it) + bitmap.setImageInfo(image.imageInfo) + image.readPixels(dst = bitmap) + return bitmap } + enum class QRLoginState { AwaitingQRCode, AwaitingScan, AwaitingApproval, ExchangingTicket, TokenReceived -} \ No newline at end of file +} diff --git a/genesis/app/src/commonMain/kotlin/uninit/genesis/app/ui/theme/Theme.kt b/genesis/app/src/commonMain/kotlin/uninit/genesis/app/ui/theme/Theme.kt deleted file mode 100644 index cc15dab..0000000 --- a/genesis/app/src/commonMain/kotlin/uninit/genesis/app/ui/theme/Theme.kt +++ /dev/null @@ -1,10 +0,0 @@ -package uninit.genesis.app.ui.theme - -import androidx.compose.ui.graphics.Color - -data class Theme( - val base: Color, - val overBase: Color, - val primary: Color, - val text: Color, -) diff --git a/genesis/app/src/commonMain/kotlin/uninit/genesis/app/ui/theme/catppuccin/mocha/CTPMochaPink.kt b/genesis/app/src/commonMain/kotlin/uninit/genesis/app/ui/theme/catppuccin/mocha/CTPMochaPink.kt deleted file mode 100644 index 98e7629..0000000 --- a/genesis/app/src/commonMain/kotlin/uninit/genesis/app/ui/theme/catppuccin/mocha/CTPMochaPink.kt +++ /dev/null @@ -1,11 +0,0 @@ -package uninit.genesis.app.ui.theme.catppuccin.mocha - -import uninit.genesis.app.ui.theme.Theme -import uninit.genesis.app.ui.theme.rgb - -val CTPMochaPink = Theme( - base = rgb(17, 17, 27), - overBase = rgb(24, 24, 37), - primary = rgb(245, 194, 231), - text = rgb(205, 214, 244) -) \ No newline at end of file diff --git a/genesis/app/src/commonMain/kotlin/uninit/genesis/app/ui/theme/rgb.kt b/genesis/app/src/commonMain/kotlin/uninit/genesis/app/ui/theme/rgb.kt deleted file mode 100644 index eb097de..0000000 --- a/genesis/app/src/commonMain/kotlin/uninit/genesis/app/ui/theme/rgb.kt +++ /dev/null @@ -1,9 +0,0 @@ -package uninit.genesis.app.ui.theme - -import androidx.compose.ui.graphics.Color - -fun rgb(r: Int, g: Int, b: Int) = Color( - r / 255f, - g / 255f, - b / 255f, -) \ No newline at end of file diff --git a/genesis/discord/client/src/commonMain/kotlin/uninit/genesis/discord/client/gateway/GatewayQRLoginClient.kt b/genesis/discord/client/src/commonMain/kotlin/uninit/genesis/discord/client/gateway/GatewayQRLoginClient.kt index a846141..4c5b3d8 100644 --- a/genesis/discord/client/src/commonMain/kotlin/uninit/genesis/discord/client/gateway/GatewayQRLoginClient.kt +++ b/genesis/discord/client/src/commonMain/kotlin/uninit/genesis/discord/client/gateway/GatewayQRLoginClient.kt @@ -9,11 +9,14 @@ import io.github.aakira.napier.Napier import io.ktor.client.* import io.ktor.client.call.* import io.ktor.client.engine.* +import io.ktor.client.plugins.contentnegotiation.* import io.ktor.client.plugins.logging.* import io.ktor.client.plugins.websocket.* import io.ktor.client.request.* +import io.ktor.client.statement.* import io.ktor.http.* import io.ktor.serialization.kotlinx.* +import io.ktor.serialization.kotlinx.json.* import io.ktor.util.* import io.ktor.websocket.* import kotlinx.coroutines.* @@ -26,6 +29,8 @@ import uninit.common.fytix.Some import uninit.genesis.discord.client.GenesisClient import uninit.genesis.discord.client.enum.LogLevel import uninit.genesis.discord.client.gateway.auth.* +import kotlin.io.encoding.Base64 +import kotlin.io.encoding.ExperimentalEncodingApi /** * GatewayQRLoginClient is a client for handling the QR login process. @@ -58,12 +63,19 @@ class GatewayQRLoginClient( } sanitizeHeader { header -> header == HttpHeaders.Authorization } } + install(ContentNegotiation) { + json(json) + } } private val rsa: RSA.OAEP.KeyPair = CryptographyProvider.Default .get(RSA.OAEP) - .keyPairGenerator(rsaWidth.bits) + .keyPairGenerator( + rsaWidth.bits, + digest = SHA256 + ) .generateKeyBlocking() + private val hasher: Hasher = CryptographyProvider.Default .get(SHA256) @@ -85,6 +97,7 @@ class GatewayQRLoginClient( val String.trimRightEquals: String get() = this.trimEnd { it == '=' } + @OptIn(ExperimentalEncodingApi::class) fun connect() { if (websocket != null && websocket?.isActive == true) return @@ -130,37 +143,43 @@ class GatewayQRLoginClient( "hello" -> { Napier.v("Hello packet received: $packet") // setup heartbeat, send encoded public key + if (heartbeat.isActive) { + Napier.w("Cancelling and Overriding old Heartbeat job") + heartbeat.cancel() + } heartbeat = scope.launch { while (true) { - if (isActive) send( - Frame.Text( - json.encodeToString( - GatewayQRAuthEvent.heartbeat() + delay(packet.heartbeat_interval!!.toLong()) + if (isActive) { + Napier.v("Sending heartbeat") + send( + Frame.Text( + json.encodeToString( + GatewayQRAuthEvent.heartbeat() + ) ) ) - ) else { + } else { break } - delay(packet.heartbeat_interval!!.toLong()) } } val pubKey = rsa .publicKey .encodeTo(RSA.PublicKey.Format.PEM) .decodeToString() - Napier.v("Sending public key: $pubKey") +// Napier.v("Sending public key: $pubKey") val packetRes = json.encodeToString( GatewayQRAuthEvent.init( - pubKey.split('\n').let { - it.subList(1, it.size - 2).joinToString("") - } + pubKey + .split('\n').let { + it.subList(1, it.size - 2).joinToString("") + } ) ) Napier.v("Sending init packet with public key: $packetRes") send(Frame.Text(packetRes)) - Napier.v("Sent init packet.") - continue } "nonce_proof" -> { Napier.v("Received nonce proof: ${packet.encrypted_nonce!!}") @@ -171,12 +190,13 @@ class GatewayQRLoginClient( packet.encrypted_nonce!!.decodeBase64Bytes() ) val proofBytes = hasher.hash(decrypted_nonce) - val proof = proofBytes.encodeBase64().trimRightEquals + val proof = json.encodeToString( + GatewayQRAuthEvent.nonceProof( + Base64.UrlSafe.encode(proofBytes).trimRightEquals)) +// Napier.v("waiting 500MS") +// delay(500L) Napier.v("Sending nonce proof: $proof") - send(Frame.Text(json.encodeToString( - GatewayQRAuthEvent.nonceProof(proof) - ))) - continue + send(Frame.Text(proof)) } "pending_remote_init" -> { Napier.v("Received fingerprint: ${packet.fingerprint}") @@ -199,7 +219,6 @@ class GatewayQRLoginClient( userAvatarUri = "https://cdn.discordapp.com/avatars/${items[0]}/${items[2]}.png", userName = items[3] )) - continue } "pending_login" -> { // emit loading "Exchanging ticket..." @@ -211,11 +230,9 @@ class GatewayQRLoginClient( } "heartbeat_ack" -> { Napier.v("Heartbeat ack received.") - continue } else -> { Napier.v("Unknown packet ${packet.op}: $packet") - continue } } } catch (e: Exception) { @@ -238,7 +255,10 @@ class GatewayQRLoginClient( setBody(QRAuthTicketExchangeRequest(ticket)) contentType(ContentType.parse("application/json")) } - val encryptedToken = res.body().encrypted_token + Napier.v("Exchange response: ${res.status.value}") + val text = res.bodyAsText() + Napier.v("Exchange response: $text") + val encryptedToken = json.decodeFromString(text).encrypted_token return rsa .privateKey .decryptor() diff --git a/genesis/genesisApi/build.gradle.kts b/genesis/genesisApi/build.gradle.kts index f3b0c49..35f98d4 100644 --- a/genesis/genesisApi/build.gradle.kts +++ b/genesis/genesisApi/build.gradle.kts @@ -24,7 +24,8 @@ kotlin { implementation(libs.ktor.client.negotiation) implementation(libs.ktor.serialization.json) - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") + implementation(libs.kotlinx.coroutines.core) + implementation(project(":uninit:common")) } @@ -32,11 +33,14 @@ kotlin { val androidMain by getting { dependencies { implementation(libs.ktor.client.okhttp) + implementation(libs.kotlinx.coroutines.android) } } val desktopMain by getting { dependencies { implementation(libs.ktor.client.okhttp) + implementation(libs.kotlinx.coroutines.jvm) + } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4240059..dfe421d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,15 +13,27 @@ kprefs = "0.12.1" kotlinx = "1.8.1" compose = "1.6.10" cryptography = "0.3.1" - +qr-code = "4.1.1" +coil = "3.0.0-alpha06" [libraries] +coil = { group = "io.coil-kt.coil3", name = "coil", version.ref = "coil" } +coil-svg = { group = "io.coil-kt.coil3", name = "coil-svg", version.ref = "coil" } +coil-compose = { group = "io.coil-kt.coil3", name = "coil-compose", version.ref = "coil" } +coil-network-core = { group = "io.coil-kt.coil3", name = "coil-network-core", version.ref = "coil" } +coil-network-ktor = { group = "io.coil-kt.coil3", name = "coil-network-ktor", version.ref = "coil" } +coil-network-okhttp = { group = "io.coil-kt.coil3", name = "coil-network-okhttp", version.ref = "coil" } +qr = { group = "io.github.g0dkar", name = "qrcode-kotlin", version.ref = "qr-code" } + crypto-core = { group = "dev.whyoleg.cryptography", name = "cryptography-core", version.ref = "cryptography" } crypto-provider-jvm = { group = "dev.whyoleg.cryptography", name = "cryptography-provider-jdk", version.ref = "cryptography" } crypto-provider-apple = { group = "dev.whyoleg.cryptography", name = "cryptography-provider-apple", version.ref = "cryptography" } kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinx" } +#kotlinx-coroutines-jvm = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core-jvm", version.ref = "kotlinx" } +kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinx" } +kotlinx-coroutines-jvm = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-swing", version.ref = "kotlinx" } serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "serialization" } diff --git a/uninit/common-compose/build.gradle.kts b/uninit/common-compose/build.gradle.kts index dfe0342..453e1a1 100644 --- a/uninit/common-compose/build.gradle.kts +++ b/uninit/common-compose/build.gradle.kts @@ -27,6 +27,11 @@ kotlin { compileOnly(libs.ktor.client.core) compileOnly(libs.ktor.client.negotiation) + compileOnly(libs.coil) + compileOnly(libs.coil.svg) + compileOnly(libs.coil.compose) + compileOnly(libs.coil.network.core) + // These have to be implementation or else gradle will pitch a fit implementation(libs.koin.core) implementation(libs.koin.compose) diff --git a/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/HCaptchaClearanceHost.kt b/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/HCaptchaClearanceHost.kt new file mode 100644 index 0000000..e810fe9 --- /dev/null +++ b/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/HCaptchaClearanceHost.kt @@ -0,0 +1,37 @@ +package uninit.common.compose + +import androidx.compose.runtime.Composable + +@Composable +fun HCaptchaClearanceHost( + fakeHostUri: String, + siteKey: String, + rqData: String, + rqToken: String, + onCleared: (String) -> Unit, +) { + TODO("Not implemented, this is terrifying and im scared :(") +} + +private const val HTML_MIMIC = """ + + + HCaptcha Clearance + + + +
+ + +""" \ No newline at end of file diff --git a/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/ApplicationTheme.kt b/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/ApplicationTheme.kt new file mode 100644 index 0000000..8e976da --- /dev/null +++ b/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/ApplicationTheme.kt @@ -0,0 +1,214 @@ +package uninit.common.compose.theme + +import androidx.compose.ui.graphics.Color +import kotlinx.serialization.* +import kotlinx.serialization.builtins.ListSerializer +import kotlinx.serialization.builtins.nullable +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.descriptors.buildClassSerialDescriptor +import kotlinx.serialization.descriptors.nullable +import kotlinx.serialization.encoding.* +import uninit.common.collections.Three +import uninit.common.collections.Two +import uninit.common.compose.theme.catppuccin.CatppuccinMochaPink + +/** + * Loosely based off of the Catppucin Style Guide + * + * Easily serializable, allowing for easy custom themes. + */ +@Serializable(ApplicationTheme.Serializer::class) +data class ApplicationTheme( + val id: String, + val backgroundPane: Color, + val secondaryPanes: Two, + val surfaceElements: Three, + val overlays: Three, + val body: Color, + val header: Color, + val subheaders: Two, + val accent: Color, + + val success: Color?, + val warning: Color?, + val error: Color?, + val info: Color?, + + val selectionOverlay: Color = overlays[1].atOpacity(0.2f), + + val cursor: Color?, +) { + inner class Serializer : KSerializer { + private val colorDescriptor = String.serializer().descriptor + private val nullableColorDescriptor = colorDescriptor.nullable + @OptIn(ExperimentalSerializationApi::class) + val colorArrayDescriptor = buildClassSerialDescriptor("Array<${colorDescriptor.serialName}>") + override val descriptor = buildClassSerialDescriptor("ApplicationTheme") { + element("id", String.serializer().descriptor) // this is equivalent but not using the color descriptor is symbolic + element("backgroundPane", colorDescriptor) + element("secondaryPanes", colorArrayDescriptor) + element("surfaceElements", colorArrayDescriptor) + element("overlays", colorArrayDescriptor) + element("body", colorDescriptor) + element("header", colorDescriptor) + element("subheaders", colorArrayDescriptor) + element("accent", colorDescriptor) + + element("success", nullableColorDescriptor) + element("warning", nullableColorDescriptor) + element("error", nullableColorDescriptor) + element("info", nullableColorDescriptor) + + element("selectionOverlay", colorDescriptor) + + element("cursor", nullableColorDescriptor) + } + + + + @OptIn(ExperimentalSerializationApi::class) + override fun deserialize(decoder: Decoder): ApplicationTheme { + val listDeserializer = ListSerializer(String.serializer()) + val nullableStringDeserializer = String.serializer().nullable + decoder.decodeStructure(descriptor) { + var id: String? = null + var backgroundPane: Color? = null + var secondaryPanes: List? = null + var surfaceElements: List? = null + var overlays: List? = null + var body: Color? = null + var header: Color? = null + var subheaders: List? = null + var accent: Color? = null + + var success: Color? = null + var warning: Color? = null + var error: Color? = null + var info: Color? = null + + var selectionOverlay: Color? = null + + var cursor: Color? = null + + while (true) { + when (val index = decodeElementIndex(descriptor)) { + 0 -> id = decodeStringElement(descriptor, index) + 1 -> backgroundPane = Color.fromHex(decodeStringElement(descriptor, index)) + 2 -> secondaryPanes = decodeSerializableElement( + colorArrayDescriptor, + index, + listDeserializer + ).map { Color.fromHex(it) } + + 3 -> surfaceElements = decodeSerializableElement( + colorArrayDescriptor, + index, + listDeserializer + ).map { Color.fromHex(it) } + + 4 -> overlays = decodeSerializableElement( + colorArrayDescriptor, + index, + listDeserializer + ).map { Color.fromHex(it) } + + 5 -> body = Color.fromHex(decodeStringElement(descriptor, index)) + 6 -> header = Color.fromHex(decodeStringElement(descriptor, index)) + 7 -> subheaders = decodeSerializableElement( + colorArrayDescriptor, + index, + listDeserializer + ).map { Color.fromHex(it) } + + 8 -> accent = Color.fromHex(decodeStringElement(descriptor, index)) + + 9 -> success = decodeNullableSerializableElement( + nullableColorDescriptor, + index, + nullableStringDeserializer + )?.let { Color.fromHex(it) } + + 10 -> warning = decodeNullableSerializableElement( + nullableColorDescriptor, + index, + nullableStringDeserializer + )?.let { Color.fromHex(it) } + + 11 -> error = decodeNullableSerializableElement( + nullableColorDescriptor, + index, + nullableStringDeserializer + )?.let { Color.fromHex(it) } + + 12 -> info = decodeNullableSerializableElement( + nullableColorDescriptor, + index, + nullableStringDeserializer + )?.let { Color.fromHex(it) } + + 13 -> selectionOverlay = Color.fromHex(decodeStringElement(descriptor, index)) + + 14 -> cursor = decodeNullableSerializableElement( + nullableColorDescriptor, + index, + nullableStringDeserializer + )?.let { Color.fromHex(it) } + + CompositeDecoder.DECODE_DONE -> break + else -> error("Unexpected index: $index") + } + } + fun List.asTypedArray() = this.toTypedArray() + return@decodeStructure ApplicationTheme( + id = id!!, + backgroundPane = backgroundPane!!, + secondaryPanes = Two(secondaryPanes!!.asTypedArray()), + surfaceElements = Three(surfaceElements!!.asTypedArray()), + overlays = Three(overlays!!.asTypedArray()), + body = body!!, + header = header!!, + subheaders = Two(subheaders!!.asTypedArray()), + accent = accent!!, + success = success, + warning = warning, + error = error, + info = info, + selectionOverlay = selectionOverlay!!, + cursor = cursor, + ) + } + throw IllegalStateException("Should not reach here") + } + + @OptIn(ExperimentalSerializationApi::class) + override fun serialize(encoder: Encoder, value: ApplicationTheme) { + val listSerializer = ListSerializer(String.serializer()) + val nullableStringSerializer = String.serializer().nullable + encoder.encodeStructure(descriptor) { + encodeSerializableElement(String.serializer().descriptor, 0, String.serializer(), value.id) + encodeStringElement(colorDescriptor, 1, value.backgroundPane.asHex) + encodeSerializableElement(colorArrayDescriptor, 2, listSerializer, value.secondaryPanes.map { it.asHex }) + encodeSerializableElement(colorArrayDescriptor, 3, listSerializer, value.surfaceElements.map { it.asHex }) + encodeSerializableElement(colorArrayDescriptor, 4, listSerializer, value.overlays.map { it.asHex }) + encodeStringElement(colorDescriptor, 5, value.body.asHex) + encodeStringElement(colorDescriptor, 6, value.header.asHex) + encodeSerializableElement(colorArrayDescriptor, 7, listSerializer, value.subheaders.map { it.asHex }) + encodeStringElement(colorDescriptor, 8, value.accent.asHex) + + encodeNullableSerializableElement(nullableColorDescriptor, 9, nullableStringSerializer, value.success?.asHex) + encodeNullableSerializableElement(nullableColorDescriptor, 10, nullableStringSerializer, value.warning?.asHex) + encodeNullableSerializableElement(nullableColorDescriptor, 11, nullableStringSerializer, value.error?.asHex) + encodeNullableSerializableElement(nullableColorDescriptor, 12, nullableStringSerializer, value.info?.asHex) + + encodeStringElement(colorDescriptor, 13, value.selectionOverlay.asHex) + + encodeNullableSerializableElement(nullableColorDescriptor, 14, nullableStringSerializer, value.cursor?.asHex) + } + } + } + + companion object { + val default = CatppuccinMochaPink + } +} + diff --git a/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/ColorUtils.kt b/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/ColorUtils.kt new file mode 100644 index 0000000..bc35f79 --- /dev/null +++ b/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/ColorUtils.kt @@ -0,0 +1,36 @@ +package uninit.common.compose.theme + +import androidx.compose.ui.graphics.Color +import uninit.common.color.RGBA + +fun rgb(r: Int, g: Int, b: Int) = Color( + r / 255f, + g / 255f, + b / 255f, +) + +fun Color.atOpacity(opacity: Float) = Color( + red = red, + green = green, + blue = blue, + alpha = opacity, +) +val Color.asHex: String + get() = "#${(red * 255f).toInt().toString(16)}${(green * 255f).toInt().toString(16)}${(blue * 255f).toInt().toString(16)}${(alpha * 255f).toInt().toString(16)}" + +fun Color.Companion.fromHex(hex: String) = hex.removePrefix("=").let { + require(it.length in 6..8) + Color( + red = it.substring(0, 2).toInt(16) / 255f, + green = it.substring(2, 4).toInt(16) / 255f, + blue = it.substring(4, 6).toInt(16) / 255f, + alpha = if (it.length == 8) it.substring(6, 8).toInt(16) / 255f else 1f, + ) +} +val RGBA.color: Color + get() = Color( + red = r / 255f, + green = g / 255f, + blue = b / 255f, + alpha = a / 255f, + ) \ No newline at end of file diff --git a/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/LocalApplicationTheme.kt b/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/LocalApplicationTheme.kt new file mode 100644 index 0000000..343aabc --- /dev/null +++ b/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/LocalApplicationTheme.kt @@ -0,0 +1,5 @@ +package uninit.common.compose.theme + +import androidx.compose.runtime.compositionLocalOf + +val LocalApplicationTheme = compositionLocalOf { ApplicationTheme.default } \ No newline at end of file diff --git a/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/ThemeRegistry.kt b/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/ThemeRegistry.kt new file mode 100644 index 0000000..8ecf5d8 --- /dev/null +++ b/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/ThemeRegistry.kt @@ -0,0 +1,133 @@ +package uninit.common.compose.theme + +import uninit.common.compose.theme.catppuccin.* + +sealed class ThemeRegistry { + private val map: MutableMap = mutableMapOf() + + /** + * Registers a theme with the registry. + * + * @param theme The theme to register + */ + fun register(theme: ApplicationTheme) { + map[theme.id] = theme + } + + /** + * Gets a theme by its ID. + * + * @param id The ID of the theme + * @return The theme + */ + fun get(id: String): ApplicationTheme? { + return map[id] + } + + /** + * Gets a theme by its ID, or returns the default theme if the theme is not found. + * + * @param id The ID of the theme + * @return The theme, or the default theme if the theme is not found + */ + fun getOrDefault(id: String): ApplicationTheme { + return map[id] ?: ApplicationTheme.default + } + + /** + * Filters the themes to only those that are in the specified "folder". + * + * Example: + * ```kt + * val registry = ThemeRegistry() + * registry.insertBuiltins() + * val mocha = registry.filterToFolder("Catppuccin/Mocha") + * // Only themes with IDs starting with "Catppuccin/Mocha" will be returned + * // For example, "Catppuccin/Mocha/Lavender" and "Catppuccin/Mocha/Blue". + * // Something like "Catppuccin/Mocha1/Lavender" will not be returned. + * ``` + * + * @param path The path to filter to + * @return The filtered themes + */ + fun filterToFolder(path: String): Map { + return map.filter { + it.key == path || it.key.startsWith("$path/") + } + } + + /** + * Inserts the built-in themes into the registry. + */ + fun insertBuiltins() { + this.map.apply { + for (theme in listOf( + CatppuccinLatteLavender, + CatppuccinLatteBlue, + CatppuccinLatteSapphire, + CatppuccinLatteSky, + CatppuccinLatteTeal, + CatppuccinLatteGreen, + CatppuccinLatteYellow, + CatppuccinLattePeach, + CatppuccinLatteMaroon, + CatppuccinLatteRed, + CatppuccinLatteMauve, + CatppuccinLattePink, + CatppuccinLatteFlamingo, + CatppuccinLatteRosewater, + CatppuccinFrappeLavender, + CatppuccinFrappeBlue, + CatppuccinFrappeSapphire, + CatppuccinFrappeSky, + CatppuccinFrappeTeal, + CatppuccinFrappeGreen, + CatppuccinFrappeYellow, + CatppuccinFrappePeach, + CatppuccinFrappeMaroon, + CatppuccinFrappeRed, + CatppuccinFrappeMauve, + CatppuccinFrappePink, + CatppuccinFrappeFlamingo, + CatppuccinFrappeRosewater, + CatppuccinMacchiatoLavender, + CatppuccinMacchiatoBlue, + CatppuccinMacchiatoSapphire, + CatppuccinMacchiatoSky, + CatppuccinMacchiatoTeal, + CatppuccinMacchiatoGreen, + CatppuccinMacchiatoYellow, + CatppuccinMacchiatoPeach, + CatppuccinMacchiatoMaroon, + CatppuccinMacchiatoRed, + CatppuccinMacchiatoMauve, + CatppuccinMacchiatoPink, + CatppuccinMacchiatoFlamingo, + CatppuccinMacchiatoRosewater, + CatppuccinMochaLavender, + CatppuccinMochaBlue, + CatppuccinMochaSapphire, + CatppuccinMochaSky, + CatppuccinMochaTeal, + CatppuccinMochaGreen, + CatppuccinMochaYellow, + CatppuccinMochaPeach, + CatppuccinMochaMaroon, + CatppuccinMochaRed, + CatppuccinMochaMauve, + CatppuccinMochaPink, + CatppuccinMochaFlamingo, + CatppuccinMochaRosewater + )) put(theme.id, theme) + } + } + + /** + * Singleton instance of the theme registry. + */ + companion object : ThemeRegistry() { + init { + insertBuiltins() + } + } +} \ No newline at end of file diff --git a/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/catppuccin/catppuccin.kt b/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/catppuccin/catppuccin.kt new file mode 100644 index 0000000..37b068d --- /dev/null +++ b/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/catppuccin/catppuccin.kt @@ -0,0 +1,67 @@ +package uninit.common.compose.theme.catppuccin + +import uninit.common.collections.three +import uninit.common.collections.two +import uninit.common.color.pallete.Catppuccin +import uninit.common.compose.theme.ApplicationTheme +import uninit.common.compose.theme.color + +internal fun catppuccin(flavor: String, variant: String? = null) = lazy { + val flavor = when (flavor.lowercase()) { + "latte" -> Catppuccin.Latte + "frappe" -> Catppuccin.Frappe + "macchiato" -> Catppuccin.Macchiato + "mocha" -> Catppuccin.Mocha + else -> throw IllegalArgumentException("Invalid flavor: $flavor") + } + val theVariant = when (variant?.lowercase()) { + "lavender" -> flavor.lavender + "blue" -> flavor.blue + "sapphire" -> flavor.sapphire + "sky" -> flavor.sky + "teal" -> flavor.teal + "green" -> flavor.green + "yellow" -> flavor.yellow + "peach" -> flavor.peach + "maroon" -> flavor.maroon + "red" -> flavor.red + "mauve" -> flavor.mauve + "pink" -> flavor.pink + "flamingo" -> flavor.flamingo + "rosewater" -> flavor.rosewater + else -> flavor.pink // Default to pink :3 + } + + return@lazy ApplicationTheme( + id = "Catppuccin/${flavor.id}/${(variant?.lowercase() ?: "pink").let { + "${it[0].uppercase()}${it.substring(1)}" + }}", + backgroundPane = flavor.base.color, + secondaryPanes = arrayOf( + flavor.crust.color, + flavor.mantle.color, + ).two, + surfaceElements = arrayOf( + flavor.surface0.color, + flavor.surface1.color, + flavor.surface2.color, + ).three, + overlays = arrayOf( + flavor.overlay0.color, + flavor.overlay1.color, + flavor.overlay2.color, + ).three, + body = flavor.text.color, + header = flavor.text.color, + subheaders = arrayOf( + flavor.subtext0.color, + flavor.subtext1.color, + ).two, + accent = theVariant.color, + success = flavor.green.color, + warning = flavor.yellow.color, + error = flavor.red.color, + info = flavor.blue.color, + cursor = flavor.rosewater.color, + ) +} \ No newline at end of file diff --git a/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/catppuccin/frappe.kt b/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/catppuccin/frappe.kt new file mode 100644 index 0000000..bfbaae2 --- /dev/null +++ b/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/catppuccin/frappe.kt @@ -0,0 +1,16 @@ +package uninit.common.compose.theme.catppuccin + +val CatppuccinFrappeLavender by catppuccin("Frappe", "Lavender") +val CatppuccinFrappeBlue by catppuccin("Frappe", "Blue") +val CatppuccinFrappeSapphire by catppuccin("Frappe", "Sapphire") +val CatppuccinFrappeSky by catppuccin("Frappe", "Sky") +val CatppuccinFrappeTeal by catppuccin("Frappe", "Teal") +val CatppuccinFrappeGreen by catppuccin("Frappe", "Green") +val CatppuccinFrappeYellow by catppuccin("Frappe", "Yellow") +val CatppuccinFrappePeach by catppuccin("Frappe", "Peach") +val CatppuccinFrappeMaroon by catppuccin("Frappe", "Maroon") +val CatppuccinFrappeRed by catppuccin("Frappe", "Red") +val CatppuccinFrappeMauve by catppuccin("Frappe", "Mauve") +val CatppuccinFrappePink by catppuccin("Frappe", "Pink") +val CatppuccinFrappeFlamingo by catppuccin("Frappe", "Flamingo") +val CatppuccinFrappeRosewater by catppuccin("Frappe", "Rosewater") diff --git a/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/catppuccin/latte.kt b/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/catppuccin/latte.kt new file mode 100644 index 0000000..12dfc26 --- /dev/null +++ b/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/catppuccin/latte.kt @@ -0,0 +1,16 @@ +package uninit.common.compose.theme.catppuccin + +val CatppuccinLatteLavender by catppuccin("Latte", "Lavender") +val CatppuccinLatteBlue by catppuccin("Latte", "Blue") +val CatppuccinLatteSapphire by catppuccin("Latte", "Sapphire") +val CatppuccinLatteSky by catppuccin("Latte", "Sky") +val CatppuccinLatteTeal by catppuccin("Latte", "Teal") +val CatppuccinLatteGreen by catppuccin("Latte", "Green") +val CatppuccinLatteYellow by catppuccin("Latte", "Yellow") +val CatppuccinLattePeach by catppuccin("Latte", "Peach") +val CatppuccinLatteMaroon by catppuccin("Latte", "Maroon") +val CatppuccinLatteRed by catppuccin("Latte", "Red") +val CatppuccinLatteMauve by catppuccin("Latte", "Mauve") +val CatppuccinLattePink by catppuccin("Latte", "Pink") +val CatppuccinLatteFlamingo by catppuccin("Latte", "Flamingo") +val CatppuccinLatteRosewater by catppuccin("Latte", "Rosewater") diff --git a/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/catppuccin/macchiato.kt b/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/catppuccin/macchiato.kt new file mode 100644 index 0000000..7cb5a36 --- /dev/null +++ b/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/catppuccin/macchiato.kt @@ -0,0 +1,17 @@ + +package uninit.common.compose.theme.catppuccin + +val CatppuccinMacchiatoLavender by catppuccin("Macchiato", "Lavender") +val CatppuccinMacchiatoBlue by catppuccin("Macchiato", "Blue") +val CatppuccinMacchiatoSapphire by catppuccin("Macchiato", "Sapphire") +val CatppuccinMacchiatoSky by catppuccin("Macchiato", "Sky") +val CatppuccinMacchiatoTeal by catppuccin("Macchiato", "Teal") +val CatppuccinMacchiatoGreen by catppuccin("Macchiato", "Green") +val CatppuccinMacchiatoYellow by catppuccin("Macchiato", "Yellow") +val CatppuccinMacchiatoPeach by catppuccin("Macchiato", "Peach") +val CatppuccinMacchiatoMaroon by catppuccin("Macchiato", "Maroon") +val CatppuccinMacchiatoRed by catppuccin("Macchiato", "Red") +val CatppuccinMacchiatoMauve by catppuccin("Macchiato", "Mauve") +val CatppuccinMacchiatoPink by catppuccin("Macchiato", "Pink") +val CatppuccinMacchiatoFlamingo by catppuccin("Macchiato", "Flamingo") +val CatppuccinMacchiatoRosewater by catppuccin("Macchiato", "Rosewater") diff --git a/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/catppuccin/mocha.kt b/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/catppuccin/mocha.kt new file mode 100644 index 0000000..49f34cc --- /dev/null +++ b/uninit/common-compose/src/commonMain/kotlin/uninit/common/compose/theme/catppuccin/mocha.kt @@ -0,0 +1,16 @@ +package uninit.common.compose.theme.catppuccin + +val CatppuccinMochaLavender by catppuccin("Mocha", "Lavender") +val CatppuccinMochaBlue by catppuccin("Mocha", "Blue") +val CatppuccinMochaSapphire by catppuccin("Mocha", "Sapphire") +val CatppuccinMochaSky by catppuccin("Mocha", "Sky") +val CatppuccinMochaTeal by catppuccin("Mocha", "Teal") +val CatppuccinMochaGreen by catppuccin("Mocha", "Green") +val CatppuccinMochaYellow by catppuccin("Mocha", "Yellow") +val CatppuccinMochaPeach by catppuccin("Mocha", "Peach") +val CatppuccinMochaMaroon by catppuccin("Mocha", "Maroon") +val CatppuccinMochaRed by catppuccin("Mocha", "Red") +val CatppuccinMochaMauve by catppuccin("Mocha", "Mauve") +val CatppuccinMochaPink by catppuccin("Mocha", "Pink") +val CatppuccinMochaFlamingo by catppuccin("Mocha", "Flamingo") +val CatppuccinMochaRosewater by catppuccin("Mocha", "Rosewater") diff --git a/uninit/common/src/commonMain/kotlin/uninit/common/QrMatrix.kt b/uninit/common/src/commonMain/kotlin/uninit/common/QrMatrix.kt deleted file mode 100644 index 854945f..0000000 --- a/uninit/common/src/commonMain/kotlin/uninit/common/QrMatrix.kt +++ /dev/null @@ -1,25 +0,0 @@ -package uninit.common - -import uninit.common.qr.ErrorCorrectionLevel -import uninit.common.qr.QRCode - -/** - * Simple ASCII QR Matrix, represented as a 2D list of booleans. - * Low error correction, Simple wrapper around kazuhikoarase/qr-code-generator - * @param data The data to encode in the QR matrix. Must be ASCII. - */ -class QrMatrix(val data: String) { - private val qrCode = QRCode.getMinimumQRCode(data, ErrorCorrectionLevel.L) - - val matrix: List> = qrCode.modules.map { row -> - row.map { it!! } - } - - val width: Int = matrix.size - val height: Int = matrix[0].size - - fun at(x: Int, y: Int): Boolean { - return matrix[x][y] - } - -} diff --git a/uninit/common/src/commonMain/kotlin/uninit/common/Quad.kt b/uninit/common/src/commonMain/kotlin/uninit/common/Quad.kt new file mode 100644 index 0000000..8c014c8 --- /dev/null +++ b/uninit/common/src/commonMain/kotlin/uninit/common/Quad.kt @@ -0,0 +1,10 @@ +package uninit.common + +open class Quad( + val first: T, + val second: U, + val third: V, + val fourth: W, +) { + override fun toString(): String = "Quad($first, $second, $third, $fourth)" +} diff --git a/uninit/common/src/commonMain/kotlin/uninit/common/collections/Three.kt b/uninit/common/src/commonMain/kotlin/uninit/common/collections/Three.kt new file mode 100644 index 0000000..f44206a --- /dev/null +++ b/uninit/common/src/commonMain/kotlin/uninit/common/collections/Three.kt @@ -0,0 +1,32 @@ +package uninit.common.collections + +import kotlinx.serialization.Serializable +import kotlinx.serialization.SerializationStrategy +import kotlin.jvm.JvmInline + +@JvmInline value class Three( internal val it: Array ) : Collection { + override val size: Int + get() = 3 + init { + require(it.size == 3) + } + val first: T get() = it[0] + val second: T get() = it[1] + val third: T get() = it[2] + override fun toString(): String = "Three($first, $second, $third)" + operator fun component1(): T = first + operator fun component2(): T = second + operator fun component3(): T = third + operator fun get(index: Int): T = it[index] + override fun contains(element: T): Boolean { + return first == element || second == element || third == element + } + override fun containsAll(elements: Collection): Boolean { + return elements.all { contains(it) } + } + override fun isEmpty(): Boolean = false + override fun iterator(): Iterator = it.iterator() +} + +val Array.three: Three + get() = Three(this) \ No newline at end of file diff --git a/uninit/common/src/commonMain/kotlin/uninit/common/collections/Two.kt b/uninit/common/src/commonMain/kotlin/uninit/common/collections/Two.kt new file mode 100644 index 0000000..eb98038 --- /dev/null +++ b/uninit/common/src/commonMain/kotlin/uninit/common/collections/Two.kt @@ -0,0 +1,29 @@ +package uninit.common.collections + +import kotlin.jvm.JvmInline + +@JvmInline value class Two(internal val it: Array) : Collection { + override val size: Int + get() = 2 + init { + require(it.size == 2) + } + val first: T get() = it[0] + val second: T get() = it[1] + override fun toString(): String = "Two($first, $second)" + operator fun component1(): T = first + operator fun component2(): T = second + operator fun get(index: Int): T = it[index] + override fun contains(element: T): Boolean { + return first == element || second == element + } + override fun containsAll(elements: Collection): Boolean { + return elements.all { contains(it) } + } + override fun isEmpty(): Boolean = false + override fun iterator(): Iterator = it.iterator() + +} + +val Array.two: Two + get() = Two(this) \ No newline at end of file diff --git a/uninit/common/src/commonMain/kotlin/uninit/common/color/RGBA.kt b/uninit/common/src/commonMain/kotlin/uninit/common/color/RGBA.kt new file mode 100644 index 0000000..a874a1b --- /dev/null +++ b/uninit/common/src/commonMain/kotlin/uninit/common/color/RGBA.kt @@ -0,0 +1,36 @@ +package uninit.common.color + +import uninit.common.Quad + +class RGBA( + val r: Int, + val g: Int, + val b: Int, + val a: Int = 255, +) : Quad(r, g, b, a){ + init { + require(r in 0..255) + require(g in 0..255) + require(b in 0..255) + require(a in 0..255) + } + override fun toString(): String = "RGBA($r, $g, $b, $a)" + fun toFloat(): Quad = Quad( + r / 255f, + g / 255f, + b / 255f, + a / 255f, + ) + companion object { + fun hex(it: String): RGBA { + require(it.length in 6..9) + it.removePrefix("#").let { + val r = it.substring(0, 2).toInt(16) + val g = it.substring(2, 4).toInt(16) + val b = it.substring(4, 6).toInt(16) + val a = if (it.length == 8) it.substring(6, 8).toInt(16) else 255 + return RGBA(r, g, b, a) + } + } + } +} \ No newline at end of file diff --git a/uninit/common/src/commonMain/kotlin/uninit/common/color/pallete/CatppuccinPallete.kt b/uninit/common/src/commonMain/kotlin/uninit/common/color/pallete/CatppuccinPallete.kt new file mode 100644 index 0000000..0407149 --- /dev/null +++ b/uninit/common/src/commonMain/kotlin/uninit/common/color/pallete/CatppuccinPallete.kt @@ -0,0 +1,172 @@ +package uninit.common.color.pallete + +import uninit.common.color.RGBA +import kotlin.jvm.JvmInline + +/** + * Catppuccin + */ +sealed class Catppuccin( + val id: String, + val crust: RGBA, + val mantle: RGBA, + val base: RGBA, + val surface0: RGBA, + val surface1: RGBA, + val surface2: RGBA, + val overlay0: RGBA, + val overlay1: RGBA, + val overlay2: RGBA, + val subtext0: RGBA, + val subtext1: RGBA, + val text: RGBA, + val lavender: RGBA, + val blue: RGBA, + val sapphire: RGBA, + val sky: RGBA, + val teal: RGBA, + val green: RGBA, + val yellow: RGBA, + val peach: RGBA, + val maroon: RGBA, + val red: RGBA, + val mauve: RGBA, + val pink: RGBA, + val flamingo: RGBA, + val rosewater: RGBA, +) { + /** + * The lightest Catppuccin theme, harmoniously inverting the essence of Catppuccin's dark themes. + * + */ + object Latte : Catppuccin( + id = "Latte", + crust = h("#dce0e8"), + mantle = h("#e6e9ef"), + base = h("#eff1f5"), + surface0 = h("#ccd0da"), + surface1 = h("#bcc0cc"), + surface2 = h("#acb0be"), + overlay0 = h("#9ca0b0"), + overlay1 = h("#8c8fa1"), + overlay2 = h("#7c7f93"), + subtext0 = h("#6c6f85"), + subtext1 = h("#5c5f77"), + text = h("#4c4f69"), + lavender = h("#7287fd"), + blue = h("#1e66f5"), + sapphire = h("#209fb5"), + sky = h("#04a5e5"), + teal = h("#179299"), + green = h("#40a02b"), + yellow = h("#df8e1d"), + peach = h("#fe640b"), + maroon = h("#e64553"), + red = h("#d20f39"), + mauve = h("#8839ef"), + pink = h("#ea76cb"), + flamingo = h("#dd7878"), + rosewater = h("#dc8a78"), + ) + + /** + * A less vibrant alternative using subdued colors for a muted aesthetic + */ + object Frappe : Catppuccin( + id = "Frappe", + crust = h("#232634"), + mantle = h("#292c3c"), + base = h("#303446"), + surface0 = h("#414559"), + surface1 = h("#51576d"), + surface2 = h("#626880"), + overlay0 = h("#737994"), + overlay1 = h("#838ba7"), + overlay2 = h("#949cbb"), + subtext0 = h("#a5adce"), + subtext1 = h("#b5bfe2"), + text = h("#c6d0f5"), + lavender = h("#babbf1"), + blue = h("#8caaee"), + sapphire = h("#85c1dc"), + sky = h("#99d1db"), + teal = h("#81c8be"), + green = h("#a6d189"), + yellow = h("#e5c890"), + peach = h("#ef9f76"), + maroon = h("#ea999c"), + red = h("#e78284"), + mauve = h("#ca9ee6"), + pink = h("#f4b8e4"), + flamingo = h("#eebebe"), + rosewater = h("#f2d5cf"), + ) + + /** + * Medium contrast with gentle colors creating a soothing atmosphere + */ + object Macchiato : Catppuccin( + id = "Macchiato", + crust = h("#181926"), + mantle = h("#1e2030"), + base = h("#24273a"), + surface0 = h("#363a4f"), + surface1 = h("#494d64"), + surface2 = h("#5b6078"), + overlay0 = h("#6e738d"), + overlay1 = h("#8087a2"), + overlay2 = h("#939ab7"), + subtext0 = h("#a5adcb"), + subtext1 = h("#b8c0e0"), + text = h("#cad3f5"), + lavender = h("#b7bdf8"), + blue = h("#8aadf4"), + sapphire = h("#7dc4e4"), + sky = h("#91d7e3"), + teal = h("#8bd5ca"), + green = h("#a6da95"), + yellow = h("#eed49f"), + peach = h("#f5a97f"), + maroon = h("#ee99a0"), + red = h("#ed8796"), + mauve = h("#c6a0f6"), + pink = h("#f5bde6"), + flamingo = h("#f0c6c6"), + rosewater = h("#f4dbd6"), + ) + + /** + * The Original - A vibrant and colorful theme with high contrast, offering + * a cozy feeling with color-rich accents + */ + object Mocha : Catppuccin( + id = "Mocha", + crust = h("#11111b"), + mantle = h("#181825"), + base = h("#1e1e2e"), + surface0 = h("#313244"), + surface1 = h("#45475a"), + surface2 = h("#585b70"), + overlay0 = h("#6c7086"), + overlay1 = h("#7f849c"), + overlay2 = h("#9399b2"), + subtext0 = h("#a6adc8"), + subtext1 = h("#bac2de"), + text = h("#cdd6f4"), + lavender = h("#b4befe"), + blue = h("#89b4fa"), + sapphire = h("#74c7ec"), + sky = h("#89dceb"), + teal = h("#94e2d5"), + green = h("#a6e3a1"), + yellow = h("#f9e2af"), + peach = h("#fab387"), + maroon = h("#eba0ac"), + red = h("#f38ba8"), + mauve = h("#cba6f7"), + pink = h("#f5c2e7"), + flamingo = h("#f2cdcd"), + rosewater = h("#f5e0dc"), + ) +} +internal inline fun h(string: String): RGBA = RGBA.hex(string) \ No newline at end of file diff --git a/uninit/common/src/commonMain/kotlin/uninit/common/platform/Text.kt b/uninit/common/src/commonMain/kotlin/uninit/common/platform/Text.kt new file mode 100644 index 0000000..67961f8 --- /dev/null +++ b/uninit/common/src/commonMain/kotlin/uninit/common/platform/Text.kt @@ -0,0 +1,9 @@ +package uninit.common.platform + +object Text { + fun stringToByteArrayWithEncoding(text: String, encoding: String): ByteArray { + return text_StringToByteArrayWithEncoding(text, encoding) + } +} + +internal expect fun text_StringToByteArrayWithEncoding(text: String, encoding: String): ByteArray \ No newline at end of file diff --git a/uninit/common/src/commonMain/kotlin/uninit/common/qr/BitBuffer.kt b/uninit/common/src/commonMain/kotlin/uninit/common/qr/BitBuffer.kt deleted file mode 100644 index b5b3034..0000000 --- a/uninit/common/src/commonMain/kotlin/uninit/common/qr/BitBuffer.kt +++ /dev/null @@ -1,49 +0,0 @@ -package uninit.common.qr - -/** - * BitBuffer implementation for QR code. - * @author Kazuhiko Arase, Ported by Aenri Lovehart - */ -class BitBuffer { - var buffer: ByteArray - private set - var lengthInBits: Int = 0 - private set - private val inclements = 32 - - init { - buffer = ByteArray(inclements) - } - - override fun toString(): String { - val buffer: StringBuilder = StringBuilder() - for (i in 0 until lengthInBits) { - buffer.append(if (get(i)) '1' else '0') - } - return buffer.toString() - } - - private fun get(index: Int): Boolean { - return ((buffer[index / 8].toInt() ushr (7 - index % 8)) and 1) == 1 - } - - fun put(num: Int, length: Int) { - for (i in 0 until length) { - put(((num ushr (length - i - 1)) and 1) == 1) - } - } - - fun put(bit: Boolean) { - if (lengthInBits == buffer.size * 8) { - val newBuffer = ByteArray(buffer.size + inclements) - buffer.copyInto(newBuffer, buffer.size, 0, buffer.size) - buffer = newBuffer - } - - if (bit) { - buffer[lengthInBits / 8] = (buffer[lengthInBits / 8].toInt() or (0x80 ushr (lengthInBits % 8))).toByte() - } - - lengthInBits++ - } -} \ No newline at end of file diff --git a/uninit/common/src/commonMain/kotlin/uninit/common/qr/ErrorCorrectionLevel.kt b/uninit/common/src/commonMain/kotlin/uninit/common/qr/ErrorCorrectionLevel.kt deleted file mode 100644 index 44704e7..0000000 --- a/uninit/common/src/commonMain/kotlin/uninit/common/qr/ErrorCorrectionLevel.kt +++ /dev/null @@ -1,30 +0,0 @@ -package uninit.common.qr - - -/** - * ErrorCorrectionLevel for QR code. - * @author Kazuhiko Arase, Ported by Aenri Lovehart - */ -interface ErrorCorrectionLevel { - companion object { - /** - * 7%. - */ - const val L: Int = 1 - - /** - * 15%. - */ - const val M: Int = 0 - - /** - * 25%. - */ - const val Q: Int = 3 - - /** - * 30%. - */ - const val H: Int = 2 - } -} \ No newline at end of file diff --git a/uninit/common/src/commonMain/kotlin/uninit/common/qr/LICENSE b/uninit/common/src/commonMain/kotlin/uninit/common/qr/LICENSE deleted file mode 100644 index 3d60672..0000000 --- a/uninit/common/src/commonMain/kotlin/uninit/common/qr/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -This code is a Kotlin Multiplatform Port of https://github.com/kazuhikoarase/qrcode-generator - -The following is the MIT license of the original project: - -MIT License - -Copyright (c) 2009 Kazuhiko Arase - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/uninit/common/src/commonMain/kotlin/uninit/common/qr/MaskPattern.kt b/uninit/common/src/commonMain/kotlin/uninit/common/qr/MaskPattern.kt deleted file mode 100644 index 584f8b3..0000000 --- a/uninit/common/src/commonMain/kotlin/uninit/common/qr/MaskPattern.kt +++ /dev/null @@ -1,26 +0,0 @@ -package uninit.common.qr - -/** - * Mask pattern for QR code. - * @author Kazuhiko Arase, Ported by Aenri Lovehart - */ -internal interface MaskPattern { - companion object { - - const val PATTERN000: Int = 0 - - const val PATTERN001: Int = 1 - - const val PATTERN010: Int = 2 - - const val PATTERN011: Int = 3 - - const val PATTERN100: Int = 4 - - const val PATTERN101: Int = 5 - - const val PATTERN110: Int = 6 - - const val PATTERN111: Int = 7 - } -} \ No newline at end of file diff --git a/uninit/common/src/commonMain/kotlin/uninit/common/qr/Mode.kt b/uninit/common/src/commonMain/kotlin/uninit/common/qr/Mode.kt deleted file mode 100644 index 1c0eb09..0000000 --- a/uninit/common/src/commonMain/kotlin/uninit/common/qr/Mode.kt +++ /dev/null @@ -1,18 +0,0 @@ -package uninit.common.qr - -/** - * QRData Mode - * @author Kazuhiko Arase - */ -interface Mode { - companion object { - - const val MODE_NUMBER: Int = 1 shl 0 - - const val MODE_ALPHA_NUM: Int = 1 shl 1 - - const val MODE_8BIT_BYTE: Int = 1 shl 2 - - const val MODE_KANJI: Int = 1 shl 3 - } -} \ No newline at end of file diff --git a/uninit/common/src/commonMain/kotlin/uninit/common/qr/Polynomial.kt b/uninit/common/src/commonMain/kotlin/uninit/common/qr/Polynomial.kt deleted file mode 100644 index b7a1f6a..0000000 --- a/uninit/common/src/commonMain/kotlin/uninit/common/qr/Polynomial.kt +++ /dev/null @@ -1,91 +0,0 @@ -package uninit.common.qr - -import kotlin.jvm.JvmOverloads - -/** - * Polynomial - * @author Kazuhiko Arase, Ported by Aenri Lovehart - */ -internal class Polynomial @JvmOverloads constructor(num: IntArray, shift: Int = 0) { - private val num: IntArray - - init { - var offset = 0 - - while (offset < num.size && num[offset] == 0) { - offset++ - } - - this.num = IntArray(num.size - offset + shift) - num.copyInto(this.num, num.size - offset, 0, offset) - } - - fun get(index: Int): Int { - return num[index] - } - - val length: Int - get() = num.size - - override fun toString(): String { - val buffer = StringBuilder() - - for (i in 0 until length) { - if (i > 0) { - buffer.append(",") - } - buffer.append(get(i)) - } - - return buffer.toString() - } - - fun toLogString(): String { - val buffer = StringBuilder() - - for (i in 0 until length) { - if (i > 0) { - buffer.append(",") - } - buffer.append(QRMath.glog(get(i))) - } - - return buffer.toString() - } - - fun multiply(e: Polynomial): Polynomial { - val num = IntArray(length + e.length - 1) - - for (i in 0 until length) { - for (j in 0 until e.length) { - num[i + j] = num[i + j] xor QRMath.gexp(QRMath.glog(get(i)) + QRMath.glog(e.get(j))) - } - } - - return Polynomial(num) - } - - fun mod(e: Polynomial): Polynomial { - if (length - e.length < 0) { - return this - } - - // 最上位桁の比率 - val ratio: Int = QRMath.glog(get(0)) - QRMath.glog(e.get(0)) - - // コピー作成 - val num = IntArray(length) - for (i in 0 until length) { - num[i] = get(i) - } - - - // 引き算して余りを計算 - for (i in 0 until e.length) { - num[i] = num[i] xor QRMath.gexp(QRMath.glog(e.get(i)) + ratio) - } - - // 再帰計算 - return Polynomial(num).mod(e) - } -} \ No newline at end of file diff --git a/uninit/common/src/commonMain/kotlin/uninit/common/qr/QR8BitByte.kt b/uninit/common/src/commonMain/kotlin/uninit/common/qr/QR8BitByte.kt deleted file mode 100644 index a4e8f45..0000000 --- a/uninit/common/src/commonMain/kotlin/uninit/common/qr/QR8BitByte.kt +++ /dev/null @@ -1,30 +0,0 @@ -package uninit.common.qr - -/** - * QR8BitByte - * @author Kazuhiko Arase, Ported by Aenri Lovehart - */ -internal class QR8BitByte(data: String) : QRData(Mode.MODE_8BIT_BYTE, data) { - override fun write(buffer: BitBuffer) { - throw RuntimeException("not implemented, \"SJIS\" encoding required.") -// try { -// val data = data.toByteArray() -// -// for (i in data.indices) { -// buffer.put(data[i].toInt(), 8) -// } -// } catch (e: Exception) { -// throw RuntimeException(e.message) -// } - } - - override val length: Int - get() { - throw RuntimeException("not implemented, \"SJIS\" encoding required.") -// try { -// return getData().getBytes(QRCode.get8BitByteEncoding()).length -// } catch (e: UnsupportedEncodingException) { -// throw java.lang.RuntimeException(e.message) -// } - } -} \ No newline at end of file diff --git a/uninit/common/src/commonMain/kotlin/uninit/common/qr/QRAlphaNum.kt b/uninit/common/src/commonMain/kotlin/uninit/common/qr/QRAlphaNum.kt deleted file mode 100644 index 059cceb..0000000 --- a/uninit/common/src/commonMain/kotlin/uninit/common/qr/QRAlphaNum.kt +++ /dev/null @@ -1,43 +0,0 @@ -package uninit.common.qr - - -/** - * QRAlphaNum - * @author Kazuhiko Arase - */ -internal class QRAlphaNum(data: String?) : QRData(Mode.MODE_ALPHA_NUM, data!!) { - override fun write(buffer: BitBuffer) { - val c = data.toCharArray() - - var i = 0 - - while (i + 1 < c.size) { - buffer.put(getCode(c[i]) * 45 + getCode(c[i + 1]), 11) - i += 2 - } - - if (i < c.size) { - buffer.put(getCode(c[i]), 6) - } - } - - override val length: Int - get() = data.length - - companion object { - private fun getCode(c: Char): Int = when (c) { - in '0'..'9' -> c.code - '0'.code - in 'A'..'Z' -> c.code - 'A'.code + 10 - ' ' -> 36 - '$' -> 37 - '%' -> 38 - '*' -> 39 - '+' -> 40 - '-' -> 41 - '.' -> 42 - '/' -> 43 - ':' -> 44 - else -> throw IllegalArgumentException("illegal char :$c") - } - } -} \ No newline at end of file diff --git a/uninit/common/src/commonMain/kotlin/uninit/common/qr/QRCode.kt b/uninit/common/src/commonMain/kotlin/uninit/common/qr/QRCode.kt deleted file mode 100644 index 00f9b17..0000000 --- a/uninit/common/src/commonMain/kotlin/uninit/common/qr/QRCode.kt +++ /dev/null @@ -1,485 +0,0 @@ -package uninit.common.qr - -import kotlin.jvm.JvmOverloads -import kotlin.math.max - - -class QRCode() { - /** - * 型番を取得する。 - * @return 型番 - */ - /** - * 型番を設定する。 - * @param typeNumber 型番 - */ - var typeNumber: Int = 1 - - lateinit var modules: Array> - - /** - * モジュール数を取得する。 - */ - var moduleCount: Int = 0 - private set - - /** - * 誤り訂正レベルを取得する。 - * @return 誤り訂正レベル - * @see ErrorCorrectionLevel - */ - /** - * 誤り訂正レベルを設定する。 - * @param errorCorrectionLevel 誤り訂正レベル - * @see ErrorCorrectionLevel - */ - var errorCorrectionLevel: Int - - private val qrDataList: MutableList - - /** - * モードを指定してデータを追加する。 - * @param data データ - * @param mode モード - * @see Mode - */ - /** - * データを追加する。 - * @param data データ - */ - @JvmOverloads - fun addData(data: String, mode: Int = QRUtil.getMode(data)) { - when (mode) { - Mode.MODE_NUMBER -> addData(QRNumber(data)) - Mode.MODE_ALPHA_NUM -> addData(QRAlphaNum(data)) - Mode.MODE_8BIT_BYTE -> addData(QR8BitByte(data)) - Mode.MODE_KANJI -> addData(QRKanji(data)) - else -> throw IllegalArgumentException("mode:$mode") - } - } - - /** - * データをクリアする。 - *

addData で追加されたデータをクリアします。 - */ - fun clearData() { - qrDataList.clear() - } - - internal fun addData(qrData: QRData) { - qrDataList.add(qrData) - } - - protected val dataCount: Int - get() = qrDataList.size - - protected fun getData(index: Int): QRData { - return qrDataList[index] - } - - /** - * 暗モジュールかどうかを取得する。 - * @param row 行 (0 ~ モジュール数 - 1) - * @param col 列 (0 ~ モジュール数 - 1) - */ - fun isDark(row: Int, col: Int): Boolean { - if (modules[row][col] != null) { - return modules[row][col]!! - } else { - return false - } - } - - /** - * QRコードを作成する。 - */ - fun make() { - make(false, bestMaskPattern) - } - - private val bestMaskPattern: Int - get() { - var minLostPoint = 0 - var pattern = 0 - - for (i in 0..7) { - make(true, i) - - val lostPoint: Int = QRUtil.getLostPoint(this) - - if (i == 0 || minLostPoint > lostPoint) { - minLostPoint = lostPoint - pattern = i - } - } - - return pattern - } - - /** - * - */ - private fun make(test: Boolean, maskPattern: Int) { - // モジュール初期化 - - moduleCount = typeNumber * 4 + 17 - modules = Array(moduleCount) { - arrayOfNulls( - moduleCount - ) - } - - // 位置検出パターン及び分離パターンを設定 - setupPositionProbePattern(0, 0) - setupPositionProbePattern(moduleCount - 7, 0) - setupPositionProbePattern(0, moduleCount - 7) - - setupPositionAdjustPattern() - setupTimingPattern() - - setupTypeInfo(test, maskPattern) - - if (typeNumber >= 7) { - setupTypeNumber(test) - } - - val dataArray = qrDataList.toTypedArray() - - val data = createData(typeNumber, errorCorrectionLevel, dataArray) - - mapData(data, maskPattern) - } - - private fun mapData(data: ByteArray, maskPattern: Int) { - var inc = -1 - var row = moduleCount - 1 - var bitIndex = 7 - var byteIndex = 0 - - var col = moduleCount - 1 - while (col > 0) { - if (col == 6) col-- - - while (true) { - for (c in 0..1) { - if (modules[row][col - c] == null) { - var dark = false - - if (byteIndex < data.size) { - dark = (((data[byteIndex].toInt() ushr bitIndex) and 1) == 1) - } - - val mask: Boolean = QRUtil.getMask(maskPattern, row, col - c) - - if (mask) { - dark = !dark - } - - modules[row][col - c] = dark - bitIndex-- - - if (bitIndex == -1) { - byteIndex++ - bitIndex = 7 - } - } - } - - row += inc - - if (row < 0 || moduleCount <= row) { - row -= inc - inc = -inc - break - } - } - col -= 2 - } - } - - /** - * 位置合わせパターンを設定 - */ - private fun setupPositionAdjustPattern() { - val pos: IntArray = QRUtil.getPatternPosition(typeNumber) - - for (i in pos.indices) { - for (j in pos.indices) { - val row = pos[i] - val col = pos[j] - - if (modules[row][col] != null) { - continue - } - - for (r in -2..2) { - for (c in -2..2) { - if ((r == -2) || r == 2 || c == -2 || c == 2 || (r == 0 && c == 0)) { - modules[row + r][col + c] = true - } else { - modules[row + r][col + c] = false - } - } - } - } - } - } - - /** - * 位置検出パターンを設定 - */ - private fun setupPositionProbePattern(row: Int, col: Int) { - for (r in -1..7) { - for (c in -1..7) { - if ((row + r <= -1 || moduleCount <= row + r) || col + c <= -1 || moduleCount <= col + c) { - continue - } - - if (((0 <= r) && r <= 6 && (c == 0 || c == 6)) - || ((0 <= c) && c <= 6 && (r == 0 || r == 6)) - || ((((2 <= r) && r <= 4) && 2 <= c) && c <= 4) - ) { - modules[row + r][col + c] = true - } else { - modules[row + r][col + c] = false - } - } - } - } - - /** - * タイミングパターンを設定 - */ - private fun setupTimingPattern() { - for (r in 8 until moduleCount - 8) { - if (modules[r][6] != null) { - continue - } - modules[r][6] = r % 2 == 0 - } - for (c in 8 until moduleCount - 8) { - if (modules[6][c] != null) { - continue - } - modules[6][c] = c % 2 == 0 - } - } - - /** - * 型番を設定 - */ - private fun setupTypeNumber(test: Boolean) { - val bits: Int = QRUtil.getBCHTypeNumber(typeNumber) - - for (i in 0..17) { - val mod = !test && ((bits shr i) and 1) == 1 - modules[i / 3][(i % 3 + moduleCount) - 8 - 3] = mod - } - - for (i in 0..17) { - val mod = !test && ((bits shr i) and 1) == 1 - modules[(i % 3 + moduleCount) - 8 - 3][i / 3] = mod - } - } - - /** - * 形式情報を設定 - */ - private fun setupTypeInfo(test: Boolean, maskPattern: Int) { - val data = (errorCorrectionLevel shl 3) or maskPattern - val bits: Int = QRUtil.getBCHTypeInfo(data) - - // 縦方向 - for (i in 0..14) { - val mod = !test && ((bits shr i) and 1) == 1 - - if (i < 6) { - modules[i][8] = mod - } else if (i < 8) { - modules[i + 1][8] = mod - } else { - modules[moduleCount - 15 + i][8] = mod - } - } - - // 横方向 - for (i in 0..14) { - val mod = !test && ((bits shr i) and 1) == 1 - - if (i < 8) { - modules[8][moduleCount - i - 1] = mod - } else if (i < 9) { - modules[8][15 - i - 1 + 1] = mod - } else { - modules[8][15 - i - 1] = mod - } - } - - // 固定 - modules[moduleCount - 8][8] = !test - } - - /** - * コンストラクタ - *

型番1, 誤り訂正レベルH のQRコードのインスタンスを生成します。 - * @see ErrorCorrectionLevel - */ - init { - this.errorCorrectionLevel = ErrorCorrectionLevel.H - this.qrDataList = ArrayList(1) - } - - companion object { - private val PAD0 = 0xEC - - private val PAD1 = 0x11 - - fun createData(typeNumber: Int, errorCorrectionLevel: Int, dataArray: Array): ByteArray { - val rsBlocks: Array = RSBlock.getRSBlocks(typeNumber, errorCorrectionLevel) - - val buffer = BitBuffer() - - for (i in dataArray.indices) { - val data = dataArray[i] - buffer.put(data.mode, 4) - buffer.put(data.length, data.getLengthInBits(typeNumber)) - data.write(buffer) - } - - // 最大データ数を計算 - var totalDataCount = 0 - for (i in rsBlocks.indices) { - totalDataCount += rsBlocks[i].dataCount - } - - if (buffer.lengthInBits > totalDataCount * 8) { - throw IllegalArgumentException( - ("code length overflow. (" - + buffer.lengthInBits - + ">") + totalDataCount * 8 + ")" - ) - } - - // 終端コード - if (buffer.lengthInBits + 4 <= totalDataCount * 8) { - buffer.put(0, 4) - } - - // padding - while (buffer.lengthInBits % 8 != 0) { - buffer.put(false) - } - - // padding - while (true) { - if (buffer.lengthInBits >= totalDataCount * 8) { - break - } - buffer.put(PAD0, 8) - - if (buffer.lengthInBits >= totalDataCount * 8) { - break - } - buffer.put(PAD1, 8) - } - - return createBytes(buffer, rsBlocks) - } - - private fun createBytes(buffer: BitBuffer, rsBlocks: Array): ByteArray { - var offset = 0 - - var maxDcCount = 0 - var maxEcCount = 0 - - val dcdata = arrayOfNulls(rsBlocks.size) - val ecdata = arrayOfNulls(rsBlocks.size) - - for (r in rsBlocks.indices) { - val dcCount: Int = rsBlocks[r].dataCount - val ecCount: Int = rsBlocks[r].totalCount - dcCount - - maxDcCount = max(maxDcCount.toDouble(), dcCount.toDouble()).toInt() - maxEcCount = max(maxEcCount.toDouble(), ecCount.toDouble()).toInt() - - dcdata[r] = IntArray(dcCount) - for (i in dcdata[r]!!.indices) { - dcdata[r]!![i] = 0xff and buffer.buffer[i + offset].toInt() - } - offset += dcCount - - val rsPoly: Polynomial = QRUtil.getErrorCorrectPolynomial(ecCount) - val rawPoly = Polynomial(dcdata[r]!!, rsPoly.length - 1) - - val modPoly = rawPoly.mod(rsPoly) - ecdata[r] = IntArray(rsPoly.length - 1) - for (i in ecdata[r]!!.indices) { - val modIndex = i + modPoly.length - ecdata[r]!!.size - ecdata[r]!![i] = if ((modIndex >= 0)) modPoly.get(modIndex) else 0 - } - } - - var totalCodeCount = 0 - for (i in rsBlocks.indices) { - totalCodeCount += rsBlocks[i].totalCount - } - - val data = ByteArray(totalCodeCount) - - var index = 0 - - for (i in 0 until maxDcCount) { - for (r in rsBlocks.indices) { - if (i < dcdata[r]!!.size) { - data[index++] = dcdata[r]!![i].toByte() - } - } - } - - for (i in 0 until maxEcCount) { - for (r in rsBlocks.indices) { - if (i < ecdata[r]!!.size) { - data[index++] = ecdata[r]!![i].toByte() - } - } - } - - return data - } - - /** - * 最小の型番となる QRCode を作成する。 - * @param data データ - * @param errorCorrectionLevel 誤り訂正レベル - */ - fun getMinimumQRCode(data: String, errorCorrectionLevel: Int): QRCode { - val mode: Int = QRUtil.getMode(data) - - val qr = QRCode() - qr.errorCorrectionLevel = errorCorrectionLevel - qr.addData(data, mode) - - val length = qr.getData(0).length - - for (typeNumber in 1..10) { - if (length <= QRUtil.getMaxLength(typeNumber, mode, errorCorrectionLevel)) { - qr.typeNumber = typeNumber - break - } - } - - qr.make() - - return qr - } - - private var _8BitByteEncoding: String = QRUtil.jISEncoding - fun set8BitByteEncoding(_8BitByteEncoding: String) { - Companion._8BitByteEncoding = _8BitByteEncoding - } - - fun get8BitByteEncoding(): String { - return _8BitByteEncoding - } - } -} \ No newline at end of file diff --git a/uninit/common/src/commonMain/kotlin/uninit/common/qr/QRData.kt b/uninit/common/src/commonMain/kotlin/uninit/common/qr/QRData.kt deleted file mode 100644 index b16e587..0000000 --- a/uninit/common/src/commonMain/kotlin/uninit/common/qr/QRData.kt +++ /dev/null @@ -1,47 +0,0 @@ -package uninit.common.qr - -/** - * QRData - * @author Kazuhiko Arase - */ -abstract class QRData protected constructor(val mode: Int, val data: String) { - abstract val length: Int - - abstract fun write(buffer: BitBuffer) - - fun getLengthInBits(type: Int): Int { - return if (type in 1..9) { - // 1 - 9 - - when (mode) { - Mode.MODE_NUMBER -> 10 - Mode.MODE_ALPHA_NUM -> 9 - Mode.MODE_8BIT_BYTE -> 8 - Mode.MODE_KANJI -> 8 - else -> throw IllegalArgumentException("mode:$mode") - } - } else if (type < 27) { - // 10 - 26 - - when (mode) { - Mode.MODE_NUMBER -> 12 - Mode.MODE_ALPHA_NUM -> 11 - Mode.MODE_8BIT_BYTE -> 16 - Mode.MODE_KANJI -> 10 - else -> throw IllegalArgumentException("mode:$mode") - } - } else if (type < 41) { - // 27 - 40 - - when (mode) { - Mode.MODE_NUMBER -> 14 - Mode.MODE_ALPHA_NUM -> 13 - Mode.MODE_8BIT_BYTE -> 16 - Mode.MODE_KANJI -> 12 - else -> throw IllegalArgumentException("mode:$mode") - } - } else { - throw IllegalArgumentException("type:$type") - } - } -} \ No newline at end of file diff --git a/uninit/common/src/commonMain/kotlin/uninit/common/qr/QRKanji.kt b/uninit/common/src/commonMain/kotlin/uninit/common/qr/QRKanji.kt deleted file mode 100644 index d6aa57f..0000000 --- a/uninit/common/src/commonMain/kotlin/uninit/common/qr/QRKanji.kt +++ /dev/null @@ -1,52 +0,0 @@ -package uninit.common.qr - -import QRUtil - - -internal class QRKanji(data: String?) : QRData(Mode.MODE_KANJI, data!!) { - @OptIn(ExperimentalStdlibApi::class) - override fun write(buffer: BitBuffer) { - throw RuntimeException("not implemented, \"SJIS\" encoding required.") -// try { -// val data: ByteArray = data.getBytes(QRUtil.getJISEncoding()) -// -// var i = 0 -// -// while (i + 1 < data.size) { -// var c = ((0xff and data[i].toInt()) shl 8) or (0xff and data[i + 1].toInt()) -// -// c -= if (0x8140 <= c && c <= 0x9FFC) { -// 0x8140 -// } else if (0xE040 <= c && c <= 0xEBBF) { -// 0xC140 -// } else { -// throw IllegalArgumentException( -// "illegal char at " + (i + 1) + "/" + c.toHexString() -// ) -// } -// -// c = ((c ushr 8) and 0xff) * 0xC0 + (c and 0xff) -// -// buffer.put(c, 13) -// -// i += 2 -// } -// -// if (i < data.size) { -// throw java.lang.IllegalArgumentException("illegal char at " + (i + 1)) -// } -// } catch (e: Exception) { -// throw RuntimeException(e.message) -// } - } - - override val length: Int - get() { - throw RuntimeException("not implemented, \"SJIS\" encoding required.") -// try { -// return data.getBytes(QRUtil.getJISEncoding()).size / 2 -// } catch (e: UnsupportedEncodingException) { -// throw java.lang.RuntimeException(e.message) -// } - } -} \ No newline at end of file diff --git a/uninit/common/src/commonMain/kotlin/uninit/common/qr/QRMath.kt b/uninit/common/src/commonMain/kotlin/uninit/common/qr/QRMath.kt deleted file mode 100644 index 59c87d4..0000000 --- a/uninit/common/src/commonMain/kotlin/uninit/common/qr/QRMath.kt +++ /dev/null @@ -1,49 +0,0 @@ -package uninit.common.qr - -/** - * QRMath - * @author Kazuhiko Arase - */ -internal object QRMath { - private val EXP_TABLE = IntArray(256) - private val LOG_TABLE: IntArray - - init { - for (i in 0..7) { - EXP_TABLE[i] = 1 shl i - } - - for (i in 8..255) { - EXP_TABLE[i] = (EXP_TABLE[i - 4] - xor EXP_TABLE[i - 5] - xor EXP_TABLE[i - 6] - xor EXP_TABLE[i - 8]) - } - - LOG_TABLE = IntArray(256) - for (i in 0..254) { - LOG_TABLE[EXP_TABLE[i]] = i - } - } - - fun glog(n: Int): Int { - if (n < 1) { - throw ArithmeticException("log($n)") - } - - return LOG_TABLE[n] - } - - fun gexp(n: Int): Int { - var n = n - while (n < 0) { - n += 255 - } - - while (n >= 256) { - n -= 255 - } - - return EXP_TABLE[n] - } -} \ No newline at end of file diff --git a/uninit/common/src/commonMain/kotlin/uninit/common/qr/QRNumber.kt b/uninit/common/src/commonMain/kotlin/uninit/common/qr/QRNumber.kt deleted file mode 100644 index 13dcedd..0000000 --- a/uninit/common/src/commonMain/kotlin/uninit/common/qr/QRNumber.kt +++ /dev/null @@ -1,49 +0,0 @@ -package uninit.common.qr - -/** - * QRNumber - * @author Kazuhiko Arase - */ -internal class QRNumber(data: String?) : QRData(Mode.MODE_NUMBER, data!!) { - override fun write(buffer: BitBuffer) { - val data = data - - var i = 0 - - while (i + 2 < data.length) { - val num = parseInt(data.substring(i, i + 3)) - buffer.put(num, 10) - i += 3 - } - - if (i < data.length) { - if (data.length - i == 1) { - val num = parseInt(data.substring(i, i + 1)) - buffer.put(num, 4) - } else if (data.length - i == 2) { - val num = parseInt(data.substring(i, i + 2)) - buffer.put(num, 7) - } - } - } - - override val length: Int - get() = data.length - - companion object { - private fun parseInt(s: String): Int { - var num = 0 - for (element in s) { - num = num * 10 + parseInt(element) - } - return num - } - - private fun parseInt(c: Char): Int { - if (c in '0'..'9') { - return c.code - '0'.code - } - throw IllegalArgumentException("illegal char :$c") - } - } -} \ No newline at end of file diff --git a/uninit/common/src/commonMain/kotlin/uninit/common/qr/QRUtil.kt b/uninit/common/src/commonMain/kotlin/uninit/common/qr/QRUtil.kt deleted file mode 100644 index 1a7e528..0000000 --- a/uninit/common/src/commonMain/kotlin/uninit/common/qr/QRUtil.kt +++ /dev/null @@ -1,384 +0,0 @@ - -import uninit.common.qr.* -import uninit.common.qr.MaskPattern -import uninit.common.qr.Polynomial -import kotlin.math.abs - -/** - * QRUtil - * @author Kazuhiko Arase - */ -internal object QRUtil { - val jISEncoding: String - get() = "SJIS" - - fun getPatternPosition(typeNumber: Int): IntArray { - return PATTERN_POSITION_TABLE[typeNumber - 1] - } - - private val PATTERN_POSITION_TABLE = arrayOf( - intArrayOf(), - intArrayOf(6, 18), - intArrayOf(6, 22), - intArrayOf(6, 26), - intArrayOf(6, 30), - intArrayOf(6, 34), - intArrayOf(6, 22, 38), - intArrayOf(6, 24, 42), - intArrayOf(6, 26, 46), - intArrayOf(6, 28, 50), - intArrayOf(6, 30, 54), - intArrayOf(6, 32, 58), - intArrayOf(6, 34, 62), - intArrayOf(6, 26, 46, 66), - intArrayOf(6, 26, 48, 70), - intArrayOf(6, 26, 50, 74), - intArrayOf(6, 30, 54, 78), - intArrayOf(6, 30, 56, 82), - intArrayOf(6, 30, 58, 86), - intArrayOf(6, 34, 62, 90), - intArrayOf(6, 28, 50, 72, 94), - intArrayOf(6, 26, 50, 74, 98), - intArrayOf(6, 30, 54, 78, 102), - intArrayOf(6, 28, 54, 80, 106), - intArrayOf(6, 32, 58, 84, 110), - intArrayOf(6, 30, 58, 86, 114), - intArrayOf(6, 34, 62, 90, 118), - intArrayOf(6, 26, 50, 74, 98, 122), - intArrayOf(6, 30, 54, 78, 102, 126), - intArrayOf(6, 26, 52, 78, 104, 130), - intArrayOf(6, 30, 56, 82, 108, 134), - intArrayOf(6, 34, 60, 86, 112, 138), - intArrayOf(6, 30, 58, 86, 114, 142), - intArrayOf(6, 34, 62, 90, 118, 146), - intArrayOf(6, 30, 54, 78, 102, 126, 150), - intArrayOf(6, 24, 50, 76, 102, 128, 154), - intArrayOf(6, 28, 54, 80, 106, 132, 158), - intArrayOf(6, 32, 58, 84, 110, 136, 162), - intArrayOf(6, 26, 54, 82, 110, 138, 166), - intArrayOf(6, 30, 58, 86, 114, 142, 170) - ) - - private val MAX_LENGTH = arrayOf( - arrayOf( - intArrayOf(41, 25, 17, 10), - intArrayOf(34, 20, 14, 8), - intArrayOf(27, 16, 11, 7), - intArrayOf(17, 10, 7, 4) - ), - arrayOf( - intArrayOf(77, 47, 32, 20), - intArrayOf(63, 38, 26, 16), - intArrayOf(48, 29, 20, 12), - intArrayOf(34, 20, 14, 8) - ), - arrayOf( - intArrayOf(127, 77, 53, 32), - intArrayOf(101, 61, 42, 26), - intArrayOf(77, 47, 32, 20), - intArrayOf(58, 35, 24, 15) - ), - arrayOf( - intArrayOf(187, 114, 78, 48), - intArrayOf(149, 90, 62, 38), - intArrayOf(111, 67, 46, 28), - intArrayOf(82, 50, 34, 21) - ), - arrayOf( - intArrayOf(255, 154, 106, 65), - intArrayOf(202, 122, 84, 52), - intArrayOf(144, 87, 60, 37), - intArrayOf(106, 64, 44, 27) - ), - arrayOf( - intArrayOf(322, 195, 134, 82), - intArrayOf(255, 154, 106, 65), - intArrayOf(178, 108, 74, 45), - intArrayOf(139, 84, 58, 36) - ), - arrayOf( - intArrayOf(370, 224, 154, 95), - intArrayOf(293, 178, 122, 75), - intArrayOf(207, 125, 86, 53), - intArrayOf(154, 93, 64, 39) - ), - arrayOf( - intArrayOf(461, 279, 192, 118), - intArrayOf(365, 221, 152, 93), - intArrayOf(259, 157, 108, 66), - intArrayOf(202, 122, 84, 52) - ), - arrayOf( - intArrayOf(552, 335, 230, 141), - intArrayOf(432, 262, 180, 111), - intArrayOf(312, 189, 130, 80), - intArrayOf(235, 143, 98, 60) - ), - arrayOf( - intArrayOf(652, 395, 271, 167), - intArrayOf(513, 311, 213, 131), - intArrayOf(364, 221, 151, 93), - intArrayOf(288, 174, 119, 74) - ) - ) - - fun getMaxLength(typeNumber: Int, mode: Int, errorCorrectionLevel: Int): Int { - val t = typeNumber - 1 - var e = 0 - var m = 0 - - e = when (errorCorrectionLevel) { - ErrorCorrectionLevel.L -> 0 - ErrorCorrectionLevel.M -> 1 - ErrorCorrectionLevel.Q -> 2 - ErrorCorrectionLevel.H -> 3 - else -> throw IllegalArgumentException("e:$errorCorrectionLevel") - } - m = when (mode) { - Mode.MODE_NUMBER -> 0 - Mode.MODE_ALPHA_NUM -> 1 - Mode.MODE_8BIT_BYTE -> 2 - Mode.MODE_KANJI -> 3 - else -> throw IllegalArgumentException("m:$mode") - } - return MAX_LENGTH[t][e][m] - } - - - /** - * エラー訂正多項式を取得する。 - */ - fun getErrorCorrectPolynomial(errorCorrectLength: Int): Polynomial { - var a: Polynomial = Polynomial(intArrayOf(1)) - - for (i in 0 until errorCorrectLength) { - a = a.multiply(Polynomial(intArrayOf(1, QRMath.gexp(i)))) - } - - return a - } - - /** - * 指定されたパターンのマスクを取得する。 - */ - fun getMask(maskPattern: Int, i: Int, j: Int): Boolean { - return when (maskPattern) { - MaskPattern.PATTERN000 -> (i + j) % 2 == 0 - MaskPattern.PATTERN001 -> i % 2 == 0 - MaskPattern.PATTERN010 -> j % 3 == 0 - MaskPattern.PATTERN011 -> (i + j) % 3 == 0 - MaskPattern.PATTERN100 -> (i / 2 + j / 3) % 2 == 0 - MaskPattern.PATTERN101 -> i * j % 2 + (i * j) % 3 == 0 - MaskPattern.PATTERN110 -> ((i * j) % 2 + (i * j) % 3) % 2 == 0 - MaskPattern.PATTERN111 -> ((i * j) % 3 + (i + j) % 2) % 2 == 0 - - else -> throw IllegalArgumentException("mask:$maskPattern") - } - } - - /** - * 失点を取得する - */ - fun getLostPoint(qrCode: QRCode): Int { - val moduleCount = qrCode.moduleCount - - var lostPoint = 0 - - - // LEVEL1 - for (row in 0 until moduleCount) { - for (col in 0 until moduleCount) { - var sameCount = 0 - val dark = qrCode.isDark(row, col) - - for (r in -1..1) { - if (row + r < 0 || moduleCount <= row + r) { - continue - } - - for (c in -1..1) { - if (col + c < 0 || moduleCount <= col + c) { - continue - } - - if (r == 0 && c == 0) { - continue - } - - if (dark == qrCode.isDark(row + r, col + c)) { - sameCount++ - } - } - } - - if (sameCount > 5) { - lostPoint += (3 + sameCount - 5) - } - } - } - - // LEVEL2 - for (row in 0 until moduleCount - 1) { - for (col in 0 until moduleCount - 1) { - var count = 0 - if (qrCode.isDark(row, col)) count++ - if (qrCode.isDark(row + 1, col)) count++ - if (qrCode.isDark(row, col + 1)) count++ - if (qrCode.isDark(row + 1, col + 1)) count++ - if (count == 0 || count == 4) { - lostPoint += 3 - } - } - } - - // LEVEL3 - for (row in 0 until moduleCount) { - for (col in 0 until moduleCount - 6) { - if (qrCode.isDark(row, col) - && !qrCode.isDark(row, col + 1) - && qrCode.isDark(row, col + 2) - && qrCode.isDark(row, col + 3) - && qrCode.isDark(row, col + 4) - && !qrCode.isDark(row, col + 5) - && qrCode.isDark(row, col + 6) - ) { - lostPoint += 40 - } - } - } - - for (col in 0 until moduleCount) { - for (row in 0 until moduleCount - 6) { - if (qrCode.isDark(row, col) - && !qrCode.isDark(row + 1, col) - && qrCode.isDark(row + 2, col) - && qrCode.isDark(row + 3, col) - && qrCode.isDark(row + 4, col) - && !qrCode.isDark(row + 5, col) - && qrCode.isDark(row + 6, col) - ) { - lostPoint += 40 - } - } - } - - // LEVEL4 - var darkCount = 0 - - for (col in 0 until moduleCount) { - for (row in 0 until moduleCount) { - if (qrCode.isDark(row, col)) { - darkCount++ - } - } - } - - val ratio = (abs((100 * darkCount / moduleCount / moduleCount - 50).toDouble()) / 5).toInt() - lostPoint += ratio * 10 - - return lostPoint - } - - fun getMode(s: String): Int { - if (isAlphaNum(s)) { - if (isNumber(s)) { - return Mode.MODE_NUMBER - } - return Mode.MODE_ALPHA_NUM - } else if (false) { - return Mode.MODE_KANJI - } else { - return Mode.MODE_8BIT_BYTE - } - } - - private fun isNumber(s: String): Boolean { - for (i in 0 until s.length) { - val c = s[i] - if (!('0' <= c && c <= '9')) { - return false - } - } - return true - } - - private fun isAlphaNum(s: String): Boolean { - for (i in 0 until s.length) { - val c = s[i] - if (!('0' <= c && c <= '9') && !('A' <= c && c <= 'Z') && " $%*+-./:".indexOf( - c - ) == -1 - ) { - return false - } - } - return true - } - - @Deprecated(level = DeprecationLevel.ERROR, message = "not implemented, \"SJIS\" encoding required.", - replaceWith = ReplaceWith("false") - ) - private fun isKanji(s: String): Boolean { - return false -// try { -// val data: ByteArray = s.toByteArray(charset(jISEncoding)) -// -// var i = 0 -// -// while (i + 1 < data.size) { -// val c = ((0xff and data[i].toInt()) shl 8) or (0xff and data[i + 1].toInt()) -// -// if (!(0x8140 <= c && c <= 0x9FFC) && !(0xE040 <= c && c <= 0xEBBF)) { -// return false -// } -// -// i += 2 -// } -// -// if (i < data.size) { -// return false -// } -// -// return true -// } catch (e: UnsupportedEncodingException) { -// throw RuntimeException(e.message) -// } - } - - private const val G15 = ((1 shl 10) or (1 shl 8) or (1 shl 5) - or (1 shl 4) or (1 shl 2) or (1 shl 1) or (1 shl 0)) - - private const val G18 = ((1 shl 12) or (1 shl 11) or (1 shl 10) - or (1 shl 9) or (1 shl 8) or (1 shl 5) or (1 shl 2) or (1 shl 0)) - - private const val G15_MASK = ((1 shl 14) or (1 shl 12) or (1 shl 10) - or (1 shl 4) or (1 shl 1)) - - fun getBCHTypeInfo(data: Int): Int { - var d = data shl 10 - while (getBCHDigit(d) - getBCHDigit(G15) >= 0) { - d = d xor (G15 shl (getBCHDigit(d) - getBCHDigit(G15))) - } - return ((data shl 10) or d) xor G15_MASK - } - - fun getBCHTypeNumber(data: Int): Int { - var d = data shl 12 - while (getBCHDigit(d) - getBCHDigit(G18) >= 0) { - d = d xor (G18 shl (getBCHDigit(d) - getBCHDigit(G18))) - } - return (data shl 12) or d - } - - private fun getBCHDigit(data: Int): Int { - var data = data - var digit = 0 - - while (data != 0) { - digit++ - data = data ushr 1 - } - - return digit - } -} \ No newline at end of file diff --git a/uninit/common/src/commonMain/kotlin/uninit/common/qr/RSBlock.kt b/uninit/common/src/commonMain/kotlin/uninit/common/qr/RSBlock.kt deleted file mode 100644 index 01f8df7..0000000 --- a/uninit/common/src/commonMain/kotlin/uninit/common/qr/RSBlock.kt +++ /dev/null @@ -1,251 +0,0 @@ -package uninit.common.qr - - -/** - * RSBlock - * @author Kazuhiko Arase - */ -internal class RSBlock private constructor(val totalCount: Int, val dataCount: Int) { - companion object { - private val RS_BLOCK_TABLE = arrayOf( // L - // M - // Q - // H - // 1 - intArrayOf(1, 26, 19), - intArrayOf(1, 26, 16), - intArrayOf(1, 26, 13), - intArrayOf(1, 26, 9), // 2 - - intArrayOf(1, 44, 34), - intArrayOf(1, 44, 28), - intArrayOf(1, 44, 22), - intArrayOf(1, 44, 16), // 3 - - intArrayOf(1, 70, 55), - intArrayOf(1, 70, 44), - intArrayOf(2, 35, 17), - intArrayOf(2, 35, 13), // 4 - - intArrayOf(1, 100, 80), - intArrayOf(2, 50, 32), - intArrayOf(2, 50, 24), - intArrayOf(4, 25, 9), // 5 - - intArrayOf(1, 134, 108), - intArrayOf(2, 67, 43), - intArrayOf(2, 33, 15, 2, 34, 16), - intArrayOf(2, 33, 11, 2, 34, 12), // 6 - - intArrayOf(2, 86, 68), - intArrayOf(4, 43, 27), - intArrayOf(4, 43, 19), - intArrayOf(4, 43, 15), // 7 - - intArrayOf(2, 98, 78), - intArrayOf(4, 49, 31), - intArrayOf(2, 32, 14, 4, 33, 15), - intArrayOf(4, 39, 13, 1, 40, 14), // 8 - - intArrayOf(2, 121, 97), - intArrayOf(2, 60, 38, 2, 61, 39), - intArrayOf(4, 40, 18, 2, 41, 19), - intArrayOf(4, 40, 14, 2, 41, 15), // 9 - - intArrayOf(2, 146, 116), - intArrayOf(3, 58, 36, 2, 59, 37), - intArrayOf(4, 36, 16, 4, 37, 17), - intArrayOf(4, 36, 12, 4, 37, 13), // 10 - - intArrayOf(2, 86, 68, 2, 87, 69), - intArrayOf(4, 69, 43, 1, 70, 44), - intArrayOf(6, 43, 19, 2, 44, 20), - intArrayOf(6, 43, 15, 2, 44, 16), // 11 - - intArrayOf(4, 101, 81), - intArrayOf(1, 80, 50, 4, 81, 51), - intArrayOf(4, 50, 22, 4, 51, 23), - intArrayOf(3, 36, 12, 8, 37, 13), // 12 - - intArrayOf(2, 116, 92, 2, 117, 93), - intArrayOf(6, 58, 36, 2, 59, 37), - intArrayOf(4, 46, 20, 6, 47, 21), - intArrayOf(7, 42, 14, 4, 43, 15), // 13 - - intArrayOf(4, 133, 107), - intArrayOf(8, 59, 37, 1, 60, 38), - intArrayOf(8, 44, 20, 4, 45, 21), - intArrayOf(12, 33, 11, 4, 34, 12), // 14 - - intArrayOf(3, 145, 115, 1, 146, 116), - intArrayOf(4, 64, 40, 5, 65, 41), - intArrayOf(11, 36, 16, 5, 37, 17), - intArrayOf(11, 36, 12, 5, 37, 13), // 15 - - intArrayOf(5, 109, 87, 1, 110, 88), - intArrayOf(5, 65, 41, 5, 66, 42), - intArrayOf(5, 54, 24, 7, 55, 25), - intArrayOf(11, 36, 12, 7, 37, 13), // 16 - - intArrayOf(5, 122, 98, 1, 123, 99), - intArrayOf(7, 73, 45, 3, 74, 46), - intArrayOf(15, 43, 19, 2, 44, 20), - intArrayOf(3, 45, 15, 13, 46, 16), // 17 - - intArrayOf(1, 135, 107, 5, 136, 108), - intArrayOf(10, 74, 46, 1, 75, 47), - intArrayOf(1, 50, 22, 15, 51, 23), - intArrayOf(2, 42, 14, 17, 43, 15), // 18 - - intArrayOf(5, 150, 120, 1, 151, 121), - intArrayOf(9, 69, 43, 4, 70, 44), - intArrayOf(17, 50, 22, 1, 51, 23), - intArrayOf(2, 42, 14, 19, 43, 15), // 19 - - intArrayOf(3, 141, 113, 4, 142, 114), - intArrayOf(3, 70, 44, 11, 71, 45), - intArrayOf(17, 47, 21, 4, 48, 22), - intArrayOf(9, 39, 13, 16, 40, 14), // 20 - - intArrayOf(3, 135, 107, 5, 136, 108), - intArrayOf(3, 67, 41, 13, 68, 42), - intArrayOf(15, 54, 24, 5, 55, 25), - intArrayOf(15, 43, 15, 10, 44, 16), // 21 - - intArrayOf(4, 144, 116, 4, 145, 117), - intArrayOf(17, 68, 42), - intArrayOf(17, 50, 22, 6, 51, 23), - intArrayOf(19, 46, 16, 6, 47, 17), // 22 - - intArrayOf(2, 139, 111, 7, 140, 112), - intArrayOf(17, 74, 46), - intArrayOf(7, 54, 24, 16, 55, 25), - intArrayOf(34, 37, 13), // 23 - - intArrayOf(4, 151, 121, 5, 152, 122), - intArrayOf(4, 75, 47, 14, 76, 48), - intArrayOf(11, 54, 24, 14, 55, 25), - intArrayOf(16, 45, 15, 14, 46, 16), // 24 - - intArrayOf(6, 147, 117, 4, 148, 118), - intArrayOf(6, 73, 45, 14, 74, 46), - intArrayOf(11, 54, 24, 16, 55, 25), - intArrayOf(30, 46, 16, 2, 47, 17), // 25 - - intArrayOf(8, 132, 106, 4, 133, 107), - intArrayOf(8, 75, 47, 13, 76, 48), - intArrayOf(7, 54, 24, 22, 55, 25), - intArrayOf(22, 45, 15, 13, 46, 16), // 26 - - intArrayOf(10, 142, 114, 2, 143, 115), - intArrayOf(19, 74, 46, 4, 75, 47), - intArrayOf(28, 50, 22, 6, 51, 23), - intArrayOf(33, 46, 16, 4, 47, 17), // 27 - - intArrayOf(8, 152, 122, 4, 153, 123), - intArrayOf(22, 73, 45, 3, 74, 46), - intArrayOf(8, 53, 23, 26, 54, 24), - intArrayOf(12, 45, 15, 28, 46, 16), // 28 - - intArrayOf(3, 147, 117, 10, 148, 118), - intArrayOf(3, 73, 45, 23, 74, 46), - intArrayOf(4, 54, 24, 31, 55, 25), - intArrayOf(11, 45, 15, 31, 46, 16), // 29 - - intArrayOf(7, 146, 116, 7, 147, 117), - intArrayOf(21, 73, 45, 7, 74, 46), - intArrayOf(1, 53, 23, 37, 54, 24), - intArrayOf(19, 45, 15, 26, 46, 16), // 30 - - intArrayOf(5, 145, 115, 10, 146, 116), - intArrayOf(19, 75, 47, 10, 76, 48), - intArrayOf(15, 54, 24, 25, 55, 25), - intArrayOf(23, 45, 15, 25, 46, 16), // 31 - - intArrayOf(13, 145, 115, 3, 146, 116), - intArrayOf(2, 74, 46, 29, 75, 47), - intArrayOf(42, 54, 24, 1, 55, 25), - intArrayOf(23, 45, 15, 28, 46, 16), // 32 - - intArrayOf(17, 145, 115), - intArrayOf(10, 74, 46, 23, 75, 47), - intArrayOf(10, 54, 24, 35, 55, 25), - intArrayOf(19, 45, 15, 35, 46, 16), // 33 - - intArrayOf(17, 145, 115, 1, 146, 116), - intArrayOf(14, 74, 46, 21, 75, 47), - intArrayOf(29, 54, 24, 19, 55, 25), - intArrayOf(11, 45, 15, 46, 46, 16), // 34 - - intArrayOf(13, 145, 115, 6, 146, 116), - intArrayOf(14, 74, 46, 23, 75, 47), - intArrayOf(44, 54, 24, 7, 55, 25), - intArrayOf(59, 46, 16, 1, 47, 17), // 35 - - intArrayOf(12, 151, 121, 7, 152, 122), - intArrayOf(12, 75, 47, 26, 76, 48), - intArrayOf(39, 54, 24, 14, 55, 25), - intArrayOf(22, 45, 15, 41, 46, 16), // 36 - - intArrayOf(6, 151, 121, 14, 152, 122), - intArrayOf(6, 75, 47, 34, 76, 48), - intArrayOf(46, 54, 24, 10, 55, 25), - intArrayOf(2, 45, 15, 64, 46, 16), // 37 - - intArrayOf(17, 152, 122, 4, 153, 123), - intArrayOf(29, 74, 46, 14, 75, 47), - intArrayOf(49, 54, 24, 10, 55, 25), - intArrayOf(24, 45, 15, 46, 46, 16), // 38 - - intArrayOf(4, 152, 122, 18, 153, 123), - intArrayOf(13, 74, 46, 32, 75, 47), - intArrayOf(48, 54, 24, 14, 55, 25), - intArrayOf(42, 45, 15, 32, 46, 16), // 39 - - intArrayOf(20, 147, 117, 4, 148, 118), - intArrayOf(40, 75, 47, 7, 76, 48), - intArrayOf(43, 54, 24, 22, 55, 25), - intArrayOf(10, 45, 15, 67, 46, 16), // 40 - - intArrayOf(19, 148, 118, 6, 149, 119), - intArrayOf(18, 75, 47, 31, 76, 48), - intArrayOf(34, 54, 24, 34, 55, 25), - intArrayOf(20, 45, 15, 61, 46, 16) - ) - - fun getRSBlocks(typeNumber: Int, errorCorrectionLevel: Int): Array { - val rsBlock = getRsBlockTable(typeNumber, errorCorrectionLevel) - val length = rsBlock.size / 3 - - - val list: MutableList = ArrayList() - - for (i in 0 until length) { - val count = rsBlock[i * 3 + 0] - val totalCount = rsBlock[i * 3 + 1] - val dataCount = rsBlock[i * 3 + 2] - - for (j in 0 until count) { - list.add(RSBlock(totalCount, dataCount)) - } - } - - return list.toTypedArray() - } - - private fun getRsBlockTable(typeNumber: Int, errorCorrectionLevel: Int): IntArray { - try { - when (errorCorrectionLevel) { - ErrorCorrectionLevel.L -> return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0] - ErrorCorrectionLevel.M -> return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1] - ErrorCorrectionLevel.Q -> return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2] - ErrorCorrectionLevel.H -> return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3] - else -> {} - } - } catch (e: Exception) { - } - - throw IllegalArgumentException("tn:$typeNumber/ecl:$errorCorrectionLevel") - } - } -} \ No newline at end of file diff --git a/uninit/common/src/desktopMain/kotlin/uninit/common/platform/Text.desktop.kt b/uninit/common/src/desktopMain/kotlin/uninit/common/platform/Text.desktop.kt new file mode 100644 index 0000000..270c582 --- /dev/null +++ b/uninit/common/src/desktopMain/kotlin/uninit/common/platform/Text.desktop.kt @@ -0,0 +1,7 @@ +package uninit.common.platform + +import java.nio.charset.Charset + +internal actual fun text_StringToByteArrayWithEncoding(text: String, encoding: String): ByteArray { + return text.toByteArray(Charset.forName(encoding)) +} \ No newline at end of file diff --git a/uninit/common/src/iosMain/kotlin/uninit/common/platform/Text.ios.kt b/uninit/common/src/iosMain/kotlin/uninit/common/platform/Text.ios.kt new file mode 100644 index 0000000..3579666 --- /dev/null +++ b/uninit/common/src/iosMain/kotlin/uninit/common/platform/Text.ios.kt @@ -0,0 +1,5 @@ +package uninit.common.platform + +internal actual fun text_StringToByteArrayWithEncoding(text: String, encoding: String): ByteArray { + TODO("Waiting on iOS implementation...") +} \ No newline at end of file