diff --git a/docs/wear-os-implementation-plan.md b/docs/wear-os-implementation-plan.md new file mode 100644 index 00000000..54d3eee7 --- /dev/null +++ b/docs/wear-os-implementation-plan.md @@ -0,0 +1,241 @@ +# Bitchat for Pixel Watch — Implementation Plan + +> **Status tracker**: each milestone carries a status (`pending` / `in-progress` / `done`) and a +> checklist. A milestone may only be started when the previous milestone's success criteria pass. +> Update statuses in this file as work progresses. + +| Milestone | Title | Status | +|-----------|-------|--------| +| M0 | Scaffolding & plan document | done | +| M1 | Shared core compiles on Wear | pending | +| M2 | BLE transport & background service on watch | pending | +| M3 | Global chat | pending | +| M4 | Noise DMs & people screen | pending | +| M5 | File/image receive & display — **DEFERRED** (post-M7, later day) | deferred | +| M6 | ADB test hook & mesh_lab interop | pending | +| M7 | Polish & final design pass | pending | + +--- + +## 1. Context for a fresh coding agent + +- **Reference app**: this repository (`bitchat-android`) is a fully working, decentralized BLE mesh + chat client. Single Gradle module `:app`, root package `com.bitchat.android`, applicationId + `com.bitchat.droid`. See `AGENTS.md` for the full architecture overview. +- **Goal**: a new `:wear` Gradle module (applicationId `com.bitchat.watch`) — a standalone Wear OS + app for the Pixel Watch. **Bluetooth mesh only**: global chat, Noise-encrypted direct messages, + and receiving/displaying files & images. It must be a fully interoperable bitchat client: scan, + advertise, connect, relay, handshake, and exchange messages with the Android (and iOS) apps. +- **Explicitly out of scope**: no internet features (no Nostr, no Tor/Arti, no relays), no GPS / + geohash / location channels, no Wi-Fi Aware, no hotspot/APK sharing, no voice notes recording. + The watch manifest must not even declare `INTERNET` or location permissions. +- **Hard constraint**: **zero modifications to `:app` production code.** The only allowed changes + to shared repo files are: `settings.gradle.kts` (add `include(":wear")`), entries in + `gradle/libs.versions.toml` (new wear dependencies only), the new `wear/` directory, and docs. + All shared Kotlin code is consumed by the `:wear` module *in place* via Gradle source sets — + files are never moved, copied, or edited. + +## 2. Code reuse strategy (shared source sets) + +Wear OS is Android. `android.util.Log`, `android.bluetooth.*`, `EncryptedSharedPreferences` +(androidx.security), BouncyCastle, and coroutines all work on the watch, so the vast majority of +the bitchat protocol stack compiles unmodified. + +In `wear/build.gradle.kts`: + +```kotlin +sourceSets["main"].java.srcDir("../app/src/main/java") // with include filters (see below) +``` + +**Include** (iteratively refined by fixing compile errors — the include list lives in +`wear/build.gradle.kts` with comments): + +- `protocol/**` — wire format, `BinaryProtocol`, `CompressionUtil`, `MessagePadding` +- `model/**` — `BitchatMessage`, `BitchatFilePacket`, `FragmentPayload`, `NoiseEncrypted`, + `IdentityAnnouncement`, `RoutedPacket` +- `noise/**` — `NoiseSession`, `NoiseSessionManager`, `NoiseEncryptionService`, + `NoiseChannelEncryption`, vendored pure-Java `noise/southernstorm/**` +- `crypto/**` — `EncryptionService` +- `identity/**` — `SecureIdentityStateManager` +- `mesh/**` — BLE stack (`BluetoothConnectionManager`, GATT server/client managers, broadcaster, + tracker, permission manager), `FragmentManager`, `SecurityManager`, `PacketProcessor`, + `MessageHandler`, `PeerManager`, `StoreForwardManager`, `MeshTransport`, `MeshService`, + `TransferProgressManager`, `PrivateMediaTransfer`, `PowerManager` +- `services/AppStateStore.kt` — process-wide state store +- `util/AppConstants.kt` — shared constants (GATT UUIDs, fragmentation sizes) +- Small transitive deps the compiler reveals (known: `ui/debug/DebugSettingsManager.kt` is + referenced from the mesh layer — include the file, not the package) + +**Exclude**: `ui/**` (except forced single-file includes), `onboarding/**`, `nostr/**`, `net/**`, +`geohash/**`, `wifi-aware/**`, `hotspot/**`, `features/voice/**`, `service/MeshForegroundService.kt` +(wear gets its own service), `BitchatApplication.kt`, `MainActivity.kt`. + +**Tests**: the app's own unit tests for shared packages (`protocol`, `noise`, `crypto`, `mesh`) +are wired into the `:wear` test source set the same way (srcDir + includes), so shared behavior is +continuously verified on both modules. + +**Resources**: font files cannot be selectively shared via srcDir cleanly — copy the 4 Geist Mono +font files (`app/src/main/res/font/geist_mono_*`) into `wear/src/main/res/font/`. Theme/palette/ +peer-color logic is re-created as wear-owned files mirroring `ui/theme/` values exactly. + +## 3. Watch UX design + +Wear Compose Material3 (round-screen safe by default): + +- **Screens**: Chat (global timeline) → People (connected peers w/ RSSI, unread badges) → + DM conversation. Edge-swipe back, `TimeText` scaffold, rotary crown scrolling. +- **Visual identity** (mirrors `ui/theme/` exactly): black background `#000000`, green primary + `#32D74B`, error `#FF453A`, orange accent for self/mentions, djb2-hash stable peer colors + (`PeerColors.kt` algorithm), Geist Mono typography, `BitchatMotion` timing tokens + (120/180/240 ms) for all animations. +- **Input**: text field using the Pixel Watch Gboard IME, plus voice dictation via + `RecognizerIntent`. Haptic feedback on incoming messages. +- **Background**: wear-owned foreground service (type `connectedDevice`) keeps scan/advertise + alive; shared `PowerManager` provides duty-cycling. + +## 4. Hardware & test environment + +- Pixel Watch connected via ADB (target device for all milestones; screencaps via + `adb exec-out screencap`). +- Two phones running the Android bitchat app, also on ADB, for interop testing (used heavily from + M6; manual interop checks from M2 onward). +- Design verification: at every UI milestone, take ADB screencaps of every screen and review them + for round-screen clipping, element visibility, contrast, and touch-target size. A milestone does + not pass until its screencap set is approved. + +## 5. Risks & notes + +- `BluetoothMeshService` (legacy monolith) vs `MeshCore` — prefer wiring the shared components + directly (MeshCore-style composition) in the wear service. +- `mesh/` references `ui/debug/DebugSettingsManager` — include that single file; do not pull in + the debug UI sheet. +- Wear BLE MTUs are small; the shared fragmentation layer (469-byte fragments) already handles + this. +- `EncryptedSharedPreferences` (androidx.security-crypto) works on Wear OS — identity persistence + is reused as-is. +- Watch has no camera/gallery: file transfer is **receive + display only** (confirmed decision). + +--- + +## Milestones + +### M0 — Scaffolding & plan document + +- [x] Write this plan to `docs/wear-os-implementation-plan.md` +- [x] Create `:wear` module: `wear/build.gradle.kts`, manifest + (``, standalone, BT permissions, + **no INTERNET/location**), `MainActivity` with hello-world screen using the ported theme +- [x] Add Wear Compose dependencies to `gradle/libs.versions.toml`; `include(":wear")` in + `settings.gradle.kts` +- [x] Build, install, and launch on the physical Pixel Watch via ADB; take first screencap + +**Success criteria**: `./gradlew :wear:assembleDebug` green; app launches on the watch; +`git diff --name-only` shows no changes under `app/src/`. +**Result**: PASSED — installed on Pixel Watch 3 (serial 4C201JEAYW0020), launch screencap shows +"bitchat" wordmark (green `#32D74B`, Geist Mono, black background) correctly centered on the +round display. No `app/src/` changes. + +--- + +### M1 — Shared core compiles on Wear + +- [ ] Configure srcDir include list in `wear/build.gradle.kts`; resolve transitive dependencies by + extending includes (never by copying Kotlin sources) +- [ ] Copy Geist Mono fonts; create wear theme/palette/peer-color files mirroring `ui/theme/` +- [ ] Wire shared unit tests (`protocol`, `noise`, `crypto`, `mesh`) into `:wear` test source set +- [ ] `./gradlew :app:test :wear:test` green + +**Success criteria**: the entire shared stack (protocol, noise, crypto, identity, mesh, model, +AppStateStore) compiles into `:wear`; both modules' unit tests pass; `app/src/` untouched. + +--- + +### M2 — BLE transport & background service on watch + +- [ ] Wear onboarding flow: Bluetooth-enable check + runtime permission requests + (`BLUETOOTH_SCAN/CONNECT/ADVERTISE`), watch-styled screens +- [ ] `WearMeshService` foreground service (type `connectedDevice`); wire shared + `BluetoothConnectionManager` + mesh components; start scanning + advertising +- [ ] Internal debug screen: discovered peers with RSSI (temporary, replaced by real UI in M3/M4) +- [ ] Manual interop check: watch and one phone mutually discover + +**Success criteria**: the phone's bitchat app lists the watch as a connected peer and vice versa +(logcat + screencap evidence); mesh survives the screen turning off (ambient mode) for 5 minutes. + +--- + +### M3 — Global chat + +- [ ] Nickname onboarding; identity announcement over the mesh +- [ ] Send/receive/relay public `BitchatMessage`s (relay/TTL comes free from shared mesh code) +- [ ] Chat timeline UI (`ScalingLazyColumn`, message bubbles per bitchat style, peer colors, + timestamps) + composer (IME + `RecognizerIntent` dictation) + incoming-message haptics +- [ ] Design check: ADB screencaps of onboarding, chat (empty/populated), composer; review for + round-screen clipping/visibility/contrast + +**Success criteria**: two-way public chat between watch and phone; messages the watch relays reach +a second phone that is only connected through the first (relay proof); screencap set approved. + +--- + +### M4 — Noise DMs & people screen + +- [ ] People screen: connected peers, nicknames, RSSI, unread-DM badges +- [ ] Tap peer → Noise XX handshake (shared `EncryptionService`/`NoiseSessionManager`) → DM thread +- [ ] DM conversation UI; unread counters; delivery/read receipts if supported by shared code +- [ ] Identity persistence (`EncryptedSharedPreferences`); stale-session detection & automatic + re-handshake after watch app restart +- [ ] Design check: screencaps of people screen, handshake state, DM thread + +**Success criteria**: encrypted DM round trip with the phone; DMs survive a watch app restart +(session recovery); screencap set approved. + +--- + +### M5 — File/image receive & display — **DEFERRED** + +> Deferred to a later day (after M7). Milestones M6 and M7 do not depend on M5 and proceed +> without it. The mesh_lab `file` scenario for the watch is skipped until M5 is un-deferred. + +- [ ] Receive broadcast files (`MessageType.FILE_TRANSFER`, `BitchatFilePacket` TLV decode, + fragment reassembly — all shared) +- [ ] Receive Noise-encrypted private files (`NoisePayloadType.FILE_TRANSFER`) +- [ ] Inline image rendering in chat timelines; full-screen image viewer (pinch/crown zoom); + non-image files saved with a way to open/share them +- [ ] Transfer progress indicator; respect shared fragment/size caps +- [ ] Design check: screencaps of inline image, full-screen viewer, transfer progress + +**Success criteria**: phone→watch image renders inline in both global chat and DM; SHA-256 of +received file matches sender; screencap set approved. (Sending files from the watch is out of +scope.) + +--- + +### M6 — ADB test hook & mesh_lab interop + +- [ ] Wear debug-only `TestHookReceiver` (`wear/src/debug/`) mirroring the phone's command set: + `ping`, `start`, `stop`, `whoami`, `set_nickname`, `scan`, `peers`, `connect`, `handshake`, + `session`, `announce`, `broadcast_msg`, `dm_send`, `dm_recv`, `msg_recv`, `file_recv`, `state`, + `clear_results` — broadcast action `com.bitchat.watch.TEST_HOOK`, same JSON-result-file protocol +- [ ] Extend `tools/release_gate/mesh_lab.py`: `--serial-watch` argument and phone↔watch scenarios + (`dm`, `broadcast`, `session_recovery`, `all`; `file` excluded while M5 is deferred), reusing + the existing `Device`/`cmd` machinery +- [ ] Run full scenario suite phone↔watch; store evidence JSON + +**Success criteria**: +`python3 tools/release_gate/mesh_lab.py scenario all --serial-a --serial-watch ` +exits 0 with evidence files; no manual intervention. + +--- + +### M7 — Polish & final design pass + +- [ ] Animations/transitions per `BitchatMotion` tokens; message-appear animations; screen + transitions; rotary scroll feel; splash screen & app icon (bitchat wordmark style) +- [ ] Power/battery: verify shared `PowerManager` duty-cycling behaves on Wear; ambient-mode + behavior; memory audit (fragment/image caps) +- [ ] Full screencap design review of every screen and state; fix all findings +- [ ] Update this document: all milestones `done`; add a short "how to build/run/test" section + +**Success criteria**: all milestones marked `done`; interop suite green; final screencap set +approved; a fresh agent can build, install, and test the watch app from this document alone. diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 95e83d07..4745b097 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -21,6 +21,10 @@ navigation-compose = "2.9.8" # Accompanist accompanist-permissions = "0.37.3" +# Wear OS +wear-compose = "1.6.2" +wear-tooling-preview = "1.0.0" + # Cryptography bouncycastle = "1.85" tink-android = "1.23.0" @@ -95,6 +99,11 @@ androidx-navigation-compose = { module = "androidx.navigation:navigation-compose # Accompanist accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist-permissions" } +# Wear OS +androidx-wear-compose-foundation = { module = "androidx.wear.compose:compose-foundation", version.ref = "wear-compose" } +androidx-wear-compose-material3 = { module = "androidx.wear.compose:compose-material3", version.ref = "wear-compose" } +androidx-wear-tooling-preview = { module = "androidx.wear:wear-tooling-preview", version.ref = "wear-tooling-preview" } + # Cryptography bouncycastle-bcprov = { module = "org.bouncycastle:bcprov-jdk18on", version.ref = "bouncycastle" } google-tink-android = { module = "com.google.crypto.tink:tink-android", version.ref = "tink-android" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 53c50616..0d96a216 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -17,4 +17,5 @@ dependencyResolutionManagement { rootProject.name = "bitchat-android" include(":app") +include(":wear") // Using published Arti AAR; local module not included diff --git a/wear/build.gradle.kts b/wear/build.gradle.kts new file mode 100644 index 00000000..72c4d57c --- /dev/null +++ b/wear/build.gradle.kts @@ -0,0 +1,89 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.parcelize) + alias(libs.plugins.kotlin.compose) +} + +android { + namespace = "com.bitchat.watch" + compileSdk = libs.versions.compileSdk.get().toInt() + + defaultConfig { + applicationId = "com.bitchat.watch" + minSdk = 30 // Wear OS 3 (Pixel Watch 1); BLE APIs match the phone app's usage + targetSdk = libs.versions.targetSdk.get().toInt() + versionCode = 1 + versionName = "0.1.0" + + vectorDrawables { + useSupportLibrary = true + } + } + + buildTypes { + release { + isMinifyEnabled = true + isShrinkResources = true + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + buildFeatures { + compose = true + buildConfig = true + } + packaging { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } + } + lint { + abortOnError = false + checkReleaseBuilds = false + } +} + +kotlin { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_11) + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.activity.compose) + + // Wear Compose + implementation(libs.androidx.wear.compose.foundation) + implementation(libs.androidx.wear.compose.material3) + implementation(libs.androidx.wear.tooling.preview) + + // Lifecycle + implementation(libs.bundles.lifecycle) + implementation(libs.androidx.lifecycle.process) + + // Coroutines + implementation(libs.kotlinx.coroutines.android) + + // Cryptography (shared Noise/encryption stack) + implementation(libs.bouncycastle.bcprov) + + // JSON (BitchatMessage model) + implementation(libs.gson) + + // Security preferences (Noise identity persistence) + implementation(libs.androidx.security.crypto) + + // Testing + testImplementation(libs.bundles.testing) + debugImplementation(libs.androidx.compose.ui.tooling) +} diff --git a/wear/src/main/AndroidManifest.xml b/wear/src/main/AndroidManifest.xml new file mode 100644 index 00000000..231bc058 --- /dev/null +++ b/wear/src/main/AndroidManifest.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wear/src/main/java/com/bitchat/watch/MainActivity.kt b/wear/src/main/java/com/bitchat/watch/MainActivity.kt new file mode 100644 index 00000000..25afe11a --- /dev/null +++ b/wear/src/main/java/com/bitchat/watch/MainActivity.kt @@ -0,0 +1,52 @@ +package com.bitchat.watch + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.wear.compose.material3.MaterialTheme +import androidx.wear.compose.material3.Text +import com.bitchat.watch.ui.theme.BitchatWearTheme + +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + BitchatWearTheme { + PlaceholderScreen() + } + } + } +} + +@Composable +fun PlaceholderScreen() { + Column( + modifier = Modifier + .fillMaxSize() + .padding(24.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Text( + text = "bitchat", + style = MaterialTheme.typography.titleLarge, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.primary + ) + Text( + text = "mesh initializing", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding(top = 8.dp) + ) + } +} diff --git a/wear/src/main/java/com/bitchat/watch/ui/theme/BitchatPalette.kt b/wear/src/main/java/com/bitchat/watch/ui/theme/BitchatPalette.kt new file mode 100644 index 00000000..5ae88981 --- /dev/null +++ b/wear/src/main/java/com/bitchat/watch/ui/theme/BitchatPalette.kt @@ -0,0 +1,38 @@ +package com.bitchat.watch.ui.theme + +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.graphics.Color + +@Immutable +data class BitchatPalette( + val inputOutline: Color, + val inputOutlineFocused: Color, + val inputSurface: Color, + val inputSurfaceFocused: Color, + val inputButton: Color, + val textTertiary: Color, + val accentOrange: Color, + val accentPurple: Color, + val peerColors: PeerColorStyle, +) + +val DarkBitchatPalette = BitchatPalette( + inputOutline = Color(0xFF333635), + inputOutlineFocused = Color(0xFF5A605D), + inputSurface = Color(0xFF0B0B0B), + inputSurfaceFocused = Color(0xFF151515), + inputButton = Color(0xFF1E1E1E), + textTertiary = Color(0xFF6B776B), + accentOrange = Color(0xFFFF9F0A), + accentPurple = Color(0xFFBF5AF2), + peerColors = PeerColorStyle.Dark, +) + +val LocalBitchatPalette = staticCompositionLocalOf { DarkBitchatPalette } + +object BitchatMotion { + const val QUICK_MS = 120 + const val STANDARD_MS = 180 + const val EMPHASIZED_MS = 240 +} diff --git a/wear/src/main/java/com/bitchat/watch/ui/theme/PeerColors.kt b/wear/src/main/java/com/bitchat/watch/ui/theme/PeerColors.kt new file mode 100644 index 00000000..4d8c390b --- /dev/null +++ b/wear/src/main/java/com/bitchat/watch/ui/theme/PeerColors.kt @@ -0,0 +1,35 @@ +package com.bitchat.watch.ui.theme + +import androidx.compose.runtime.Immutable +import androidx.compose.ui.graphics.Color +import kotlin.math.abs + +@Immutable +data class PeerColorStyle( + val saturation: Float, + val value: Float, +) { + companion object { + val Dark = PeerColorStyle(saturation = 0.55f, value = 0.82f) + } +} + +fun colorForPeer(stableKey: String, palette: BitchatPalette): Color { + var hash = 5381UL + for (byte in stableKey.toByteArray()) { + hash = ((hash shl 5) + hash) + byte.toUByte().toULong() + } + + var hue = (hash % 360UL).toDouble() / 360.0 + val orange = 30.0 / 360.0 + if (abs(hue - orange) < 0.05) { + hue = (hue + 0.12) % 1.0 + } + + val style = palette.peerColors + return Color.hsv( + hue = (hue * 360).toFloat(), + saturation = style.saturation, + value = style.value + ) +} diff --git a/wear/src/main/java/com/bitchat/watch/ui/theme/Theme.kt b/wear/src/main/java/com/bitchat/watch/ui/theme/Theme.kt new file mode 100644 index 00000000..3f928aba --- /dev/null +++ b/wear/src/main/java/com/bitchat/watch/ui/theme/Theme.kt @@ -0,0 +1,42 @@ +package com.bitchat.watch.ui.theme + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.graphics.Color +import androidx.wear.compose.material3.ColorScheme +import androidx.wear.compose.material3.MaterialTheme + +val BitchatWearColorScheme = ColorScheme( + primary = Color(0xFF32D74B), + onPrimary = Color.Black, + primaryContainer = Color(0xFF163D1D), + onPrimaryContainer = Color(0xFFB8F5C1), + secondary = Color(0xFF0A84FF), + onSecondary = Color.Black, + secondaryContainer = Color(0xFF082E54), + onSecondaryContainer = Color(0xFFC2E0FF), + tertiary = Color(0xFFFF9F0A), + onTertiary = Color.Black, + background = Color(0xFF000000), + onBackground = Color(0xFFF5F5F5), + surfaceContainer = Color(0xFF0E150E), + surfaceContainerLow = Color(0xFF0B0B0B), + surfaceContainerHigh = Color(0xFF182118), + onSurface = Color(0xFFF5F5F5), + onSurfaceVariant = Color(0xFF9AA69A), + outline = Color(0xFF2A3A2A), + outlineVariant = Color(0xFF1C271C), + error = Color(0xFFFF453A), + onError = Color.Black, +) + +@Composable +fun BitchatWearTheme(content: @Composable () -> Unit) { + CompositionLocalProvider(LocalBitchatPalette provides DarkBitchatPalette) { + MaterialTheme( + colorScheme = BitchatWearColorScheme, + typography = BitchatWearTypography, + content = content + ) + } +} diff --git a/wear/src/main/java/com/bitchat/watch/ui/theme/Typography.kt b/wear/src/main/java/com/bitchat/watch/ui/theme/Typography.kt new file mode 100644 index 00000000..ff66567d --- /dev/null +++ b/wear/src/main/java/com/bitchat/watch/ui/theme/Typography.kt @@ -0,0 +1,43 @@ +package com.bitchat.watch.ui.theme + +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.Font +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp +import androidx.wear.compose.material3.Typography +import com.bitchat.watch.R + +val BitchatFontFamily = FontFamily( + Font(R.font.geist_mono_regular, FontWeight.Normal), + Font(R.font.geist_mono_medium, FontWeight.Medium), + Font(R.font.geist_mono_semibold, FontWeight.SemiBold), + Font(R.font.geist_mono_bold, FontWeight.Bold), +) + +val BitchatWearTypography = Typography( + defaultFontFamily = BitchatFontFamily, +) + +object ChatVisualTokens { + val MessageBodyStyle = TextStyle( + fontFamily = BitchatFontFamily, + fontWeight = FontWeight.Normal, + fontSize = 13.sp, + lineHeight = 17.sp, + ) + + val SenderStyle = TextStyle( + fontFamily = BitchatFontFamily, + fontWeight = FontWeight.SemiBold, + fontSize = 13.sp, + lineHeight = 15.sp, + ) + + val SystemActionStyle = TextStyle( + fontFamily = BitchatFontFamily, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 14.sp, + ) +} diff --git a/wear/src/main/res/drawable/ic_launcher.xml b/wear/src/main/res/drawable/ic_launcher.xml new file mode 100644 index 00000000..f1b68e08 --- /dev/null +++ b/wear/src/main/res/drawable/ic_launcher.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/wear/src/main/res/font/geist_mono_bold.ttf b/wear/src/main/res/font/geist_mono_bold.ttf new file mode 100644 index 00000000..90eb8a86 Binary files /dev/null and b/wear/src/main/res/font/geist_mono_bold.ttf differ diff --git a/wear/src/main/res/font/geist_mono_medium.ttf b/wear/src/main/res/font/geist_mono_medium.ttf new file mode 100644 index 00000000..ff49ece5 Binary files /dev/null and b/wear/src/main/res/font/geist_mono_medium.ttf differ diff --git a/wear/src/main/res/font/geist_mono_regular.ttf b/wear/src/main/res/font/geist_mono_regular.ttf new file mode 100644 index 00000000..50c9d5a6 Binary files /dev/null and b/wear/src/main/res/font/geist_mono_regular.ttf differ diff --git a/wear/src/main/res/font/geist_mono_semibold.ttf b/wear/src/main/res/font/geist_mono_semibold.ttf new file mode 100644 index 00000000..1b21724d Binary files /dev/null and b/wear/src/main/res/font/geist_mono_semibold.ttf differ diff --git a/wear/src/main/res/values/strings.xml b/wear/src/main/res/values/strings.xml new file mode 100644 index 00000000..c89fc442 --- /dev/null +++ b/wear/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + + bitchat +